-
-
Save danpaldev/0adcd5028f300ce6fc960f7f514dbf10 to your computer and use it in GitHub Desktop.
Multi-Stage Docker Build for a Haskell (Stack) Project
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The below is a combination of the following two things: | |
# 1) This article + corresponding Gist: https://medium.com/permutive/optimized-docker-builds-for-haskell-76a9808eb10b | |
# 2) This Reddit comment in response to the above: | |
# - https://www.reddit.com/r/haskell/comments/cl5uod/optimized_docker_builds_for_haskell/evuzccm/ | |
# It is a multi-stage Docker build with 3 stages: 1) dependencies, 2) build, and 3) deploy | |
# ------------------------------------------------------------------------------------------- | |
# STAGE 1: Dependencies | |
# ------------------------------------------------------------------------------------------- | |
FROM haskell:8.10.2 as dependencies | |
RUN mkdir /opt/build | |
WORKDIR /opt/build | |
# GHC dynamically links its compilation targets to lib gmp | |
RUN apt-get update -y \ | |
&& apt-get download -y libgmp10 | |
RUN mv libgmp*.deb libgmp.deb | |
# First build long-term stable dependencies using YAML/etc. files you do not expect to change often | |
# Keep those locally in a /long-term/ sub-directory to your project's root path | |
COPY ./long-term/stack.yaml ./long-term/package.yaml ./long-term/stack.yaml.lock ./ | |
RUN stack build --dependencies-only | |
# If the current .yaml files in your project root have not changed from the long-term versions, | |
# Docker should use the cached version from the above layer. If they HAVE changed, Docker will | |
# rebuild the dependencies. If those changes are permanent, move the YAMLs into new /long-term/ | |
COPY ./stack.yaml ./package.yaml ./stack.yaml.lock ./ | |
RUN stack build --dependencies-only | |
# In the 'haskell' image, stack is configured with | |
# - system-ghc: true | |
# - install-ghc: false | |
# If an error is thrown by stack saying that it cannot find the correct GHC, then: | |
# - You might have tried a `stack build`/etc. command in a folder lacking | |
# a stack.yaml that correspnds to the version indicated in FROM above | |
# - Stack then defaults to creating a ~/.stack/global-config/stack.yaml | |
# from the most recent LTS resolver | |
# - And will expect to find whatever compiler version that LTS points to | |
# - This will happen if you built in a different WORKDIR than where you copied the YAML files | |
# ------------------------------------------------------------------------------------------- | |
# STAGE 2: Build | |
# ------------------------------------------------------------------------------------------- | |
FROM haskell:8.10.2 as build | |
# Copy stack's cache of your project's compiled dependencies | |
# from the previous build stage to the container | |
COPY --from=dependencies /root/.stack /root/.stack | |
# Change the container working directory to where you want to copy your project files | |
WORKDIR /opt/build | |
# Copy the entire contents of your local project folder | |
# to this new container (...and not just the YAMLs) | |
COPY . . | |
# Build your project | |
RUN stack build | |
# Move your compiled Haskell binary to a path easily accessible from the next stage | |
RUN mv "$(stack path --local-install-root --system-ghc)/bin" /opt/build/bin | |
# ------------------------------------------------------------------------------------------- | |
# STAGE 3: Deploy | |
# ------------------------------------------------------------------------------------------- | |
# Copy the compiled Haskell binary to a more lightweight (and less diskspace-hungry) image | |
# for uploading to a repository or using in deployment. 'ubuntu' is not the base image for | |
# the 'haskell' image, so this should probably be changed to 'debian'/etc. | |
FROM ubuntu:20.04 as deploy | |
RUN mkdir -p /opt/app | |
WORKDIR /opt/app | |
# Install libgmp using the .deb from the dependencies stage | |
# To-Do: Find a way to ensure the same version of libgmp is used as in the previous | |
# two build stages. Have experience a downgrade at this step before. Changing | |
# from Ubuntu to Debian for this final stage would probably help here. | |
COPY --from=dependencies /opt/build/libgmp.deb /tmp | |
RUN dpkg -i /tmp/libgmp.deb && rm /tmp/libgmp.deb | |
# Copy your compiled program from the build stage | |
COPY --from=build /opt/build/bin . | |
# Execute your app | |
CMD ["/opt/app/my-dummy-app", "--verbose"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment