Skip to content

Add dockerfile for container with sysimage #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ docs/
Dockerfile
.gitignore
data
launch_sysimage.sh
2 changes: 1 addition & 1 deletion .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ WORKER_USERNAME=worker@email.com
WORKER_PASSWORD=password

# Debug
JULIA_DEBUG=ReefGuideWorker
JULIA_DEBUG=ReefGuideWorker,ReefGuide

# Configuration for the worker
POLL_INTERVAL_MS=5000
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/PublishDockerImage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
target: app-src # Specifies which stage of the Dockerfile to build
push: true # Pushes the image to the registry
tags: ${{ steps.meta.outputs.tags }} # Uses the tags generated in the metadata step
labels: ${{ steps.meta.outputs.labels }} # Uses the labels generated in the metadata step
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ sandbox/

data
.env
sysimages
112 changes: 53 additions & 59 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
#==============================================================================
# ReefGuide Worker Base Image
#==============================================================================
# This Dockerfile creates a Julia-based container image for the ReefGuide Worker
# application. It provides a complete Julia environment with the ReefGuideWorker
# package installed from source as a development dependency.
#
# Usage:
# Build> docker build -t reefguide-worker .
# Run> docker build -t reefguide-worker .
#==============================================================================

# See https://hub.docker.com/_/julia for valid versions.
ARG JULIA_VERSION="1.11.5"
ARG BASE_IMAGE="julia:${JULIA_VERSION}-bookworm"

#------------------------------------------------------------------------------
# internal-base build target: julia with OS updates and an empty @app
# Julia environment prepared for use. NOT intended for standalone use.
#------------------------------------------------------------------------------
FROM julia:${JULIA_VERSION}-bookworm AS internal-base
FROM ${BASE_IMAGE}

# Record the actual base image used from the FROM command as label in the compiled image
ARG BASE_IMAGE="julia:${JULIA_VERSION}-bookworm"
# Redeclare
ARG BASE_IMAGE

# Record the actual base image used from the FROM command as label in the
# compiled image
LABEL org.opencontainers.image.base.name=${BASE_IMAGE}

# Update all pre-installed OS packages (to get security updates)
# and add a few extra utilities
RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
--mount=target=/var/cache/apt,type=cache,sharing=locked \
apt-get update \

