Creating a Container for the Application

Installing a Container Manager

We can use either Docker or Podman for managing containers. Both of them use OCI compliant containers, and their CLIs are compatible with each other. We should begin by installing Docker or installing Podman by following the instructions on their website. We recommend using Podman instead of Docker due to its daemon-less design and ability to run rootless mode. You can read more in the article Transitioning from Docker to Podman.

Ignoring Files from the Container

The container image needs to ignore files that it does not need to function, such as local project files, automatically-generated files, or version control files from the container image. We can ignore files by creating a .dockerignore file, which works for Genie applications. Docker's documentation is a great reference for Dockerfiles.

## Julia Project
README.md
Manifest.toml
test
docs

## Genie
config/secrets.jl
data
log

## Git
.git
.gitignore
.gitattributes
*.gitkeep
.github

Creating the Container File

We also need to create a configuration for the container image. Below, we explain how to create a Dockerfile to define how container engine builds a container image. We will start by using a Julia container image as the base image.

FROM julia:1.6-buster

Then, we create genie user inside the container.

RUN useradd --create-home --shell /bin/bash genie

Next, we create app directory inside the /home/genie directory, copy the application files into it while ignoring files specified in .dockerignore, and change our working directory to it.

RUN mkdir /home/genie/app
COPY . /home/genie/app
WORKDIR /home/genie/app

Next, we give read, write and execution permissions and change ownership to the genie user with root group for the specified files. OpenShift requires permissions for the root group.

RUN chgrp root /home/genie
RUN chown genie:root -R *
RUN chmod -R g+rw /home/genie/app
RUN chmod g+rwX bin/server
RUN chmod -R g+rwX /usr/local/julia

Now, we change the user to genie with root group.

USER genie:root

We specify environment variables for the Genie application with the ENV directive.

ENV JULIA_DEPOT_PATH "/home/genie/.julia"
ENV GENIE_ENV "prod"
ENV HOST "0.0.0.0"
ENV PORT "8000"
ENV EARLYBIND "true"

Now, we can install the application as a Julia package inside the container.

RUN julia -e "using Pkg; Pkg.activate(\".\"); Pkg.instantiate(); Pkg.precompile(); "

We can remove the Julia registries afterward to reduce the container size.

RUN rm -rf /genie/.julia/registries

We also need to give the root group execution permissions for files inside the .julia directory.

RUN chmod -R -f g+rwX \
    /home/genie/.julia/packages \
    /home/genie/.julia/artifacts \
    /home/genie/.julia/compiled \
    /home/genie/.julia/logs

We will expose the container to networking using port 8000 via TCP.

EXPOSE 8000/tcp

Finally, we set the container to execute the bin/server script to start the webserver when we run the container.

CMD ["bin/server"]

Building and Running the Container

If you are using Docker, use the docker command as below. If you are using Podman, you should substitute docker with podman. Now, we can build a container image locally using the build command. The option -t defines the name and tag for the image in the format <name>:<tag>.

sudo docker build -t genie:latest .

After building the image, we can run it locally with the run command. The option -p publishes the container port 8000 to host post 8000 in format <container-port>:<host-port>.

sudo docker run -it -p 8000:8000 --rm genie:latest

The local web server should be running on http://localhost:8000/, and we can open it in the browser.