Building Slim OCI Images Using Python+Poetry
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 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 dependeny graph.
poetry is great software and brings to
Python development what more modern languages such as
Rust brought straight from the beginning.
OCI image from a
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"]
Poetryhas resolved all dependencies and the
OCI-image is made up of deterministic (ish) components.
- The image is slim, because we do not include anything but the
srcfolder and the dependencies. We do not require any build tools or
- We can build the image through
podman-compose, because we used a
Interesting alternatives to consider are
buildah and particularly
pkgs.dockerTools). I might write a post about these in the future.