What profit of using virtualenv with pyenv or docker? - python

I recently join in current project and find steps in ReadMe like this (and I can't contact with man who create it):
# install pyenv
git clone git://github.com/pyenv/pyenv.git ~/.pyenv
...
pyenv install 3.7.9
pyenv global 3.7.9
# install venv
pip install virtualenv
# create virtual environment
source .venv/bin/activate
# install dependencies
pip install pipenv
pipenv install --dev
...
So my questions are:
what is reason/profit of using virtual environment inside virtual environment?
what is reason/profit of using pyenv or venv if we running application inside python container? Isn't better idea to install all libraries using docker's system pip/python? Docker container is already abstract layer (virtual environment).
pyenv already creating user depending environment that can be easily removed/changed/reseted without influence on system python libraries
In other way environment created with virtualenv still depending on system libraries, so It can't be moved easily between servers.
Maybe here is some benefits or good practices of using venv when service deploying?
Even localstack is using virualenv inside docker. Isn't docker isolation level is not enough?
Update 2022/06/02
According this answer looks like virtualenv may be used to keep size of resulted image smaller.
I checked two patterns:
staged build with virtualenv
FROM python:3-alpine as compiler
ENV PYTHONUNBUFFERED 1
WORKDIR /app/
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./r.txt /app/r.txt
RUN pip install -Ur r.txt
FROM python:3-alpine as runner
WORKDIR /app/
COPY --from=compiler /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
CMD ["sleep", "999" ]
and
unstaged build without virtualenv
FROM python:3-alpine as runner
WORKDIR /app/
COPY ./r.txt /app/r.txt
RUN python -m venv /opt/venv
RUN pip install -Ur r.txt && pip cache purge
CMD ["sleep", "999" ]
where r.txt is:
django
django-rest-framework
flask
fastapi
Result is:
$ docker images | grep stage
unstaged_python latest 08460a18018c ... 160MB
staged_python latest dd606b218724 ... 151MB
Conclusion:
venv may be used to save image total size, however size difference is not so big. Also unstaged image may be little bit more cleaned after pip installation to reduce total size. In other words venv usage may reasonable when we have many heavy build operations using compile tools that needed only when build (compile) timing and may be removed when image is ready.

Related

WARNING: Running pip as the 'root' user

I am making simple image of my python Django app in Docker. But at the end of the building container it throws next warning (I am building it on Ubuntu 20.04):
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead
Why does it throw this warning if I am installing Python requirements inside my image? I am building my image using:
sudo docker build -t my_app:1 .
Should I be worried about warning that pip throws, because I know it can break my system?
Here is my Dockerfile:
FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
The way your container is built doesn't add a user, so everything is done as root.
You could create a user and install to that users's home directory by doing something like this;
FROM python:3.8.3-alpine
RUN pip install --upgrade pip
RUN adduser -D myuser
USER myuser
WORKDIR /home/myuser
COPY --chown=myuser:myuser requirements.txt requirements.txt
RUN pip install --user -r requirements.txt
ENV PATH="/home/myuser/.local/bin:${PATH}"
COPY --chown=myuser:myuser . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
This behavior was introduced in pip 21.1 as a "bug fix".
As of pip 22.1, you can now opt out of the warning using a parameter:
pip install --root-user-action=ignore
You can ignore this in your container by using the environment:
ENV PIP_ROOT_USER_ACTION=ignore
#11035
UPDATE 220930
The good news of this answer here is just that you can ignore the warning, but ignoring the warning is not best practice anymore for pip version >=22.1. At the time of writing this answer, the new trick for pip version >=22.1 was not known to me.
pip version >=22.1
Follow the answer of Maximilian Burszley. It was not known to me at the time of writing and allows you to avoid the warning with a tiny parameter.
pip version >=21.1 and <22.1
You can ignore this warning since you create the image for an isolated purpose and it therefore is organizationally as isolated as a virtual environment. Not technically, but that does not matter here.
It usually should not pay off to invest the time and create a virtual environment in an image or add a user as in the other answer only to avoid the warning, since you should not have any issues with this. It might cloud your view during debugging, but it does not stop the code from working.
Just check pip -V and pip3 -V to know whether you need to pay attention not to mistakenly use pip for Python 2 when you want pip for Python 3. But that should be it, and if you install only pip for python 3, you will not have that problem anyway.
pip version <21.1
In these older versions, the warning does not pop up anyway, see the other answer again. And it is also clear from the age of the question that this warning did not show up in older versions.
I don't like ignoring warnings, as one day you will oversee an important one.
Here is a good explanation on best docker practices with python. Search for Example with virtualenv and you'll find this:
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install -r requirements.txt
# final stage
FROM python:3.9-slim
COPY --from=builder /opt/venv /opt/venv
WORKDIR /app
ENV PATH="/opt/venv/bin:$PATH"
Works like charm. No warnings or alike. BTW they also recommend to create a non root user for security reasons.
EDIT: to get rid of all warnings you may also want to add the following entries to the builder part of your Dockerfile (applies for Debian 8.3.x):
ARG DEBIAN_FRONTEND=noninteractive
ARG DEBCONF_NOWARNINGS="yes"
RUN python -m pip install --upgrade pip && \
...

Dockerized flask app builds and runs locally but wont work when deployed on Azure [duplicate]

I have a Dockerfile where I try to activate python virtualenv after what, it should install all dependencies within this env. However, everything still gets installed globally. I used different approaches and non of them worked. I also do not get any errors. Where is a problem?
1.
ENV PATH $PATH:env/bin
2.
ENV PATH $PATH:env/bin/activate
3.
RUN . env/bin/activate
I also followed an example of a Dockerfile config for the python-runtime image on Google Cloud, which is basically the same stuff as above.
Setting these environment variables are the same as running source /env/bin/activate.
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
Additionally, what does ENV VIRTUAL_ENV /env mean and how it is used?
You don't need to use virtualenv inside a Docker Container.
virtualenv is used for dependency isolation. You want to prevent any dependencies or packages installed from leaking between applications. Docker achieves the same thing, it isolates your dependencies within your container and prevent leaks between containers and between applications.
Therefore, there is no point in using virtualenv inside a Docker Container unless you are running multiple apps in the same container, if that's the case I'd say that you're doing something wrong and the solution would be to architect your app in a better way and split them up in multiple containers.
EDIT 2022: Given this answer get a lot of views, I thought it might make sense to add that now 4 years later, I realized that there actually is valid usages of virtual environments in Docker images, especially when doing multi staged builds:
FROM python:3.9-slim as compiler
ENV PYTHONUNBUFFERED 1
WORKDIR /app/
RUN python -m venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt /app/requirements.txt
RUN pip install -Ur requirements.txt
FROM python:3.9-slim as runner
WORKDIR /app/
COPY --from=compiler /opt/venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY . /app/
CMD ["python", "app.py", ]
In the Dockerfile example above, we are creating a virtualenv at /opt/venv and activating it using an ENV statement, we then install all dependencies into this /opt/venv and can simply copy this folder into our runner stage of our build. This can help with with minimizing docker image size.
There are perfectly valid reasons for using a virtualenv within a container.
You don't necessarily need to activate the virtualenv to install software or use it. Try invoking the executables directly from the virtualenv's bin directory instead:
FROM python:2.7
RUN virtualenv /ve
RUN /ve/bin/pip install somepackage
CMD ["/ve/bin/python", "yourcode.py"]
You may also just set the PATH environment variable so that all further Python commands will use the binaries within the virtualenv as described in https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
FROM python:2.7
RUN virtualenv /ve
ENV PATH="/ve/bin:$PATH"
RUN pip install somepackage
CMD ["python", "yourcode.py"]
Setting this variables
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
is not exactly the same as just running
RUN . env/bin/activate
because activation inside single RUN will not affect any lines below that RUN in Dockerfile. But setting environment variables through ENV will activate your virtual environment for all RUN commands.
Look at this example:
RUN virtualenv env # setup env
RUN which python # -> /usr/bin/python
RUN . /env/bin/activate && which python # -> /env/bin/python
RUN which python # -> /usr/bin/python
So if you really need to activate virtualenv for the whole Dockerfile you need to do something like this:
RUN virtualenv env
ENV VIRTUAL_ENV /env # activating environment
ENV PATH /env/bin:$PATH # activating environment
RUN which python # -> /env/bin/python
Although I agree with Marcus that this is not the way of doing with Docker, you can do what you want.
Using the RUN command of Docker directly will not give you the answer as it will not execute your instructions from within the virtual environment. Instead squeeze the instructions executed in a single line using /bin/bash. The following Dockerfile worked for me:
FROM python:2.7
RUN virtualenv virtual
RUN /bin/bash -c "source /virtual/bin/activate && pip install pyserial && deactivate"
...
This should install the pyserial module only on the virtual environment.
If you your using python 3.x :
RUN pip install virtualenv
RUN virtualenv -p python3.5 virtual
RUN /bin/bash -c "source /virtual/bin/activate"
If you are using python 2.x :
RUN pip install virtualenv
RUN virtualenv virtual
RUN /bin/bash -c "source /virtual/bin/activate"
Consider a migration to pipenv - a tool which will automate virtualenv and pip interactions for you. It's recommended by PyPA.
Reproduce environment via pipenv in a docker image is very simple:
FROM python:3.7
RUN pip install pipenv
COPY src/Pipfile* ./
RUN pipenv install --deploy
...

pipenv --system option for docker. What is the suggested way to get all the python packages in docker

I use pipenv for my django app.
$ mkdir djangoapp && cd djangoapp
$ pipenv install django==2.1
$ pipenv shell
(djangoapp) $ django-admin startproject example_project .
(djangoapp) $ python manage.py runserver
Now i am shifting to docker environment.
As per my understanding pipenv only installs packages inside a virtualenv
You don't need a virtual env inside a container, docket container IS a virtual environment in itself.
Later after going through many Dockerfile 's i found --system option to install in the system.
For example the following i found:
https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/
COPY ./Pipfile /usr/src/app/Pipfile
RUN pipenv install --skip-lock --system --dev
https://hub.docker.com/r/kennethreitz/pipenv/dockerfile
# -- Install dependencies:
ONBUILD RUN set -ex && pipenv install --deploy --system
https://wsvincent.com/beginners-guide-to-docker/
# Set work directory
WORKDIR /code
# Copy Pipfile
COPY Pipfile /code
# Install dependencies
RUN pip install pipenv
RUN pipenv install --system
So --system is only sufficient or --deploy --system is better way. And --skip-lock --system --dev which is different again.
So can some one guide how to get my environment back in my Docker
A typical Docker deployment would involve having a requirements.txt (it's a file where you can store your pip dependencies, including Django itself) file and then in your Dockerfile you do something like:
FROM python:3.7 # or whatever version you need
ADD requirements.txt /code/
WORKDIR /code
# install your Python dependencies
RUN pip install -r requirements.txt
# run Django
CMD [ "python", "./manage.py", "runserver", "0.0.0.0:8000"]
You don't need pipenv here at all since you no longer have a virtual environment as you say.
Even better you can configure a lot of that stuff in a docker-compose.yml file and then use docker-compose to run and manage your services, not just Django.
Docker have a very good tutorial on dockerising Django with it. And if you're unsure what's going on in the Dockerfile itself, check the manual.
In either a docker image, a CI pipeline, a production server or even in your development workstation: you should always include the --deploy flag in your installs unless you want to potentially relock all dependencies, e.g. while evolving your requirements. It will check that the lockfile is up-to-date and will never install anything that is not listed there.
As for the --system flag, you'd better drop it. There is no real harm on using a virtual environment inside docker images, but some subtle benefits. See this comment by #anishtain4. Pipenv now recommends against system-wide installs https://github.com/pypa/pipenv/pull/2762.

Activate python virtualenv in Dockerfile

I have a Dockerfile where I try to activate python virtualenv after what, it should install all dependencies within this env. However, everything still gets installed globally. I used different approaches and non of them worked. I also do not get any errors. Where is a problem?
1.
ENV PATH $PATH:env/bin
2.
ENV PATH $PATH:env/bin/activate
3.
RUN . env/bin/activate
I also followed an example of a Dockerfile config for the python-runtime image on Google Cloud, which is basically the same stuff as above.
Setting these environment variables are the same as running source /env/bin/activate.
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
Additionally, what does ENV VIRTUAL_ENV /env mean and how it is used?
You don't need to use virtualenv inside a Docker Container.
virtualenv is used for dependency isolation. You want to prevent any dependencies or packages installed from leaking between applications. Docker achieves the same thing, it isolates your dependencies within your container and prevent leaks between containers and between applications.
Therefore, there is no point in using virtualenv inside a Docker Container unless you are running multiple apps in the same container, if that's the case I'd say that you're doing something wrong and the solution would be to architect your app in a better way and split them up in multiple containers.
EDIT 2022: Given this answer get a lot of views, I thought it might make sense to add that now 4 years later, I realized that there actually is valid usages of virtual environments in Docker images, especially when doing multi staged builds:
FROM python:3.9-slim as compiler
ENV PYTHONUNBUFFERED 1
WORKDIR /app/
RUN python -m venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY ./requirements.txt /app/requirements.txt
RUN pip install -Ur requirements.txt
FROM python:3.9-slim as runner
WORKDIR /app/
COPY --from=compiler /opt/venv /opt/venv
# Enable venv
ENV PATH="/opt/venv/bin:$PATH"
COPY . /app/
CMD ["python", "app.py", ]
In the Dockerfile example above, we are creating a virtualenv at /opt/venv and activating it using an ENV statement, we then install all dependencies into this /opt/venv and can simply copy this folder into our runner stage of our build. This can help with with minimizing docker image size.
There are perfectly valid reasons for using a virtualenv within a container.
You don't necessarily need to activate the virtualenv to install software or use it. Try invoking the executables directly from the virtualenv's bin directory instead:
FROM python:2.7
RUN virtualenv /ve
RUN /ve/bin/pip install somepackage
CMD ["/ve/bin/python", "yourcode.py"]
You may also just set the PATH environment variable so that all further Python commands will use the binaries within the virtualenv as described in https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
FROM python:2.7
RUN virtualenv /ve
ENV PATH="/ve/bin:$PATH"
RUN pip install somepackage
CMD ["python", "yourcode.py"]
Setting this variables
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
is not exactly the same as just running
RUN . env/bin/activate
because activation inside single RUN will not affect any lines below that RUN in Dockerfile. But setting environment variables through ENV will activate your virtual environment for all RUN commands.
Look at this example:
RUN virtualenv env # setup env
RUN which python # -> /usr/bin/python
RUN . /env/bin/activate && which python # -> /env/bin/python
RUN which python # -> /usr/bin/python
So if you really need to activate virtualenv for the whole Dockerfile you need to do something like this:
RUN virtualenv env
ENV VIRTUAL_ENV /env # activating environment
ENV PATH /env/bin:$PATH # activating environment
RUN which python # -> /env/bin/python
Although I agree with Marcus that this is not the way of doing with Docker, you can do what you want.
Using the RUN command of Docker directly will not give you the answer as it will not execute your instructions from within the virtual environment. Instead squeeze the instructions executed in a single line using /bin/bash. The following Dockerfile worked for me:
FROM python:2.7
RUN virtualenv virtual
RUN /bin/bash -c "source /virtual/bin/activate && pip install pyserial && deactivate"
...
This should install the pyserial module only on the virtual environment.
If you your using python 3.x :
RUN pip install virtualenv
RUN virtualenv -p python3.5 virtual
RUN /bin/bash -c "source /virtual/bin/activate"
If you are using python 2.x :
RUN pip install virtualenv
RUN virtualenv virtual
RUN /bin/bash -c "source /virtual/bin/activate"
Consider a migration to pipenv - a tool which will automate virtualenv and pip interactions for you. It's recommended by PyPA.
Reproduce environment via pipenv in a docker image is very simple:
FROM python:3.7
RUN pip install pipenv
COPY src/Pipfile* ./
RUN pipenv install --deploy
...

How to keep track of pip installed packages on one docker container to use them in another

i'm trying to get involved in docker magic and there is a question:
I want to run container in which installed all python packages and after up of this "source" container run my python script that uses those installed on first docker container packages?
I have one dockerfile in which I have installed python3 and pip and in docker-compose.yml file i am building all requirments in first container
How to update docker-compose.yml file to make second container be able to use all installed on another container packages?
use pip freeze to get a list of all installed pip packages along with their specific versions. Standard practice is to keep a text file requirements.txt for pip installations.
$ pip freeze > requirements.txt
$ pip install -r requirements.txt
add this line to your docker file:
ONBUILD RUN pip install -r /app/requirements.txt
Another option is to use a virtualenv and docker volumes.
In one image build a virtualenv and make it a volume:
RUN virtualenv /venv
RUN /venv/bin/pip install ...
VOLUME /venv
And for the second container use volumes_from: ['the-first-container']
You might not need the virtualenv if you want to just make the default python site-packages path a VOLUME.

Categories