diff --git a/.github/workflows/docker-conf-ubuntu-publish.yml b/.github/workflows/docker-conf-ubuntu-publish.yml new file mode 100644 index 000000000..3759ee55c --- /dev/null +++ b/.github/workflows/docker-conf-ubuntu-publish.yml @@ -0,0 +1,48 @@ +name: Docker JULEA Configuration + +on: + push: + branches: + - master + + tags: + - v* + +env: + IMAGE_NAME: julea-ubuntu-config + +jobs: + push-conf: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Build image + run: docker build . --file container/docker/github/Dockerfile_JULEA_Config_Ubuntu --tag $IMAGE_NAME + + - name: Log into registry + run: echo "${{ secrets.GIT_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push image + run: | + IMAGE_ID=ghcr.io/${{ github.actor }}/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION diff --git a/.github/workflows/docker-deps-centos-publish.yml b/.github/workflows/docker-deps-centos-publish.yml new file mode 100644 index 000000000..0d090f986 --- /dev/null +++ b/.github/workflows/docker-deps-centos-publish.yml @@ -0,0 +1,43 @@ +name: Docker Build JULEA Centos Dependencies + +on: + release: + types: [published, edited] + +env: + IMAGE_NAME: julea-centos-deps-standard + +jobs: + push-deps: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Build image + run: docker build . --file container/docker/github/Dockerfile_CentOS_Deps_Standard --tag $IMAGE_NAME + + - name: Log into registry + run: echo "${{ secrets.GIT_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push image + run: | + IMAGE_ID=ghcr.io/${{ github.actor }}/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION diff --git a/.github/workflows/docker-deps-ubuntu-publish.yml b/.github/workflows/docker-deps-ubuntu-publish.yml new file mode 100644 index 000000000..ed5747302 --- /dev/null +++ b/.github/workflows/docker-deps-ubuntu-publish.yml @@ -0,0 +1,43 @@ +name: Docker Build JULEA Ubuntu Dependencies + +on: + release: + types: [published, edited] + +env: + IMAGE_NAME: julea-ubuntu-deps-standard + +jobs: + push-deps: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Build image + run: docker build . --file container/docker/github/Dockerfile_Ubuntu_Deps_Standard --tag $IMAGE_NAME + + - name: Log into registry + run: echo "${{ secrets.GIT_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push image + run: | + IMAGE_ID=ghcr.io/${{ github.actor }}/$IMAGE_NAME + + # Change all uppercase to lowercase + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') + + # Strip git ref prefix from version + VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') + + # Use Docker `latest` tag convention + [ "$VERSION" == "master" ] && VERSION=latest + + echo IMAGE_ID=$IMAGE_ID + echo VERSION=$VERSION + + docker tag $IMAGE_NAME $IMAGE_ID:$VERSION + docker push $IMAGE_ID:$VERSION diff --git a/Singularity b/Singularity new file mode 100644 index 000000000..0d11cc125 --- /dev/null +++ b/Singularity @@ -0,0 +1,19 @@ +Bootstrap: library +From: ubuntu:20.04 + +%files + . /julea + +%post + apt-get update && apt-get install -y \ + build-essential \ + curl \ + unzip \ + git \ + python3 + + cd /julea + ./scripts/install-dependencies.sh + . scripts/environment.sh + meson setup --prefix="${HOME}/julea-install" -Db_sanitize=address,undefined bld + ninja -C bld diff --git a/container/docker/client/Dockerfile b/container/docker/client/Dockerfile new file mode 100644 index 000000000..a200a8ae8 --- /dev/null +++ b/container/docker/client/Dockerfile @@ -0,0 +1,6 @@ +FROM ghcr.io/julea-io/julea-ubuntu-config:latest + +COPY docker-entrypoint.sh /entrypoint.sh + +CMD ["juleaServer", "9876"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/container/docker/client/docker-entrypoint.sh b/container/docker/client/docker-entrypoint.sh new file mode 100755 index 000000000..93bdcdf0b --- /dev/null +++ b/container/docker/client/docker-entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +serverName=$1 +portNumber=$2 + +. /julea/scripts/environment.sh + +#Set environment script to .bashrc. So when exec is called with /bin/bash variables are set +sed -i -e '$a. /julea/scripts/environment.sh' ~/.bashrc + +julea-config --user \ + --object-servers="$serverName:$portNumber" --kv-servers="$serverName:$portNumber" --db-servers="$serverName:$portNumber" \ + --object-backend=posix --object-component=server --object-path="/tmp/julea-$(id -u)/posix" \ + --kv-backend=lmdb --kv-component=server --kv-path="/tmp/julea-$(id -u)/lmdb" \ + --db-backend=sqlite --db-component=server --db-path="/tmp/julea-$(id -u)/sqlite" + +tail -f /dev/null diff --git a/container/docker/github/Dockerfile_CentOS_Deps_Standard b/container/docker/github/Dockerfile_CentOS_Deps_Standard new file mode 100644 index 000000000..db37048af --- /dev/null +++ b/container/docker/github/Dockerfile_CentOS_Deps_Standard @@ -0,0 +1,10 @@ +FROM centos:latest + +COPY container/docker/github/install-centos-packages.sh . +RUN ./install-centos-packages.sh && rm install-centos-packages.sh + +WORKDIR /julea +COPY scripts/ ./scripts/ + +SHELL ["/bin/bash", "-c"] +RUN ./scripts/install-dependencies.sh diff --git a/container/docker/github/Dockerfile_JULEA_Config_CentOS b/container/docker/github/Dockerfile_JULEA_Config_CentOS new file mode 100644 index 000000000..5c0755b7d --- /dev/null +++ b/container/docker/github/Dockerfile_JULEA_Config_CentOS @@ -0,0 +1,12 @@ +FROM ghcr.io/julea-io/julea-centos-deps-standard:1.0.1 + +WORKDIR /julea +COPY . . + +RUN yum install -y libubsan \ + libasan + +SHELL ["/bin/bash", "-c"] +RUN . scripts/environment.sh && \ + meson setup --prefix="${HOME}/julea-install" bld && \ + ninja -C bld diff --git a/container/docker/github/Dockerfile_JULEA_Config_Ubuntu b/container/docker/github/Dockerfile_JULEA_Config_Ubuntu new file mode 100644 index 000000000..34dd39886 --- /dev/null +++ b/container/docker/github/Dockerfile_JULEA_Config_Ubuntu @@ -0,0 +1,9 @@ +FROM ghcr.io/julea-io/julea-ubuntu-deps-standard:1.0.0 + +WORKDIR /julea +COPY . . + +SHELL ["/bin/bash", "-c"] +RUN . scripts/environment.sh && \ + meson setup --prefix="${HOME}/julea-install" -Db_sanitize=address,undefined bld && \ + ninja -C bld diff --git a/container/docker/github/Dockerfile_Ubuntu_Deps_Standard b/container/docker/github/Dockerfile_Ubuntu_Deps_Standard new file mode 100644 index 000000000..784ed181c --- /dev/null +++ b/container/docker/github/Dockerfile_Ubuntu_Deps_Standard @@ -0,0 +1,10 @@ +FROM ubuntu:20.04 + +COPY container/docker/github/install-ubuntu-packages.sh . +RUN ./install-ubuntu-packages.sh && rm install-ubuntu-packages.sh + +WORKDIR /julea +COPY scripts/ ./scripts/ + +SHELL ["/bin/bash", "-c"] +RUN ./scripts/install-dependencies.sh diff --git a/container/docker/github/install-centos-packages.sh b/container/docker/github/install-centos-packages.sh new file mode 100755 index 000000000..91b530ac2 --- /dev/null +++ b/container/docker/github/install-centos-packages.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +yum update -y +yum -y install \ + gcc \ + gcc-c++ \ + make \ + curl \ + unzip \ + python3 \ + git \ + patch \ + bzip2 + +yum clean all +rm -rf /var/cache/yum diff --git a/container/docker/github/install-ubuntu-packages.sh b/container/docker/github/install-ubuntu-packages.sh new file mode 100755 index 000000000..5e7ce27b9 --- /dev/null +++ b/container/docker/github/install-ubuntu-packages.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +export DEBIAN_FRONTEND=noninteractive + +apt-get update +apt-get -y install --no-install-recommends \ + build-essential \ + curl \ + unzip \ + python3 + +apt-get -y install git + +rm -rf /var/lib/apt/lists/* diff --git a/container/docker/server/Dockerfile b/container/docker/server/Dockerfile new file mode 100644 index 000000000..563765c91 --- /dev/null +++ b/container/docker/server/Dockerfile @@ -0,0 +1,7 @@ +FROM ghcr.io/julea-io/julea-ubuntu-config:latest + +COPY docker-entrypoint.sh /entrypoint.sh +EXPOSE 9876/tcp + +CMD ["juleaServer", "9876"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/container/docker/server/docker-entrypoint.sh b/container/docker/server/docker-entrypoint.sh new file mode 100755 index 000000000..d54658085 --- /dev/null +++ b/container/docker/server/docker-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +serverName=$1 +portNumber=$2 + +. /julea/scripts/environment.sh + +sed -i -e '$a. /julea/scripts/environment.sh' ~/.bashrc + +julea-config --user \ + --object-servers="$serverName:$portNumber" --kv-servers="$serverName:$portNumber" --db-servers="$serverName:$portNumber" \ + --object-backend=posix --object-component=server --object-path="/tmp/julea-$(id -u)/posix" \ + --kv-backend=lmdb --kv-component=server --kv-path="/tmp/julea-$(id -u)/lmdb" \ + --db-backend=sqlite --db-component=server --db-path="/tmp/julea-$(id -u)/sqlite" + +julea-server --host "$serverName" --port $portNumber diff --git a/container/singularity/client/Singularity b/container/singularity/client/Singularity new file mode 100644 index 000000000..3e4dc565e --- /dev/null +++ b/container/singularity/client/Singularity @@ -0,0 +1,8 @@ +Bootstrap: docker +From: ghcr.io/julea-io/julea-ubuntu-config:latest + +%files + singularity-entrypoint.sh /singularity-entrypoint.sh + +%startscript + /bin/bash /singularity-entrypoint.sh diff --git a/container/singularity/client/singularity-entrypoint.sh b/container/singularity/client/singularity-entrypoint.sh new file mode 100755 index 000000000..d08ed276c --- /dev/null +++ b/container/singularity/client/singularity-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +. /julea/scripts/environment.sh + +julea-config --user \ + --object-servers="juleaserver:9876" --kv-servers="juleaserver:9876" --db-servers="juleaserver:9876" \ + --object-backend=posix --object-component=server --object-path="/singularity-mnt/julea-$(id -u)/posix" \ + --kv-backend=lmdb --kv-component=server --kv-path="/singularity-mnt/julea-$(id -u)/lmdb" \ + --db-backend=sqlite --db-component=server --db-path="/singularity-mnt/julea-$(id -u)/sqlite" + diff --git a/container/singularity/client/singularity-mnt/.gitkeep b/container/singularity/client/singularity-mnt/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/container/singularity/server/Singularity b/container/singularity/server/Singularity new file mode 100644 index 000000000..3e4dc565e --- /dev/null +++ b/container/singularity/server/Singularity @@ -0,0 +1,8 @@ +Bootstrap: docker +From: ghcr.io/julea-io/julea-ubuntu-config:latest + +%files + singularity-entrypoint.sh /singularity-entrypoint.sh + +%startscript + /bin/bash /singularity-entrypoint.sh diff --git a/container/singularity/server/singularity-entrypoint.sh b/container/singularity/server/singularity-entrypoint.sh new file mode 100755 index 000000000..171a71c01 --- /dev/null +++ b/container/singularity/server/singularity-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +. /julea/scripts/environment.sh + +julea-config --user \ + --object-servers="localhost:9876" --kv-servers="localhost:9876" --db-servers="localhost:9876" \ + --object-backend=posix --object-component=server --object-path="/singularity-mnt/julea-$(id -u)/posix" \ + --kv-backend=lmdb --kv-component=server --kv-path="/singularity-mnt/julea-$(id -u)/lmdb" \ + --db-backend=sqlite --db-component=server --db-path="/singularity-mnt/julea-$(id -u)/sqlite" + +julea-server --daemon --host "localhost" --port "9876" diff --git a/container/singularity/server/singularity-mnt/.gitkeep b/container/singularity/server/singularity-mnt/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/README.md b/doc/README.md index 086eb03c0..b2eb46f54 100644 --- a/doc/README.md +++ b/doc/README.md @@ -17,3 +17,4 @@ Its goal is to provide a solid foundation for storage research and teaching. * [Debugging](debugging.md) * [Implementing a Backend](implementing-backend.md) * [HDF5 Support](hdf5.md) +* [Container Support](container.md) diff --git a/doc/container.md b/doc/container.md new file mode 100644 index 000000000..1c14f8c1c --- /dev/null +++ b/doc/container.md @@ -0,0 +1,171 @@ +# Container + +## Docker + +Root privileges are required on the system. + +### Build Docker Image + +In the container/docker/github directory of the repository are two Dockerfiles that loads the standard dependencies and configures JULEA using Meson and Ninja. By default JULEA files are copied to **/julea**. + +The Dockerfiles can be modified to create a new image. For example the Ubuntu dependcy image: + +``` +sudo docker build -t julea-ubunut-deps-standard:1.0 -f container/docker/github/Dockerfile_Ubuntu_Deps_Standard . +``` + +### Use Docker Image + +In the directory **./container/docker** a JULEA-Server and a JULEA-Client and their interaction are shown as an example. + +#### Network Communication between containers +If docker-compose is not an option a network must be created for the communication between the different containers. +All containers that are supposed to interact with each other have set this network as a parameter. + +``` +sudo docker network create myNet +``` + +#### Server + +The Dockerfile of the server uses the JULEA image from the GitHub Container Registry. In the same folder a script is created to configure the server. +The script loads the environment variables, sets the configuration of JULEA and starts a server. +Also, it adds the execution of the environment script to **.bashrc** so that the variables are loaded when entering the container. +This need to be done otherwise the JULEA commands are not available within the container. + +In the Dockerfile the script is copied into the image. The default port for ther JULEA server is exposed. +At the end the *ENTRYPOINT* is defined which calls the script when *docker run* is executed. +The default values for the script are provided by the *CMD* command (hostname: **juleaServer**, port number: **9876**). + +``` +cd ./container/docker/server +sudo docker build -t julea-server-image . +sudo docker run -it -d --name julea-server julea-server-image +sudo docker exec -ti julea-server /bin/bash +``` + +If the default values should be overwritten add the requested values at the end of the statement. + +``` +sudo docker run -it -d --name julea-server julea-server-image SERVERNAME PORT +``` + +For network communication, the hostname and port number given in the script must be set. In addition the created network must be specified. +``` +sudo docker run -it -d --network-alias juleaServer -p 9876:9876/tcp --network myNet --name julea-server julea-server-image +``` + +#### Client +The client uses basically the same procedure. Here, the hostname and port number of the JULEA server are specified in the *CMD* command of the Dockerfile. If the default values are overwritten, this must happen in the client as well. +In the script a *tail* command is executed at the end, which ensures that the container is not shut down immediately after starting. +This would normally happen because no permanent process is running. + +``` +cd ./container/docker/client +sudo docker build -t julea-client-image . +sudo docker run -it -d --name julea-client julea-client-image +sudo docker exec -ti julea-client /bin/bash +``` + +If the default values should be overwritten: +``` +sudo docker run -it -d --name julea-client julea-client-image SERVERNAME PORT +``` + +To be able to communicate with the server, the network must also be specified. +``` +sudo docker run -it -d --network myNet --name julea-client julea-client-image +``` + +### Why to a script in ENTRYPOINT +The server's Dockerfile only contains the *ENTRYPOINT* that refers to a script. +Within the script the configuration for JULEA is done and a server is started. +The configuration is not set directly in the Dockerfile because every *RUN* command in the Dockerfile creates its own layer. +The environment variables, which are set by the environment script, are not persistent. +This means that another layer can't access them, which leads to the fact, +that with two *RUN* statements the command *julea-config* is no longer available and an error occurs. + +To do the whole configuration inside the Dockerfile becomes much too complicated and unclear. +Therefore it is recommended to outsource the configuration to an external script. + +### Volume +To be able to develop more dynamically, there is the possibility to mount local directories into the container. +So you could develop or use JULEA on the local host and then compile and execute the result in the container. + +``` +sudo docker run -it -d --network myNet -v /absolute/path/to/dir/build:/build --name julea-client julea-client-image +``` + +### Docker-Compose +To simplify the administration of containers there is a docker-compose example in the root directory of JULEA. +In this example a server and a client is created. The name of the service of juleaServer is the server name, +which must be set for the client, otherwise the server cannot be reached from the client. +For the server the ports are defined, which are visible to the hostsystem and other containers. +In addition, a name for the image and one for the container for both services is defined, so that no random ones are generated and remain unique. +For the client, a directory is mounted from the host into the container. + +Creating the network is no longer necessary, because docker-compose does this. For this example a network is created, +which is called *julea_default*. This can be examined with *docker network inspect julea_default*. + +To start both containers, just type: +``` +docker-compose up -d +``` + +## Singularity +No root privileges are required on the system. + +Logs can be found here: ~/.singularity/instances/logs/HOSTNAME/USERNAME/ + +## Singularity Image +All commands (exec, run and shell) use the image as read-only. +To use Singularity with JULEA, e.g. to compile files in the image, a directory with the files can be mounted to the container. + +In addition, by default some directories from the host system are mounted into the image. Read about this here: +https://sylabs.io/guides/3.7/user-guide/bind_paths_and_mounts.html?highlight=bind%20path + +The Singularity file uses the Docker images from the GitHub Container Registry. + +### Server +The Singularity server image copies a script into the image, which is executed when the instance is started. +This script sets the configuration for JULEA. The path for the backends is set here to the mounted directory. +Then the server is started with the hostname *juleaserver* and the port *9876*. +``` +cd ./container/singularity/server +singularity build --fakeroot julea-server.sif Singularity +singularity instance start --bind singularity-mnt/:/singularity-mnt julea-server.sif julea-server-instance +``` + +For the client, or other system, the IP address of the server is needed. This can be found out as follows. +The IP should be in subnet 10.X.X.X. +``` +singularity exec instance://julea-server-instance hostname -I +``` + +### Client +The Singularity client image proceeds the same way as the server image. +Again, a script is created that uses the hostname and port of the server. +In order to resolve the hostname of the server, the following step must be executed. +Alternatively you can also work with the IP address of the server, then this step is not necessary. + +Create an *etc.hosts* file in the directory of the client. +Add the IP address of the server and mount this file into the instance. +``` +cd ./container/singularity/client +singularity build --fakeroot julea-client.sif Singularity +singularity instance start --bind etc.hosts/:/etc/hosts --bind singularity-mnt/:/singularity-mnt julea-client.sif julea-client-instance +singularity shell instance://julea-client-instance +``` + +If you want to work with JULEA in the container, make sure that the environment variables are loaded. +``` +. /julea/scripts/environment.sh +``` + +## Podman +The Docker images and Dockerfiles are also supported by Podman. +Here you can use the same commands as for Docker. +Just replace *docker* with *podman* at the beginning of the instruction. + +### Podman-Compose +To use the *docker-compose.yml* with Podman, the following project can be used: https://github.com/containers/podman-compose diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..dc517cb3d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + juleaServer: + build: + context: ./container/docker/server + ports: + - "9876:9876" + image: julea-server-image + container_name: julea-server + juleaClient: + build: + context: ./container/docker/client + volumes: + - "./example:/build" + image: julea-client-image + container_name: julea-client