3

How do I install poetry in my image? (should I use pip?)

Which version of poetry should I use?

Do I need a virtual environment?

There are many examples and opinions in the wild which offer different solutions.

Soof Golan
  • 151
  • 1
  • 5

1 Answers1

3

TL;DR

Install poetry with pip, configure virtualenv, install dependencies, run your app.

FROM python:3.10

ENV POETRY_VERSION=1.1.13
RUN python3 -m pip install poetry==$POETRY_VERSION

WORKDIR /my_app

COPY poetry.lock pyproject.toml ./
RUN poetry config virtualenvs.in-project true --local
RUN poetry install --no-dev

COPY . .
CMD [ "poetry", "run", "python", "-c", "print('hello world')" ]

In Detail

Installing Poetry

How do I install poetry in my image? (should I use pip?)

Install it with the official installer.

# Set Poetry Version
ENV POETRY_VERSION=1.1.13
# Install Poetry
RUN curl -sSL https://install.python-poetry.org | python3 - --version $POETRY_VERSION
# Add poetry install location to PATH
ENV PATH=/root/.local/bin:$PATH

Install it with pip

Using pip to install poetry is somewhat discouraged.

Be aware that it will also install Poetry’s dependencies which might cause conflicts with other packages.

ENV POETRY_VERSION=1.1.13
RUN python3 -m pip install poetry==$POETRY_VERSION

Poetry Version

Which version of poetry should I use?

Specify the latest stable version explicitly in your installation.

Forgetting to specify POETRY_VERSION will result in undeterministic builds, as the installer will always install the latest version - which may introduce breaking changes

Virtual Environment (virtualenv)

Do I need a virtual environment?

Yes, and you need to configure it a bit.

RUN poetry config virtualenvs.in-project true --local

The reasons for this are somewhat off topic:

By default, poetry creates a virtual environment in $HOME/.cache/pypoetry/virtualenvs to isolate the system interpreter from your application. This is the desired behavior for most development scenarios. When using a container, the $HOME variable may be changed by certain runtimes, so creating the virutal environment within your project solves any reproducibility issues that may arise.

Bringing It All Together

To use poetry in a docker image you need to:

  1. Install your desired version of poetry
  2. Configure virtual environment location
  3. Install your dependencies
  4. Use poetry run python ... to run your application

A Working Example:

This is a minimal flask project managed with poetry.

You can copy these contents to your machine to test it out (expect for poerty.lock)

Project structure

python-poetry-docker/
|- Dockerfile
|- app.py
|- pyproject.toml
|- poetry.lock

Dockerfile

FROM python:3.10 as poetry-base

RUN apt-get update && apt-get install -y curl

# https://python-poetry.org/docs/master/#installation
ENV POETRY_VERSION=1.1.13

RUN curl -sSL https://install.python-poetry.org | python3 - --version "$POETRY_VERSION"

# See "Add Poetry to your PATH" in https://python-poetry.org/docs/master/#installing-with-the-official-installer
ENV PATH="/root/.local/bin:$PATH"

FROM poetry-base as example-app

WORKDIR /app

COPY poetry.lock pyproject.toml ./

# Configure virtualenv location inside project
RUN poetry config virtualenvs.in-project true --local

RUN poetry install --no-dev

COPY . /app

EXPOSE 5000
CMD [ "poetry", "run", "python", "-m", "flask", "run", "--host=0.0.0.0" ]

app.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, Docker!'

pyproject.toml

[tool.poetry]
name = "python-poetry-docker-example"
version = "0.1.0"
description = ""
authors = ["Someone <someone@example.com>"]

[tool.poetry.dependencies]
python = "^3.10"
Flask = "^2.1.2"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

poetry.lock

[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"

... more lines ommitted

Full contents in gist.

Soof Golan
  • 151
  • 1
  • 5
  • This answer turned out to be quite long, edits welcome to reduce mental overhead – Soof Golan Jun 01 '22 at 16:46
  • 2
    Your answer is very good and will written. I just disagree with the point to disable virtual environments in a docker image. venv's isolate the dependencies of your application from the one installed in the system. And those exists in a docker images the same way as in a "normal" system. – finswimmer Jun 01 '22 at 17:13
  • [off topic] @finswimmer, disabling virtualenv within docker actually solved a reproducibility bug I experienced with Google Cloud Run. In my first version, I left poetry with its default config, and it created a virtual environment during the build. Running locally with `docker run` worked as expected. When starting up the **same image** (same sha256) with Cloud Run poetry creates another virtualenv, empty of all dependencies and the app fails to start (due to a missing pacakage). This was kind of shocked about docker reproducibility in general, but wanted to post a valid solution. – Soof Golan Jun 02 '22 at 04:54
  • [still off topic] Apparently Cloud Run [modify the HOME variable](https://cloud.google.com/run/docs/issues#home) (), that is the root cause of my reproducibility issue. – Soof Golan Jun 02 '22 at 06:04