Skip to content

Commit

Permalink
feat: Derive JVM-memory options automatically from host/container
Browse files Browse the repository at this point in the history
  • Loading branch information
MoritzWeber0 committed Jun 21, 2024
1 parent 3f55ddf commit 86bdf44
Show file tree
Hide file tree
Showing 17 changed files with 194 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ prometheus*
volumes
*.egg-info/
.env

**/set_memory_flags.py
!eclipse/set_memory_flags.py
24 changes: 22 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ DOCKER_REGISTRY ?= localhost:12345
# Log level when running Docker containers
LOG_LEVEL ?= DEBUG

# Set memory options for the TeamForCapella server
MEMORY_MAX ?= 90%
MEMORY_MIN ?= 70%

# If this option is set to 1, all tests that require a running t4c server
# will be executed. To run these tests, you need a Makefile in
# t4c/server with a target t4c/server/server that builds the t4c server
Expand Down Expand Up @@ -181,6 +185,7 @@ jupyter-notebook: base
capella/base: SHELL=./capella_loop.sh
capella/base: base
envsubst < capella/.dockerignore.template > capella/.dockerignore
cp eclipse/set_memory_flags.py capella/setup/set_memory_flags.py
docker build $(DOCKER_BUILD_FLAGS) \
-t $(DOCKER_PREFIX)$@:$$DOCKER_TAG \
--build-arg BUILD_ARCHITECTURE=$(BUILD_ARCHITECTURE) \
Expand All @@ -192,16 +197,19 @@ capella/base: base
--build-arg INSTALL_OLD_GTK_VERSION=$(INSTALL_OLD_GTK_VERSION) \
capella
rm capella/.dockerignore
rm capella/setup/set_memory_flags.py
$(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) IMAGENAME=$@ .push

papyrus/base: DOCKER_TAG=$(PAPYRUS_VERSION)-$(CAPELLA_DOCKERIMAGES_REVISION)
papyrus/base: DOCKER_BUILD_FLAGS=--platform linux/amd64
papyrus/base: base
cp eclipse/set_memory_flags.py papyrus/set_memory_flags.py
docker build $(DOCKER_BUILD_FLAGS) \
-t $(DOCKER_PREFIX)$@:$(DOCKER_TAG) \
--build-arg BASE_IMAGE=$(DOCKER_PREFIX)$<:$(CAPELLA_DOCKERIMAGES_REVISION) \
--build-arg PAPYRUS_VERSION=$(PAPYRUS_VERSION) \
papyrus
rm papyrus/set_memory_flags.py
$(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) DOCKER_TAG=$(DOCKER_TAG) IMAGENAME=$@ .push

eclipse/base: DOCKER_TAG=$(ECLIPSE_VERSION)-$(CAPELLA_DOCKERIMAGES_REVISION)
Expand Down Expand Up @@ -299,6 +307,8 @@ run-capella/remote: capella/remote
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e AUTOSTART_CAPELLA=$(AUTOSTART_CAPELLA) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-p $(RDP_PORT):3389 \
-p $(WEB_PORT):10000 \
-p $(METRICS_PORT):9118 \
Expand All @@ -312,6 +322,8 @@ run-papyrus/remote: papyrus/remote
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e WORKSPACE_DIR=/workspace/papyrus \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-p $(RDP_PORT):3389 \
-p $(WEB_PORT):10000 \
-p $(METRICS_PORT):9118 \
Expand All @@ -324,6 +336,8 @@ run-eclipse/remote: eclipse/remote
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e WORKSPACE_DIR=/workspace/eclipse \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-p $(RDP_PORT):3389 \
-p $(WEB_PORT):10000 \
-p $(METRICS_PORT):9118 \
Expand All @@ -338,6 +352,8 @@ run-eclipse/remote/pure-variants: eclipse/remote/pure-variants
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e WORKSPACE_DIR=/workspace/eclipse_pv \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-v $$(pwd)/volumes/pure-variants:/inputs/pure-variants \
-v $$(pwd)/volumes/workspace:/workspace \
-v $$(pwd)/pure-variants/versions:/opt/versions \
Expand All @@ -358,6 +374,8 @@ run-t4c/client/remote-legacy: t4c/client/remote
-e T4C_USERNAME=$(T4C_USERNAME) \
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-p $(RDP_PORT):3389 \
-p $(WEB_PORT):10000 \
-p $(METRICS_PORT):9118 \
Expand All @@ -372,21 +390,23 @@ run-t4c/client/remote: t4c/client/remote
-e T4C_USERNAME=$(T4C_USERNAME) \
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
-e MEMORY_MIN=$(MEMORY_MIN) \
-e MEMORY_MAX=$(MEMORY_MAX) \
-p $(RDP_PORT):3389 \
-p $(WEB_PORT):10000 \
-p $(METRICS_PORT):9118 \
$(DOCKER_PREFIX)$<:$$(echo "$(DOCKER_TAG_SCHEMA)" | envsubst)