# Update all pre-installed OS packages (to get security updates) and add a few
# extra utilities
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get install --no-install-recommends -y \
git \
Expand All @@ -32,67 +43,50 @@ RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \
&& rm -rf /var/lib/apt/lists/*

# Tweak the JULIA_DEPOT_PATH setting so that our shared environments will end up
# in a user-agnostic location, not in ~/.julia => /root/.julia which is the default.
# See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_DEPOT_PATH
# This allows apps derived from this image to drop privileges and run as non-root
# user accounts, but still activate environments configured by this dockerfile.
ENV JULIA_DEPOT_PATH="/usr/local/share/julia"
ENV PRJ_PATH="/usr/local/share/julia/environments/app"
# in a user-agnostic location, not in ~/.julia => /root/.julia which is the
# default. See
# https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_DEPOT_PATH
# This allows apps derived from this image to drop privileges and run as
# non-root user accounts, but still activate environments configured by this
# dockerfile.
ENV JULIA_DEPOT_PATH=/usr/local/share/julia

# Ensure the @app environment is in the load path for Julia, so that apps
# derived from this image can access any packages installed to there. (See
# https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_LOAD_PATH)
ENV JULIA_LOAD_PATH="@:@app:@v#.#:@stdlib"

# This tells Julia's package manager to use the CLI installation of Git rather
# than an internal lib version - this works better with auth for example in
# CI/CD environments
ENV JULIA_PKG_USE_CLI_GIT=true

# Coerce Julia to build across multiple targets
# Generic targets taken from: cpu_targets taken from:
# Coerce Julia to build across multiple targets. See:
# https://docs.julialang.org/en/v1/devdocs/sysimg/#Specifying-multiple-system-image-targets
ENV JULIA_CPU_TARGET=generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)
# Alternate set that was found to initially alleviate excessive
# (re)precompilation issues on AWS at expense of very long build times...
# x86_64;haswell;skylake;skylake-avx512;tigerlake
ENV JULIA_CPU_TARGET="generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1);x86-64-v4,-rdrnd,base(1);znver4,-rdrnd,base(1)"

# Alternate set that was found to initially alleviate issues on AWS at expense of very
# long build times.
# ENV JULIA_CPU_TARGET=x86_64;haswell;skylake;skylake-avx512;tigerlake

# Prepare an empty @app Julia environment for derived images to use - this is created in the shared depot path
# Prepare an empty @app Julia environment for derived images to use - this is
# created in the shared depot path
RUN mkdir -p "${JULIA_DEPOT_PATH}" && \
chmod 0755 "${JULIA_DEPOT_PATH}" && \
julia -e 'using Pkg; Pkg.activate("app", shared=true)'

# Ensure the @app environment is in the load path for Julia, so that apps derived
# from this image can access any packages installed to there.
# (See https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_LOAD_PATH)
ENV JULIA_LOAD_PATH="@:@app:@v#.#:@stdlib"
chmod 0755 "${JULIA_DEPOT_PATH}"

# Copy project and manifest - includes Manifest-v1.11 etc
COPY Project.toml Manifest*.toml ./

# Install ReefGuideWorker from source and configure it as a development
# package in the @app shared environment.
# Should be v speedy if the .toml file is unchanged, because all the
# dependencies *should* already be installed.
COPY ./src src
RUN julia --project=@app \
-e 'using Pkg; \
Pkg.add("MKL"); \
RUN julia --project=@app -e \
'using Pkg; \
Pkg.develop(PackageSpec(path=pwd())); \
Pkg.precompile(); \
using ReefGuideWorker;'

# Run Julia commands by default as the container launches.
# Derived applications should override the command.
ENTRYPOINT ["julia", "--project=@app"]

#------------------------------------------------------------------------------
# app-src build target: installs directly from source files in this repo.
#------------------------------------------------------------------------------
FROM internal-base AS app-src

ENV APP_ENV_DIR="${JULIA_DEPOT_PATH}/environments/app" \
APP_SRC_DIR="/usr/local/src/app" \
JULIA_PKG_USE_CLI_GIT=true

# Expect to include the prepped data at /data/app and the config at
# /data/.config.toml
VOLUME ["/data/app"]
Pkg.instantiate(); \
Pkg.precompile();'

# By default, drops the user into a julia shell with ReefGuideWorker activated
ENTRYPOINT ["julia", "--project=@app", "-t", "auto,1", "-e"]
ENTRYPOINT ["julia", "--project=@app", "-e"]

# Derived applications should override the command e.g. to start
CMD ["using ReefGuideWorker; ReefGuideWorker.start_worker()"]
CMD ["using ReefGuideWorker; ReefGuideWorker.start_worker()"]
84 changes: 84 additions & 0 deletions Dockerfile.build_sysimage
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#==============================================================================
# ReefGuide Worker: Sysimage Builder
#==============================================================================
# Builds a Julia system image for faster ReefGuide Worker startup times.
# The system image is compiled using PackageCompiler.jl and can be extracted
# from the resulting container.
#
# Following advice found in this Discourse thread:
# https://discourse.julialang.org/t/creating-a-docker-base-image-for-faster-deployments/121165/2
#
# Also found this relevant issue:
# https://github.com/JuliaLang/PackageCompiler.jl/issues/743
#
# Build:
# docker build --target export-sysimage -f Dockerfile.build_sysimage -t reefguide-sysimage .
#
# Extract sysimage:
# docker create --name temp-sysimage reefguide-sysimage
# docker cp temp-sysimage:/reefguide_img.so ./reefguide_img.so
# docker rm temp-sysimage
#==============================================================================

ARG JULIA_VERSION="1.11.5"
FROM julia:${JULIA_VERSION}-bookworm AS internal-base

# Since 1.9.0 Julia, the CPU target is set to "native" by default. This settings
# avoids the need to compile the Julia packages for the specific CPU
# architecture of the host machine Make sure the image can be used on any x86_64
# machine by setting JULIA_CPU_TARGET to the same value used by the generic
# julia binaries, see
# https://github.com/JuliaCI/julia-buildkite/blob/4b6932992f7985af71fc3f73af77abf4d25bd146/utilities/build_envs.sh#L23-L31
ENV JULIA_CPU_TARGET="generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1);x86-64-v4,-rdrnd,base(1);znver4,-rdrnd,base(1)"

ENV JULIA_VERSION=1.11.5
ENV JULIA_DIR=/usr/local/julia
ENV JULIA_PATH=${JULIA_DIR}
ENV JULIA_DEPOT_PATH=/usr/local/share/julia
ENV APP_ENV_PATH=${JULIA_DEPOT_PATH}/environments/app
ENV APP_SRC_DIR=/usr/local/src/app
ENV JULIA_PKG_USE_CLI_GIT=true

# Update all pre-installed OS packages (to get security updates)
# and add a few extra utilities
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get install --no-install-recommends -y \
git \
openssl \
libssl-dev \
g++ \
curl \
ca-certificates \
gdal-bin \
libgdal-dev \
libfftw3-dev \
&& apt-get clean \
&& apt-get autoremove --purge \
&& rm -rf /var/lib/apt/lists/*

# Setup shared environment and add packages
RUN mkdir -p "${JULIA_DEPOT_PATH}" \
&& chmod 0755 "${JULIA_DEPOT_PATH}"

WORKDIR "${APP_SRC_DIR}"

COPY Project.toml Manifest*.toml ./
COPY src/ src/

# Build sysimage
RUN julia -t auto --project=@app -e \
'using Pkg; \
Pkg.add("PackageCompiler"); \
Pkg.develop(PackageSpec(path=pwd())); \
Pkg.instantiate(); '

# Reduce number of tasks/threads to avoid heavy memory use during sysimage compilation
# https://github.com/JuliaLang/PackageCompiler.jl/issues/1031#issuecomment-2823054267
RUN julia --project=@app -t auto -e 'include("src/sysimage.jl")'

# Export Julia sysimage to host filesystem
# From project root
# docker build --target export-sysimage -t reefguide-sysimage -f sandbox/smaller_sysimage/Dockerfile .
FROM scratch AS export-sysimage
COPY --from=internal-base /usr/local/src/app/reefguide_img.so /reefguide_img.so
Loading