Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using official Python base images and packaging into distroless later on #1543

Open
artursmet opened this issue Mar 12, 2024 · 5 comments
Open

Comments

@artursmet
Copy link

Hello,

I’ve been using official Docker images for python (eg. python:3.11-slim-bookworm) in my projects and I wanted to use multi stage build approach to package the final image into distroless one.

I’ve found an instruction of how to do it in this repo - https://github.com/GoogleContainerTools/distroless/tree/main/examples/python3-requirements but I wonder if it would be possible to use an official python docker image instead of installing an apt-based python distro from debian repos.

My attempt so far was like this:

FROM python:3.11-slim-bookworm AS build-venv

WORKDIR /opt/app/
ADD requirements.txt /opt/app/
RUN python3 -m venv /venv
RUN /venv/bin/pip install -r /opt/app/requirements.txt

FROM gcr.io/distroless/python3-debian12
COPY --from=build-venv /venv /venv
COPY . /app
WORKDIR /app
ENTRYPOINT ["/venv/bin/uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9090"]
EXPOSE 9090

However, the virtualenv produced by official python image does not work, because python in that image is compiled, not sourced from official Debian repositories (it has an additional benefit of being able to deliver most recent python version, without waiting for Debian maintainers).

So the python binary is located in /usr/local/bin/python3.11 in this case. However, on the distoless image, the python binary is located in /usr/bin/python3.11.
As an effect, the container does not boot because the binary path inside the virtualenv is incorrect.
This is because the virtualenv configuration:

/app # cat /venv/pyvenv.cfg 
home = /usr/local/bin
include-system-site-packages = false
version = 3.11.8
executable = /usr/local/bin/python3.11
command = /usr/local/bin/python3 -m venv /venv
/app # 

I’ve tried to work around it and add a symlink into the final image, it worked fine in the debug image in interactive shell:

/app # /venv/bin/python --version
sh: /venv/bin/python: not found
/app # mkdir -p /usr/local/bin && ln -s /usr/bin/python3 /usr/local/bin/python3 && ln -s /usr/bin/python3 /usr/local/bin/python3.11
/app # /venv/bin/python --version
Python 3.11.2
/app # 

But after adding it into the Dockerfile:

RUN mkdir -p /usr/local/bin && ln -s /usr/bin/python3 /usr/local/bin/python3 && ln -s /usr/bin/python3 /usr/local/bin/python3.11

It produces the following build error:

failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown

Is there a way to use the official python base image and later on carry over the virtualenv from this image into a distroless base? In my opinion it will bring a lot of benefits and allow to use most recent python versions in distroless (even in my case - official python image has 3.11.8 while the debian-repo-based distroless has 3.11.2 - both use Debian 12 bookworm)

@ding-ma
Copy link

ding-ma commented Mar 15, 2024

I made it worked this way

  1. Install pip/peotry into a distroless container (build container). Make sure to run in exec/vector mode as there is no shell.
  2. Build your project
  3. Copy the python sys dist-package (/usr/local/lib/python3.11/dist-packages) into a new distroless container (deploy)

That way you have one a container that only has your app with its dependencies - no build tools.
Hope this helps

@joonvena
Copy link

joonvena commented Jun 10, 2024

I made it worked this way

1. Install pip/peotry into a distroless container (build container). Make sure to run in exec/vector mode as there is no shell.

2. Build your project

3. Copy the python sys dist-package (/usr/local/lib/python3.11/dist-packages) into a new distroless container (deploy)

That way you have one a container that only has your app with its dependencies - no build tools. Hope this helps

@ding-ma Could you provide simple example Dockerfile for this implementation?

@cstruck
Copy link

cstruck commented Jul 2, 2024

ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim AS requirements-stage

ARG POETRY_VERSION=1.7.1

WORKDIR /tmp
RUN pip install --no-cache-dir "poetry==${POETRY_VERSION}"
COPY ./pyproject.toml ./poetry.lock* /tmp/

RUN poetry export -f requirements.txt --output requirements.txt --without-hashes \
  && pip install --no-cache-dir --upgrade -r requirements.txt
  && chmod 0755 /usr/local/bin/uvicorn 
  
FROM gcr.io/distroless/python3-debian12:nonroot
COPY --from=requirements-stage /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages
COPY --from=requirements-stage /usr/local/bin/uvicorn /app/uvicorn

COPY . /app
WORKDIR /app
CMD ["/app/uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9090"]
ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages
...

@ding-ma
Copy link

ding-ma commented Jul 8, 2024

ARG PYTHON_VERSION=3.11

FROM golang as builder
- download pip

FROM gcr.io/distroless/python3-debian12:latest as py-build
- user root:root  (need root to build)
- copy over pip
- RUN ["python", "/tmp/get-pip.py"]
- RUN ["pip", "install", "--no-cache-dir","poetry"]
- RUN ["poetry", "config", "virtualenvs.create", "false"]
- RUN ["poetry", "install", "--no-cache"]  --- poetry install rest of your deps
- NOTE: you can't chain them since there is no shell. You have to run them in the vector form show above


FROM gcr.io/distroless/python3-debian12:nonroot

# poetry installs under dist-packages
COPY --from=py-build /usr/local/lib/python${PYTHON_VERSION}/dist-packages /usr/local/lib/python${PYTHON_VERSION}/dist-packages
COPY --from=py-build /usr/local/bin/uvicorn /app/uvicorn

COPY . /app
WORKDIR /app
CMD ["/app/uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9090"]
ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages

Hope that helps :) let me know if you have any other questions

@AbelHristodor
Copy link

For anyone looking for a solution without poetry, I adapted @cstruck's implementation and got it working with:

ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim AS builder
WORKDIR /tmp

RUN apt-get update && pip install --upgrade pip

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt


FROM gcr.io/distroless/python3-debian12:nonroot
ARG PYTHON_VERSION=3.11

COPY --from=builder /usr/local/lib/python${PYTHON_VERSION}/site-packages /usr/local/lib/python${PYTHON_VERSION}/site-packages

COPY . /app
WORKDIR /app

ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages

CMD ["main.py"]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants