I am thinking about switching from pip & virtualenv to pipenv. But after studying the documentation I am still at a loss on how the creators of pipenv structured the deployment workflow.
For example, in development I have a Pipfile
& a Pipfile.lock
that define the environment. Using a deployment script I want to deploy
git pull
via Github to production serverpipenv install
creates/refreshes the environment in the home directory of the deployment userBut I need a venv in a specific directory which is already configured in systemd or supervisor. E.g.: command=/home/ubuntu/production/application_xy/env/bin/gunicorn module:app
pipenv creates the env in some location such as
/home/ultimo/.local/share/virtualenvs/application_xy-jvrv1OSi
What is the intended workflow to deploy an application with pipenv
?
Pipenv was never dead. The author of that article doesn't know what he's talking about. The latest release of pipenv was 25 days ago and there were 8 releases in 2020. He also says he uses pyenv for virtual environment management, but pyenv doesn't even do that.
Pipenv rolls the management of Python virtual environments and Python packages into a single tool. Pipenv ensures that each project uses the correct version of each package it needs, and that each of those packages has the correct dependencies as well.
Pipenv is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world. Windows is a first-class citizen, in our world.
While pip can install Python packages, Pipenv is recommended as it's a higher-level tool that simplifies dependency management for common use cases. This does a user installation to prevent breaking any system-wide packages.
You have few options there.
You can run your gunicorn via pipenv run
:
pipenv run gunicorn module:app
This creates a slight overhead, but has the advantage of also loading environment from $PROJECT_DIR/.env
(or other $PIPENV_DOTENV_LOCATION
).
You can set the PIPENV_VENV_IN_PROJECT
environment variable. This will keep pipenv's virtualenv in $PROJECT_DIR/.venv
instead of the global location.
You can use an existing virtualenv and run pipenv from it. Pipenv will not attempt to create its own virtualenv if it's run from one.
You can just use the weird pipenv-created virtualenv path.
I've just switched to pipenv
for deployment and my workflow is roughly as follows (managed with ansible). For an imaginary project called "project", assuming that a working Pipfile.lock is checked into source control:
Clone the git repository:
git clone https://github.com/namespace/project.git /opt/project
Change into that directory
cd /opt/project
Check out the target reference (branch, tag, ...):
git checkout $git_ref
Create a virtualenv somewhere, with the target Python version (3.6, 2.7, etc):
virtualenv -p"python$pyver" /usr/local/project/$git_ref
Call pipenv in the context of that virtualenv, so it won't install its own:
VIRTUAL_ENV="/usr/local/project/$git_ref" pipenv --python="/usr/local/project/$git_ref/bin/python" install --deploy
The --deploy
will throw an error, when the Pipfile.lock does not match the Pipfile.
Install the project itself using the virtualenv's pip
(only necessary if it isn't already in the Pipfile):
/usr/local/project/$git_ref/bin/pip install /opt/project
Set a symlink to the new installation directory:
ln -s /usr/local/project/$git_ref /usr/local/project/current
My application is then callable e.g. with /usr/local/project/current/bin/project_exec --foo --bar
, which is what's configured in supervisor, for instance.
All of this is triggered when a tag is pushed to the remote.
As the virtualenvs of earlier versions remain intact, a rollback is simply done by setting the current-
symlink back to an earlier version. I.e. if tag 1.5 is broken, and I want to go back to 1.4, all I have to do is ln -s /usr/local/project/1.4 /usr/local/project/current
and restart the application with supervisorctl
.
I think pipenv is very good for managing dependencies but is too slow, cumbersome and still a bit unstable for using it for automatic deployments.
Instead I use virtualenv (or virtualenvwrapper) and pip on the target machine.
On my build/development machine I create a requirements.txt
compatible text file using pipenv lock -r
:
$ pipenv lock -r > deploy-requirements.txt
While deploying, inside a virtualenv I run:
$ pip install -r deploy-requirements.txt
Just do this:
mkdir .venv
pipenv install
Explanation:
pipenv
checks your project directory for a sub directory named .venv
. If it finds it, then pipenv creates a local virtual environment (because then it sets automatically PIPENV_VENV_IN_PROJECT=true
)
So now if you want you can either activate the virtual environment with:
source .venv/bin/activate
Or config you app.conf for gunicorn with something like this:
exec /path/to/.venv/bin/gunicorn myapp:app
To create virtual environment in the same directory as the project set the following environment variable doc
PIPENV_VENV_IN_PROJECT=true
This installs the dependencies to .venv
directory inside project. Available from PipEnv v2.8.7
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With