Getting Started with the Python Chainguard Image
The Python images based on Wolfi and maintained by Chainguard provide distroless images that are suitable for building and running Python workloads.
Chainguard offers both a minimal runtime image containing just Python, and a development image that contains a package manager and a shell. Because Python applications typically require the installation of third-party dependencies via the Python package installer pip, you may need to implement a multi-stage Docker build that uses the Python -dev
image to set up the application.
In this guide, we’ll cover two examples to showcase Python container images based on Wolfi as a runtime. In the first, we’ll use the minimal image containing just Python (which has access to the Python standard library), and in the second we’ll demonstrate a multi-stage build.
What is distroless
Distroless images are minimalist container images containing only essential software required to build or execute an application. That means no package manager, no shell, and no bloat from software that only makes sense on bare metal servers.What is Wolfi
Wolfi is a community Linux undistro created specifically for containers. This brings distroless to a new level, including additional features targeted at securing the software supply chain of your application environment: comprehensive SBOMs, signatures, daily updates, and timely CVE fixes.Chainguard Images
Chainguard Images are a mix of distroless and development images based on Wolfi. Nightly builds make sure images are up-to-date with the latest package versions and patches from upstream Wolfi.Example 1 — Minimal Python Chainguard Image
In this example, we’ll build and run a distroless Python Chainguard Image in a single-stage build process. We’ll first make a demonstration app and then build and run it.
Step 1: Setting up a Demo Application
We’ll start by creating a basic command-line Python application to serve as a demo. This app will generate random octopus facts based on a list in a text file. This app will use the random
module from the Python standard library.
First, create a directory for your app. You can use any meaningful name and path for you, our example will use octo-facts/
.
mkdir ~/octo-facts/ && cd $_
Create a new file to serve as the application entry point. We’ll use main.py
. You can edit this file in whatever code editor you would like. We’ll use Nano as an example.
nano main.py
The following Python script defines a light CLI app that takes in a text file, octo-facts.txt
, and returns a random line from that file.
'''Import random module to implement random.choice() function'''
import random
def random_line(text):
'''Opens and reads lines of a UTF-8 encoded file, returning a random line'''
with open(text, 'r', encoding='UTF-8') as file:
line = file.readlines()
return random.choice(line)
def main():
'''Prints random line from facts.txt; verify your path'''
print(random_line('facts.txt'))
if __name__ == "__main__":
main()
Copy this code to your main.py
script, save and close the file.
Next, pull down the facts.txt
file with curl
. Inspect the URL before downloading it to ensure it is safe to do so. Make sure you are still in the same directory where your main.py
script is.
curl -O https://raw.githubusercontent.com/chainguard-dev/edu-images-demos/main/python/octo-facts/facts.txt
At this point, you can run the script and be sure you are satisfied with the functionality. It is recommended that you use a Python programming environment. Ensure whether you will be using the python
or python3
command.
python main.py
You should receive the output of a randomized octopus fact.
The wolfi octopus was discovered in 1913.
The demo application is now ready. In the next step, you’ll create a Dockerfile to run your app.
Step 2: Creating the Dockerfile
For this single-stage build, we’ll only use one FROM
line in our Dockerfile. Our resulting image will be based on the distroless Python Wolfi image, which means it doesn’t come with a package manager or even a shell.
We’ll begin by creating a Dockerfile. Again, you can use any code editor of your choice, we’ll use Nano for demonstation purposes.
nano Dockerfile
The following Dockerfile will:
- Start a build stage based on the
python:latest
image; - Declare the working directory;
- Copy the script and the text file that’s being read;
- Set up the application as entry point for this image.
FROM cgr.dev/chainguard/python:latest
WORKDIR /octo-facts
COPY main.py facts.txt ./
ENTRYPOINT [ "python", "/octo-facts/main.py" ]
Save the file when you’re finished.
You can now build the image. If you receive an error, try again with sudo
.
docker build . -t octo-facts
Once the build is finished, run the image.
docker run --rm octo-facts
And you should get output similar to what you got before, with a random octopus fact.
Octopuses can breathe and see through their skin.
You have successfully completed the single-stage Python Chainguard Image. At this point, you can continue to the multi-stage example or advanced usage.
Example 2 — Multi-Stage Build for Python Chainguard Image
In this example, we’ll build and run a multi-stage Python Chainguard Image. We’ll have a build image that includes pip and a shell before creating a final distroless image without these development tools for production.
Step 1: Setting up a Demo Application
We’ll start by creating a Python application that will take in an image file and convert it to ANSI escape sequences on the CLI to render an image.
To begin, create a directory for your app. You can use any meaningful name and path that resonates with you, our example will use linky/
.
mkdir ~/linky/ && cd $_
We’ll first write out the requirements for our app in a new file, for example we named our file requirements.txt
. You can edit this file in your preferred code editor, in our case we will use Nano.
nano requirements.txt
We’ll use version 68.2.2 of Python setuptools and also install climage. We need to use a slightly older version of setuptools for compatability with climage. Add the following text to the file:
setuptools==68.2.2
climage==0.2.0
Save the file and we will next create a new file with our python code called linky.py
. You can edit this file in whatever code editor you would like. We’ll use Nano as an example.
nano linky.py
Add the following Python code which defines a CLI app that takes in an image file, linky.png
, and
prints a representation of that file to the terminal:
'''import climage module to display images on terminal'''
from climage import convert
def main():
'''Take in PNG and output as ANSI to terminal'''
output = convert('linky.png', is_unicode=True)
print(output)
if __name__ == "__main__":
main()
Next, pull down the linky.png
image file with curl
. Inspect the URL before downloading it to ensure it is safe to do so. Make sure you are still in the same directory where your linky.py
script is.
curl -O https://raw.githubusercontent.com/chainguard-dev/edu-images-demos/main/python/linky/linky.png
If you have python and pip installed in your local environment, you can now install the dependencies with pip
and run our program. Don’t worry if you don’t have python installed, you can simply skip this step and move onto the Dockerfile.
pip install -r requirements.txt
python linky.py
You’ll receive a representation of the Chainguard Linky logo on the command line. With your demo application ready, you’re ready to move onto the container stage.
Step 2: Creating the Dockerfile
To make sure our final image is distroless while still being able to install dependencies with pip,
our build will consist of two stages: first, we’ll build the application using the
python:latest-dev
image variant, a Wolfi-based image that includes pip and other useful tools for
development. Then, we’ll create a separate stage for the final image. The resulting image will be
based on the distroless Python Wolfi image, which means it doesn’t come with pip or even a shell.
Begin by editing a Dockerfile, with Nano for instance.
nano Dockerfile
The following Dockerfile will:
- Start a new build stage based on the
python:latest-dev
image and call itbuilder
; - Create a new virtual environment to cleanly hold the application’s dependencies;
- Copy
requirements.txt
from the current directory to the/linky
location in the container; - Run
pip install --no-cache-dir -r requirements.txt
to install dependencies; - Start a new build stage based on the
python:latest
image; - Copy the dependencies in the virtual environment from the builder stage, and the source code from the current directory;
- Set up the application as the entry point for this image.
Copy this configuration to your own Dockerfile:
FROM cgr.dev/chainguard/python:latest-dev as builder
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/linky/venv/bin:$PATH"
WORKDIR /linky
RUN python -m venv /linky/venv
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM cgr.dev/chainguard/python:latest
WORKDIR /linky
ENV PYTHONUNBUFFERED=1
ENV PATH="/venv/bin:$PATH"
COPY linky.py linky.png ./
COPY --from=builder /linky/venv /venv
ENTRYPOINT [ "python", "/linky/linky.py" ]
Save the file when you’re finished.
You can now build the image. If you receive a permission error, try running under sudo
.
docker build -t linky .
Once the build is finished, run the image with:
docker run --rm linky
And you should get output similar to what you got before, with a printed Linky on the command line.
Advanced Usage
If your project requires a more specific set of packages that aren't included within the general-purpose Python Chainguard Image, you'll first need to check if the package you want is already available on the wolfi-os repository.
Note: If you're building on top of an image other than the wolfi-base image, the image will run as a non-root user. Because of this, if you need to install packages with
apk install
you need to use theUSER root
directive.
If the package is available, you can use the wolfi-base image in a Dockerfile and install what you need with apk
, then use the resulting image as base for your app.
Check the "Using the wolfi-base Image" section of our images quickstart guide for more information.
If the packages you need are not available, you can build your own apks using melange. Please refer to this guide for more information.
Last updated: 2023-11-02 13:46