DEV Community

Clayton Walker
Clayton Walker

Posted on • Edited on

Python and distroless: A simplified guide

While this guide's focus is on python, the general advice here applies to anyone trying to build docker images on top of distroless base images.

No dependencies

If you have a simple python script with no dependencies, you can follow this guide, whose Dockerfile is reproduced below.

FROM python:3-slim AS build-env
COPY . /app
WORKDIR /app

FROM gcr.io/distroless/python3
COPY --from=build-env /app /app
WORKDIR /app
CMD ["hello.py", "/etc"]
Enter fullscreen mode Exit fullscreen mode

Pure-python dependencies

If you have a requirements.txt but no additional native dependencies you can follow this guide

# Build a virtualenv using the appropriate Debian release
# * Install python3-venv for the built-in Python3 venv module (not installed by default)
# * Install gcc libpython3-dev to compile C Python modules
# * In the virtualenv: Update pip setuputils and wheel to support building new packages
FROM debian:11-slim AS build
RUN apt-get update && \
    apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \
    python3 -m venv /venv && \
    /venv/bin/pip install --upgrade pip setuptools wheel

# Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes
FROM build AS build-venv
COPY requirements.txt /requirements.txt
RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt

# Copy the virtualenv into a distroless image
FROM gcr.io/distroless/python3-debian11
COPY --from=build-venv /venv /venv
COPY . /app
WORKDIR /app
ENTRYPOINT ["/venv/bin/python3", "psutil_example.py"]
Enter fullscreen mode Exit fullscreen mode

Native dependencies

Finally if we have native dependencies, we can download them from a debian-based build stage, extract their contents to a temp directory (i.e. /dpkg), then copy those into the root distroless image during the final stage.

One way to generate a list of dependencies needed is to call apt-cache depends <your-lib-here>.
In our case we want to install the psycopg2 library, that has a native dependency on libpq. To find the package (and its dependencies) in debian, we'll first need the name of the package.

$ apt-get update
<snip>
Reading package lists... Done
$ apt-cache search libpq
<snip>
libpq5 - PostgreSQL C client library
Enter fullscreen mode Exit fullscreen mode

Now that we have the name of the package (libpq5) we can see what dependencies it has.

$ apt-cache depends --recurse libpq5
libpq5
  Depends: libc6
  Depends: libgssapi-krb5-2
  Depends: libldap-2.4-2
  Depends: libssl1.1
Enter fullscreen mode Exit fullscreen mode

We can see the pre-installed distroless dependencies listed in the python build file, the cc build file and the base build file. Looks like we'll need to add these dependencies.
NOTE: Dependencies might have dependencies, you can use the script here (written by @tuananh) to get all dependencies.

Final Dockerfile

# Build a virtualenv using the appropriate Debian release
# * Install python3-venv for the built-in Python3 venv module (not installed by default)
# * Install gcc libpython3-dev to compile C Python modules
# * In the virtualenv: Update pip setuputils and wheel to support building new packages
FROM debian:11-slim AS build
RUN apt-get update && \
    apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \
    python3 -m venv /venv && \
    /venv/bin/pip install --upgrade pip setuptools wheel
RUN cd /tmp && \
    apt-get update && \
    apt-get install -y --no-install-recommends \
        # install only deps
        curl \
        ca-certificates \
        openssl \
        && \
    apt-get download \
        # ca-certificates \
        \
        # additional python dependencies
        libpq5 \
        && \
    mkdir -p /dpkg/var/lib/dpkg/status.d/ && \
    for deb in *.deb; do \
        package_name=$(dpkg-deb -I ${deb} | awk '/^ Package: .*$/ {print $2}'); \ 
        echo "Process: ${package_name}"; \
        dpkg --ctrl-tarfile $deb | tar -Oxf - ./control > /dpkg/var/lib/dpkg/status.d/${package_name}; \
        dpkg --extract $deb /dpkg || exit 10; \
    done

# Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes
FROM build AS build-venv
COPY requirements.txt /requirements.txt
RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt

# Copy the virtualenv into a distroless image
FROM gcr.io/distroless/python3-debian11
COPY --from=build-venv /venv /venv
COPY --from=build/dpkg /
COPY . /app
WORKDIR /app
ENTRYPOINT ["/venv/bin/python3", "psutil_example.py"]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)