on
Building Slim OCI Images Using Python+Poetry
Motivation
poetry
is a great library for Python
packaging and dependency management. In particular, it protrudes with its exhaustive dependency resolution algorithm. Suppose that a component A
depends on components B
and C
, where B
also depends on C
. So far so good! Now add 6 version release specifiers (~=
, ==
, !=
, <=, >=
, <, >
, ===
): These version specifiers place constraints on the version of dependencies needed in order to build or run the desired software and can get pretty complex (e.g. ~= 0.9, >= 1.0, != 1.3.4.*, < 2.0
). Without going into detail of the intricacies of the version specifiers, note that poetry
takes great care to resolve all dependencies, which are usually versioned using Semantic Versioning. Furthermore, poetry
locks all dependencies in a poetry.lock
file by referencing their respective hashes, to guarantee a reproducible dependency graph.
Dependency graph
poetry
is great software and brings to Python
development what more modern languages such as Go
or Rust
brought straight from the beginning.
Building an OCI
image from a Dockerfile
The Open Container Initiative standardizes several specifications around containers. Amongst them is the Image Specification, which outlines how to package a filesystem bundle, that can be executed by container runtimes (e.g. containerd
). Or as people say colloquially: OCI
images are Docker
container images.
Our goal is to build such a container with a Python
application and its dependencies, as fetched by poetry
. The approach is simple: Using a multi-stage build, we create a /app
directory with our src
directory and a .venv
directory, which includes all dependencies. We then copy the .venv
directory to the prod
OCI image, where poetry
does not reside. This is shown below (note the use of poetry config virtualenvs.in-project true
to force the creation of a .venv
folder in the current directory).
FROM python:3.11.1-alpine3.17 AS builder
RUN apk add --no-cache build-base libffi-dev openssl-dev curl
RUN curl -sSL https://install.python-poetry.org | python3 -
WORKDIR /app
COPY poetry.lock pyproject.toml README.md .
COPY src ./src
RUN /root/.local/bin/poetry config virtualenvs.in-project true
RUN /root/.local/bin/poetry install
FROM python:3.11.1-alpine3.17
RUN mkdir -p /app/src
COPY --from=builder /app/.venv/ /app/.venv
COPY src /app/src
EXPOSE 8000
WORKDIR /app/src
CMD ["../.venv/bin/python", "app.py"]
Result
Poetry
has resolved all dependencies and theOCI
-image is made up of deterministic (ish) components.- The image is slim, because we do not include anything but the
src
folder and the dependencies. We do not require any build tools orPoetry
itself. - We can build the image through
docker compose
orpodman-compose
, because we used aDockerfile
Interesting alternatives to consider are buildah
and particularly nixpkgs
with pkgs.dockerTools
. I might write a post about these in the future.