I am trying to execute a multistage docker build to create a light image. I am using poetry for dependency management. My dockerfile is as below
FROM python:3.12-slim as builder
RUN pip install poetry
RUN mkdir -p /app
COPY . /app
WORKDIR /app
RUN python -m venv .venv
RUN poetry install
FROM python:3.12-slim as base
COPY --from=builder /app /app
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "main"]
My main.py file is (yes i know I am not using the yaml import, just trying to test dependency resolution)
import yaml
if __name__ == '__main__':
print(f'Hi, Poetry')
My pyproject.toml file is as below
[tool.poetry]
name = "poetrydemo"
version = "0.1.0"
description = ""
readme = "README.md"
package-mode = false
[tool.poetry.dependencies]
python = "^3.12"
pyyaml = "^6.0.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
After i build
docker build -t poetrydemo:latest .
and run
docker run poetrydemo:latest
I get a ModuleNotFoundError: No module named 'yaml'
error
Examining the contents of the container,
docker run -it --entrypoint sh poetrydemo:latest
I see the .venv file is copied over correctly and the path is set correctly, but still does not find the module. What am I missing? If it helps my folder structure is as below
Your setup here is mixing a couple of different tools. You’re both manually creating a virtual environment and also asking Poetry to install one. poetry install
creates its own virtual environment, though, and installs packages there. You’re copying the manually-created virtual environment, but not the Poetry environment, from one stage to the other.
A multi-stage build isn’t benefiting you here at all. It’s useful if you need some tool to build your application but not to run it. In the context of a Python package, it’s useful if you have a C extension that needs a C toolchain to build it (if you have a RUN apt-get install build-essential
line). Your Dockerfile doesn’t have anything like that.
For the simple setup you show here, I’d remove the multi-stage build and the manual virtual environment. There are a couple of options for Python package management (including installing packages directly into the “system” Python, but isolated in this image) but maybe the most straightforward for this setup is just to embrace Poetry
FROM python:3.12-slim as builder
RUN pip install poetry
WORKDIR /app
COPY ./ ./
RUN poetry install
ENTRYPOINT ["poetry", "run"]
CMD ["python", "-m", "main"]
(The Poetry FAQ has a couple more hints on building efficient Dockerfiles that I won’t reproduce here.)
2