I am trying to create a Docker image for my flask application, shown below:
# syntax=docker/dockerfile:1
FROM python:3.9.5-slim-buster as build
RUN python3 -m venv /app/venv
COPY . /app
RUN /app/venv/bin/pip install -r /app/requirements.txt
FROM gcr.io/distroless/python3
COPY --from=build /app /app
# ENV PATH = "/app/venv/bin:${PATH}"
EXPOSE 5000
ENTRYPOINT [ "/app/venv/bin/python3" , "main.py"]
Basically, I have two build stages: the first one creates a virtual environment with venv
, and the second uses a distroless image and copies the virtual environment (along with the rest of my files) from the previous build stage to the new one.
The Docker images builds without issue, but once I try to run the image with docker run
, I get the following error:
docker: Error response from daemon: failed to create shim: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/app/venv/bin/python3": stat /app/venv/bin/python3: no such file or directory: unknown.
This error confuses me, since I know the python executable is located at /app/venv/bin
, and I double checked this by exporting the container using docker export <container name> > container.tar
and exploring the tar file's contents. From what I can tell, I should not be receiving this error.
What am I doing wrong?
Edit: As requested by @RQDQ, below are bare minimum versions of my requirements.txt
and main.py
:
requirements.txt
:
click==8.1.3
Flask==2.1.2
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.0.1
Werkzeug==2.1.2
main.py
:
from flask import Flask
app = Flask(__name__, static_folder='build')
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if (__name__ == "__main__"):
app.run(use_reloader=False, host='0.0.0.0', port=5000, threaded=True)
CodePudding user response:
virtualenv
is not standalone environment that can be copied between OSes (or containers):
$ python -m venv venv
$ ls -l venv/bin/
total 36
-rw-r--r-- 1 user user 1990 Jun 2 08:35 activate
-rw-r--r-- 1 user user 916 Jun 2 08:35 activate.csh
-rw-r--r-- 1 user user 2058 Jun 2 08:35 activate.fish
-rw-r--r-- 1 user user 9033 Jun 2 08:35 Activate.ps1
-rwxr-xr-x 1 user user 239 Jun 2 08:35 pip
-rwxr-xr-x 1 user user 239 Jun 2 08:35 pip3
-rwxr-xr-x 1 user user 239 Jun 2 08:35 pip3.10
lrwxrwxrwx 1 user user 46 Jun 2 08:35 python -> /home/user/.pyenv/versions/3.10.2/bin/python
lrwxrwxrwx 1 user user 6 Jun 2 08:35 python3 -> python
lrwxrwxrwx 1 user user 6 Jun 2 08:35 python3.10 -> python
As you can see python
executables are just links to original python executable. It is something like snapshot for your original python that may be reverted or applied. But snapshot is useless if you don't have original base. So you have to create venv
at same environment that will be used on.
However in case of containers you don't need venv
at all. Container is already an isolated environment and you don't need one more isolation level with venv. (At least my question about why we need to use venv
inside container is still don't have an answer)
In short: remove all venv
related lines:
# syntax=docker/dockerfile:1
FROM gcr.io/distroless/python3
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000
ENTRYPOINT [ "python" , "main.py"]
However if you need to some extra libraries/compile tools (like gcc
) to build python libraries when installing it by pip
then venv
may be used to be able move only resulted library binaries without store compile tools inside container.
In this case you have to use same (or compatible) python base at build
image and resulted image (venv
"snapshot" should be applied to compatible base).
Let's see this example:
FROM debian:11-slim AS build
...
FROM gcr.io/distroless/python3-debian11
...
Both images at least Debian
based.
Or another example:
FROM python:3.9-slim as compiler
...
FROM python:3.9-slim as runner
...
And again base of builder
and runner
is the same
Looks like python:3.9.5-slim-buster
and gcr.io/distroless/python3
are both Debian based and should be compatible, but probably it is not fully compatible.
You change endpoint to ENTRYPOINT [ "sleep" , "600"]
. That will allow to keep container running for 10 minutes. After that attach to running container: docker exec -it container_name bash
and check is python
executable exists: ls -l /app/venv/bin/
or just simply use it without venv
as I said before