Poetry is a great build system.
And in 2023, I believe, no one should use the pip
for a private Python codebase.
Getting it right inside Docker is a different issue, however.
Consider a simple Flask-based web server as an example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| # Install poetry
$ pip3 install poetry==1.7.1
$ poetry --version
Poetry (version 1.7.1)
# Create a sample package
$ poetry init --python=~3.10 --name=src --description='Flask Hello world' --dependency=Flask@3.0.0 --author='Ashish' --license='Apache 2.0' --no-interaction
$ poetry install
$ touch README.md
$ mkdir src
# Create a file src/server.py in your favorite editor
$ cat src/server.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if __name__ == "__main__":
app.run()
|
Let’s finish the build process
Now, let’s add a simple Dockerfile titled Dockerfile1
1
2
3
4
5
6
7
8
9
10
11
| FROM python:3.10-slim as base
# Install Poetry
RUN pip3 install poetry==1.7.1
WORKDIR /app
COPY pyproject.toml poetry.lock /app
# Install dependencies
RUN poetry install
COPY src /app/src
ENTRYPOINT ["poetry", "run", "python", "src/server.py"]
|
And let’s build and check its size
1
2
| $ docker build -f Dockerfile1 -t example1 . && docker image inspect example1 --format='{{.Size}}' | numfmt --to=iec-i
235Mi
|
We don’t need poetry in the final build, so, we can save space via multi-stage docker builds.
Consider following the multi-stage docker file Dockerfile2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| FROM python:3.10-slim as builder
# Install Poetry
RUN pip3 install poetry==1.7.1
WORKDIR /app
COPY pyproject.toml poetry.lock /app
# virtual env is created in "/app/.venv" directory
ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_VIRTUALENVS_CREATE=true \
POETRY_CACHE_DIR=/tmp/poetry_cache
# Install dependencies
RUN --mount=type=cache,target=/tmp/poetry_cache poetry install --only main --no-root
RUN poetry install
FROM python:3.10-slim as runner
COPY src /app/src
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
ENTRYPOINT ["/app/.venv/bin/python", "src/server.py"]
|
And the result
1
2
| $ docker build -f Dockerfile2 -t example1 . && docker image inspect example1 --format='{{.Size}}' | numfmt --to=iec-i
158Mi
|
That’s an extra 77Mi (33%) of saving while reducing the attack surface of the docker image!