question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Docker layer caching-friendly workflow with pipenv

See original GitHub issue

The 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:closed
  • Created 5 years ago
  • Reactions:11
  • Comments:19 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
lukasz-madoncommented, Jul 25, 2019

I’d recommend multistage builds with docker and pipenv

FROM python:3.7 AS base

ENV LC_ALL C.UTF-8
ENV LANG C.UTF-8

WORKDIR /src

FROM base AS build

RUN pip install pipenv

...

# -- Adding Pipfiles, changes should rebuild whole layer
COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock

# -- Install dependencies: --deploy aborts if deps are incorrect with Pipfile.lock or Python version is incorrect
RUN pipenv install --dev --deploy --system


# use alpine for production to keep the image size small
FROM python:3.7-alpine AS release

...
# install dependencies from Pipfile.lock for reproducible builds
COPY Pipfile.lock Pipfile.lock
RUN pipenv install --system --deploy --ignore-pipfile

3reactions
slintcommented, Dec 8, 2018

Another workflow we have been investigating is building two images:

A dependencies-only image, tagged with the _meta.hash.sha256 key from the Pipfile.lock:

# ./Dockerfile.deps

FROM python:3.6

RUN pip install --upgrade pip pipenv setuptools wheel

RUN mkdir /app
WORKDIR /app

COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
RUN pipenv install --deploy --system

To build the image:

$ deps_version=$(jq ._meta.hash.sha256 Pipfile.lock)
$ docker build -t myapp-deps:$deps_version Dockerfile.deps

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 first FROM to specify the exact tag of our dependencies image:

# ./Dockefile

ARG DEPS_VERSION=latest
FROM myapp-deps:${DEPS_VERSION}

COPY . /app
RUN pip install .

CMD ["python", "-m", "myapp.run"]

To build the image we need to pass the dependencies version via --build-arg:

$ deps_version=$(jq ._meta.hash.sha256 Pipfile.lock)
$ docker build -t myapp:1.2.0 --build-arg DEPS_VERSION=$deps_version .

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Faster Docker builds with pipenv, poetry, or pip-tools
Here's how to do it with pipenv, poetry, or pip-tools. ... /tmp/myapp layer in the Docker cache, as well as all subsequent lines...
Read more >
Building a Python package, and a docker image via Pipenv
Now starts the hard part, using Pipenv. # creating the Pipfile and Pipfile.lock % pipenv --python 3.7# installing the dependencies % pipenv install...
Read more >
Build images on GitHub Actions with Docker layer caching
Save hours of googling and learn how to build images on GitHub Actions with proper Docker layer caching. With Docker's BuildKit capabilities ...
Read more >
A perfect way to Dockerize your Pipenv Python application
A simple Dockerfile for setting up a perfect Python application using Pipenv.
Read more >
GitHub Actions cache - Docker Documentation
mode, cache-to, min , max, min, Cache layers to export, see cache mode. ... the base branch and the default branch is accessible...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found