run-t4c/client/remote/pure-variants: t4c/client/remote/pure-variants
docker run $(DOCKER_RUN_FLAGS) \
-v $$(pwd)/volumes/pure-variants:/inputs/pure-variants \
-v $$(pwd)/volumes/workspace:/workspace \
-e T4C_LICENCE_SECRET=$(T4C_LICENCE_SECRET) \
-e T4C_JSON=$(T4C_JSON) \
-e RMT_PASSWORD=$(RMT_PASSWORD) \
-e T4C_USERNAME=$(T4C_USERNAME) \
-e PURE_VARIANTS_LICENSE_SERVER=$(PURE_VARIANTS_LICENSE_SERVER) \
-e PURE_VARIANTS_KNOWN_SERVERS=$(PURE_VARIANTS_KNOWN_SERVERS) \
-v $$(pwd)/volumes/pure-variants:/inputs/pure-variants \
-v $$(pwd)/volumes/workspace:/workspace \
-e AUTOSTART_CAPELLA=$(AUTOSTART_CAPELLA) \
-e CONNECTION_METHOD=$(CONNECTION_METHOD) \
-e XPRA_SUBPATH=$(XPRA_SUBPATH) \
Expand Down
8 changes: 7 additions & 1 deletion capella/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ RUN mkdir -p /opt/capella/configuration/.settings; \
chown techuser /opt /opt/capella/capella.ini

