Docker layer caching-friendly workflow with pipenv
See original GitHub issueThe usual way to create a Docker-based deployment (e.g. for deploying on Kubernetes) for a Python application looked something like this, using a requirements.txt
, produced by pip freeze
or pip-compile
:
FROM python3.6
RUN mkdir /app
WORKDIR /app
# Only copy application dependencies to take advantage of image layer caching,
# i.e. if the "requirements.txt" file doesn't change, the layer is cached
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
# Install the actual application code now. Usually, this is the part of the code
# that changes, thus invalidating the image layer cache.
ADD . /app
# We actually need to do this because our application code might have a "setup.py"
# which defines "entry_points". If that wasn't the case, it could be optional
RUN pip install .
# Run the app...
CMD ["python", "-m", "myapp.run", ... ]
Using pipenv
, I would imagine the equivalent would be something like:
FROM python3.6
# Install "pipenv"
RUN pip install pipenv
RUN mkdir /app
WORKDIR /app
# In a similar fashion as before if the "Pipfile.lock" doesn't change, the
# image layer is going to be cached.
COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
RUN pipenv install --deploy --system
ADD . /app
RUN pip install .
# Run the app...
CMD ["python", "-m", "myapp.run", ... ]
If this is something that others have come across and consider a best practice, I believe it would be useful to make it part of the official documentation, since pipenv
is meant to be a solution for applications.
For example, before that, I thought that the logical thing would be doing something like:
# Install my application dependencies
pipenv install requests flask celery ...
pipenv install --dev pytest ...
# ...develop my app...
# Add the app to the Pipfile
pipenv install -e .
# commit everything
git add .
git commit ...
This would make it easy for someone to create an application that could be easily installed locally with just a pipenv install --dev
. The problem is that now that the application package is part of the Pipfile, Docker layer caching is thrown out of the window (i.e. one has to do ADD . /app
much earlier in order for pipenv install --system --deploy
to be able to find the application’s setup.py
).
(This issue is in no way meant to be a complaint or a back in the “pip”-days I used to…-kind of rant. I’m really just hoping for a good discussion with practical advice and seeing how others tackle this issue using “pipenv”)
Issue Analytics
- State:
- Created 5 years ago
- Reactions:11
- Comments:19 (2 by maintainers)
Top GitHub Comments
I’d recommend multistage builds with docker and pipenv
Another workflow we have been investigating is building two images:
A dependencies-only image, tagged with the
_meta.hash.sha256
key from thePipfile.lock
:To build the image:
This image can be e.g. built on a regular basis by some cronjob/CI workflow, since it’s often the case that dependencies don’t change that often, compared to actual application code.
For the application image now, we can use
ARG
before the firstFROM
to specify the exact tag of our dependencies image:To build the image we need to pass the dependencies version via
--build-arg
:One caveat of this method is that, if the specific dependencies image might not have been already built, and thus you have to check your registry and trigger a build if needed before building the application image.