In my experience, deploying Julia to the cloud can be frustrating because it can be slow to build the container used to your app. I don’t seem to be the only one. A major gripe is that AWS CodeBuild can’t do layer caching in a VPC, so strategies for rearranging your DockerFile to benefit from cache don’t help.
I finally got a working solution for me. I build a base image with a SysImage of my dependencies and then copy my current src
directory on deployment. Posting it here in case it is helpful for others. It comes in four parts
1. A Julia script for building a system image within the Docker container
using Pkg
# Install PackageCompiler in the base environment
Pkg.activate(@__DIR__)
Pkg.instantiate()
Pkg.add("PackageCompiler")
Pkg.build("PackageCompiler")
using PackageCompiler
# Activate the Project Environment
# Build the sysimage
PackageCompiler.create_sysimage(;
sysimage_path="/usr/local/julia/bin/julia_base.so",
cpu_target="generic",
sysimage_build_args=`-O3`
)
2. A DockerFile to build the base image
FROM public.ecr.aws/amazonlinux/amazonlinux:latest
ENV JULIA_CPU_TARGET=generic
# Download and install Julia
WORKDIR /usr/local
RUN yum -y groupinstall "Development Tools"
RUN yum install -y tar gzip
RUN curl -LO https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.5-linux-x86_64.tar.gz
RUN tar xf julia-1.10.5-linux-x86_64.tar.gz
RUN rm julia-1.10.5-linux-x86_64.tar.gz
RUN ln -s julia-1.10.5 julia
# Use a special depot path to store precompiled binaries
ENV JULIA_DEPOT_PATH=./.julia
COPY Project.toml .
COPY Manifest.toml .
COPY create_sys_image.jl .
# Only copying src right because package compiler expense a
# wellformed package for the active project.
ADD src src/.
RUN /usr/local/julia/bin/julia create_sys_image.jl -t auto -O3 --startup-file=no --heap-size-hint=6G
3. A bash script to orchestrate building the base image when you change your dependencies
Note that for this script to work, you need to have Docker Engine running and 8GB of RAM allocated to it. You also need to be logged into AWS.
I don’t use other image hosting services (i.e. DockerHub), but I’m sure this script could be amended pretty easily to work with whatever solution you use.
docker build -f Dockerfile.base --tag julia_base:latest . --shm-size 8gb
aws ecr get-login-password --region <YOUR REGION> --profile <YOUR AWS PROFILE NAME> | \
docker login --username AWS --password-stdin <YOUR ECR URL>
docker tag juila_base:latest <YOUR ECR URL>/<YOUR ECR REPO>:julia_base
docker push <YOUR ECR URL>/<YOUR ECR REPO>:julia_base
4. The DockerFile to use in deployment
I have heap size hint set to 12GB since my Fargate task is allocated 16GB of RAM. I notice that sometime Julia thinks that the RAM available is actually the host machine's total RAM, not what is allocated to the container. Set it accordingly for your use case.FROM <YOUR ECR URL>/<YOUR ECR REPO>:juila_base
ADD src src/.
COPY entry_point_script.jl .
RUN /usr/local/julia/bin/julia \
--sysimage /usr/local/julia/bin/juila_base.so \
--sysimage-native-code=yes \
-t auto \
--project=. \
-O3 \
--startup-file=no \
-e 'using Pkg;Pkg.instantiate()'
ENTRYPOINT /usr/local/julia/bin/julia \
--sysimage /usr/local/julia/bin/juila_base.so \
--sysimage-native-code=yes \
-t auto \
--project=. \
-O3 \
--startup-file=no \
--heap-size-hint=12G \
entry_point_script.jl
Note that all these files assume they are at the root of your project directory.
Thanks to @oxinabox and others for your help along the way!
UPDATE: I discovered that, even if you copy in a new src/
directory, if you import your project package with using MyPackage
it will pick up the version that PackageCompiler.jl cached in the sys image. So it’s best to have your entry_point_script.jl
use include("src/MyPackage.jl")
, so that you are using the fresh version of your package.