RUN echo '-Dosgi.configuration.area=file:/opt/capella/configuration' >> /opt/capella/capella.ini
COPY startup/* /opt/setup/
COPY setup/* /opt/setup/

ENV AUTOSTART_CAPELLA=1
ENV RESTART_CAPELLA=1
Expand All @@ -151,6 +151,12 @@ COPY ./autostart /home/techuser/.config/openbox/autostart
ENV ECLIPSE_INSTALLATION_PATH=/opt/capella
ENV ECLIPSE_EXECUTABLE=/opt/capella/capella

# Set memory options for the JVM (used by set_memory_flags.py)
ARG MEMORY_MAX=90%
ENV MEMORY_MAX=${MEMORY_MAX}
ARG MEMORY_MIN=70%
ENV MEMORY_MIN=${MEMORY_MIN}

COPY ./autostart /home/techuser/.config/openbox/autostart

COPY git_askpass.py /etc/git_askpass.py
Expand Down
9 changes: 9 additions & 0 deletions capella/setup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!--
~ SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

# Directory for startup scripts

This directory contains scripts that are executed during container startup. As
of now, we support scripts with file-ending `.sh` and `.py`.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions capella/startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
# SPDX-License-Identifier: Apache-2.0

export DISPLAY=":99"
python3 /opt/setup/set_memory_flags.py
(Xvfb ${DISPLAY} -screen 0 1920x1080x8 -nolisten tcp 0>/dev/null 2>&1 &)
/opt/capella/capella "$@"
2 changes: 2 additions & 0 deletions ci-templates/gitlab/image-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ capella/base:
- *docker
# prettier-ignore
- mv ../capella.tar.gz ./capella/versions/$CAPELLA_VERSION/$BUILD_ARCHITECTURE/capella.tar.gz
- cp ./eclipse/set_memory_flags.py ./capella/setup/set_memory_flags.py
- >
if [[ -n "$(find ../dropins -maxdepth 1 -type d)" ]]; then
mv ../dropins/* ./capella/versions/$CAPELLA_VERSION/dropins/
Expand Down Expand Up @@ -435,6 +436,7 @@ papyrus/base:
- *prepare-papyrus
- *docker
- mv ../papyrus.tar.gz ./papyrus/versions/$PAPYRUS_VERSION/papyrus.tar.gz
- cp ./eclipse/set_memory_flags.py ./papyrus/set_memory_flags.py
- |
docker build $DOCKER_BUILD_ARGS \
-t ${IMAGE}:${DOCKER_TAG} \
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/capella/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ Please replace the followings variables:
(default) and when `AUTOSTART_CAPELLA=1`, Capella will be re-started as soon
as it has been exited (after clean quits as well as crashs).

If you want to configure the JVM memory options, have a look at
[Eclipse memory options](../eclipse/memory-options.md).

### Example to export representations (diagrams) as SVG images

Replace `/path/to/model` and `<PROJECT_NAME>` to pass any local Capella model.
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/eclipse/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,6 @@ Please replace the followings variables:
- `RESTART_ECLIPSE` defines the restart behaviour of Eclipse. When set to 1
(default) and when `RESTART_ECLIPSE=1`, Eclipse will be re-started as soon as
it has been exited (after clean quits as well as crashs).

If you want to configure the JVM memory options, have a look at
[Eclipse memory options](./memory-options.md).
29 changes: 29 additions & 0 deletions docs/docs/eclipse/memory-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!--
~ SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

# Memory options for Eclipse

To specify fixed memory options for the JVM, you can pass the following
environment variables to the `docker run` commands:

- `MEMORY_MIN` (default `70%`), translated to `-Xms` for absolute values and
`-XX:InitialRAMPercentage` and `-XX:MinRAMPercentage` for percentage values.
Percentage values are calculated according to the total memory or the
requested memory by the container.
- `MEMORY_MAX` (default `90%`), translated to `-Xmx` for absolute values and
`-XX:MaxRAMPercentage` for percentage values. Percentage values are
calculated according to the total memory of the system or the total memory
available to the container.

If the value ends with a %, we assume that it's a percentage value.

- If used in a Kubernetes cluster, it determines the values from the Pod
requests/limits.
- If used on a host system, it determines the value from the host memory.

See also:

- https://stackoverflow.com/a/65327769
- https://www.merikan.com/2019/04/jvm-in-a-container/#java-10
3 changes: 3 additions & 0 deletions docs/docs/papyrus/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ To customise the Papyrus client you can
docker build -t papyrus/base papyrus --build-arg PAPYRUS_VERSION=$PAPYRUS_VERSION
```

If you want to configure the JVM memory options, have a look at
[Eclipse memory options](../eclipse/memory-options.md).

## Run the container

### Locally on X11 systems
Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ nav:
- Base: papyrus/base.md
- Eclipse:
- Base: eclipse/base.md
- Memory Options: eclipse/memory-options.md
- 'pure::variants': pure-variants.md
- Remote: remote.md
- Development:
Expand Down
13 changes: 9 additions & 4 deletions eclipse/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ WORKDIR /opt/
RUN tar -xf eclipse.tar.gz && \
rm -rf eclipse.tar.gz

ARG MEMORY_LIMIT=5500m

RUN mkdir -p /workspace; \
# Set workspace permissions
chown -R techuser /workspace && \
chmod +x eclipse/eclipse && \
chown -R techuser /opt/eclipse && \
sed -i "s/-Xmx[^ ]*/-Xmx$MEMORY_LIMIT/g" /opt/eclipse/eclipse.ini
chown -R techuser /opt/eclipse

COPY set_memory_flags.py /opt/setup/set_memory_flags.py

USER techuser

Expand All @@ -52,6 +51,12 @@ ENV RESTART_ECLIPSE=1
ENV ECLIPSE_INSTALLATION_PATH=/opt/eclipse
ENV ECLIPSE_EXECUTABLE=/opt/eclipse/eclipse

# Set memory options for the JVM (used by set_memory_flags.py)
ARG MEMORY_MAX=90%
ENV MEMORY_MAX=${MEMORY_MAX}
ARG MEMORY_MIN=70%
ENV MEMORY_MIN=${MEMORY_MIN}

ENV BASE_TYPE=eclipse

USER techuser
94 changes: 94 additions & 0 deletions eclipse/set_memory_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Set the memory flags in the *.ini file for Eclipse applications.
The script reads the following environment variable:
ECLIPSE_EXECUTABLE: The path to the eclipse executable.
MEMORY_MIN: The minimum memory that the JVM should use.
Check the set_memory_flags function for more information.
MEMORY_MAX: The maximum memory that the JVM should use.
Check the set_memory_flags function for more information.
The script is called during container startup.
"""

import os
import pathlib


def remove_all_memory_flags(file_content: list[str]) -> list[str]:
"""Remove all memory flags from *.ini files.
To make sure that we don't have any conflicting memory flags in the
.ini file, we remove all flags that start with -Xm.
This will explicitely remove the -Xms and -Xmx flags.
"""

return [
line for line in file_content if not line.lstrip().startswith("-Xm")
]


def set_memory_flags(
file_content: list[str], memory_min: str, memory_max: str
) -> None:
"""Set the memory flags in *.ini.
If the value of memory_max ends with a %, we assume that it's a percentage value.
- If used in a Kubernetes cluster, it determines the values from the Pod requests/limits.
- If used on a host system, it determines the value from the host memory.
See Also
--------
- https://stackoverflow.com/a/65327769
- https://www.merikan.com/2019/04/jvm-in-a-container/#java-10
"""

if memory_min.strip().endswith("%"):
append_flag_to_file(
file_content, "XX:InitialRAMPercentage=", memory_min.strip("%")
)
append_flag_to_file(
file_content, "XX:MinRAMPercentage=", memory_min.strip("%")
)
else:
append_flag_to_file(file_content, "Xms", memory_min)

if memory_max.strip().endswith("%"):
append_flag_to_file(
file_content, "XX:MaxRAMPercentage=", memory_max.strip("%")
)
else:
append_flag_to_file(file_content, "Xmx", memory_max)


def print_vm_memory_usage_during_start(file_content: list[str]) -> None:
"""Print the JVM memory options during start."""

file_content.append("-XshowSettings:vm")


def append_flag_to_file(
file_content: list[str], flag: str, value: str
) -> None:
"""Append a flag to the *.ini file."""

file_content.append(f"-{flag}{value}")


if __name__ == "__main__":
eclipse_executable = pathlib.Path(os.environ["ECLIPSE_EXECUTABLE"])
_memory_min = os.environ["MEMORY_MIN"].strip()
_memory_max = os.environ["MEMORY_MAX"].strip()

ini_path = eclipse_executable.with_suffix(".ini")

_file_content = ini_path.read_text("utf-8").splitlines()

_file_content = remove_all_memory_flags(_file_content)
set_memory_flags(_file_content, _memory_min, _memory_max)
print_vm_memory_usage_during_start(_file_content)

ini_path.write_text("\n".join(_file_content), encoding="utf-8")
11 changes: 8 additions & 3 deletions papyrus/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ WORKDIR /opt/
RUN tar -xf papyrus.tar.gz && \
rm -rf papyrus.tar.gz

ARG MEMORY_LIMIT=5500m

COPY set_memory_flags.py /opt/setup/set_memory_flags.py
RUN mkdir -p /workspace; \
# Set workspace permissions
chown -R techuser /workspace && \
chmod +x Papyrus/papyrus && \
sed -i "s/-Xmx[^ ]*/-Xmx$MEMORY_LIMIT/g" /opt/Papyrus/papyrus.ini
chown techuser /opt/Papyrus/papyrus.ini

# Install SysML
RUN /opt/Papyrus/papyrus \
Expand All @@ -43,6 +42,12 @@ ENV RESTART_PAPYRUS=1
ENV ECLIPSE_INSTALLATION_PATH=/opt/Papyrus
ENV ECLIPSE_EXECUTABLE=/opt/Papyrus/papyrus

# Set memory options for the JVM (used by set_memory_flags.py)
ARG MEMORY_MAX=90%
ENV MEMORY_MAX=${MEMORY_MAX}
ARG MEMORY_MIN=70%
ENV MEMORY_MIN=${MEMORY_MIN}

ENV BASE_TYPE=papyrus

USER techuser

0 comments on commit 86bdf44

Please sign in to comment.