Python Like A Pro: Building Docker Containers
Packaging your application code into Docker containers is a tricky business. Python code is no exception. There are a ton of best practices that you need to know about if you’re going to build a container that is safe, secure, and maintainable over the long term. Buildpacks codify these best practices, and they’re open-source, so they’re a great way to turn your application code into runnable containers.
And because buildpacks completely remove the need for a
Dockerfile, they dramatically simplify the maintenance of your container images — particularly useful if you have multiple images to maintain. They’re no ‘flash-in-the-pan’ either. Developed by Heroku in 2011, and also used in Cloud Foundry, buildpacks have built and run millions of production workloads!
For Python developers, there are currently two implementations of the Buildpack standard which provide Python compatible buildpacks. One is from Google and the other is from Heroku. In this guide, you’ll learn how to use Heroku’s Python buildpack to create a container image for a sample Python application.
Before You Begin
There are a few things you need to do before getting started with Python Buildpacks:
Install Docker Desktop. The
packCLI requires the Docker daemon, so you’ll need to have that installed and running locally.
Check out Containers 101 on KubeAcademy, particularly if you’ve never worked with containers or Docker before.
Follow the documentation for installing
packin your local environment.
[Optional] If you are completely new to buildpacks, you might prefer to first read up on what are buildpacks?
Using Buildpacks With Python
Buildpacks work the same way no matter what language the code is written in. In this guide you’ll use a simple Python application, but remember that the process works in the same way for Node.JS, Java, Go, PHP, and more.
Follow the steps below to quickly create a container image for a Python application using the Heroku Python Buildpack.
Get The Sample Python Application
Download the sample Python application from Github and make the sample application’s folder your current working directory as follows:
> git clone https://github.com/benwilcock/buildpacks-python-demo.git > cd buildpacks-python-demo
In the folder you will notice three text files,
web.py contains a hello-world web application written in
Python3 using the
from flask import Flask from markupsafe import escape app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' @app.route('/hello/<username>') def hello_user(username): # say hello to that user return 'Hello %s' % escape(username)
requirements.txt clarifies which libraries your application is dependent on, and their versions. Pip can generate the contents of this file for you using the command
click==7.1.2 Flask==1.1.2 itsdangerous==1.1.0 Jinja2==2.11.2 MarkupSafe==1.1.1 Werkzeug==1.0.1
Procfile specifies the command-line used to execute the application at runtime. In this case the Procfile declares that the
web.py file contains your
flask run, and binds the web-server to the
--host with the IP address
0.0.0.0. It is expected that the
--port to bind the application to will be set as an environment variable using the name
web: FLASK_APP=web.py python3 -m flask run --host=0.0.0.0 --port=$PORT
Get The List Of Suggested Builders
There are many implementations of the Buildpacks standard. These implementations are called ‘builders’. To discover the very latest list of suggested builders, use the
pack suggest-builders command as follows:
> pack suggest-builders Suggested builders: Google: gcr.io/buildpacks/builder:v1 Ubuntu 18 base image with buildpacks for .NET, Go, Java, Node.js, and Python Heroku: heroku/buildpacks:18 heroku-18 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP Paketo Buildpacks: gcr.io/paketo-buildpacks/builder:full-cf cflinuxfs3 base image with buildpacks for Java, .NET, NodeJS, Golang, PHP, HTTPD and NGINX ...
Set Heroku As Your Default Builder
Copy the name of the builder that you want to set as your default from the list above — in this case it’s
heroku/buildpacks:18 — and use the
pack set-default-builder command to set this buildpack as the default as shown below:
> pack set-default-builder heroku/buildpacks:18 Builder heroku/buildpacks:18 is now the default builder
pack To Create Your Container Image
To run the builder and create your Python application container image, use the command
pack build. Be sure to also specify an image name for the container in the format “<repository>/<container-name>:<tag>” as shown in the following example:
> pack build benwilcock/python-sample:1.0.0
The process of building the image will now begin. The first time you do this, you will notice that docker is downloading a series of container image ‘layers.’ This is because buildpacks are also containers, so they must first be pulled by Docker before the buildpack can be run locally. Once these images are in your cache, the process is much quicker. The output looks something like this:
18: Pulling from heroku/buildpacks 4e20becbd46f: Pull complete 3c742a4a0f38: Already exists ab0f59294661: Downloading [=============================> ] 16.05MB/26.96MB Digest: sha256:296e4f3394e3147a61bd8b08d3c46c0dfa2bf2d4266ed598241cf2419dc96fa3 Status: Image is up to date for heroku/buildpacks:18 18: Pulling from heroku/pack Digest: sha256:219a7621db58790ace66a87d33a200cd89aeda03192994e11a05967fbf8892f6 Status: Image is up to date for heroku/pack:18 ===> DETECTING heroku/python 0.2 heroku/procfile 0.5 ===> ANALYZING Restoring metadata for "heroku/python:shim" from cache ===> RESTORING Restoring data for "heroku/python:shim" from cache ===> BUILDING -----> No change in requirements detected, installing from cache -----> Installing SQLite3 -----> Installing requirements with pip -----> Discovering process types Procfile declares types -> web ===> EXPORTING Reusing layer 'launcher' Reusing layer 'heroku/python:profile' Adding 1/1 app layer(s) Reusing layer 'config' *** Images (a904788f7748): index.docker.io/benwilcock/python-sample:1.0.0 Adding cache layer 'heroku/python:shim' Successfully built image benwilcock/python-sample:1.0.0
When you see the words “Successfully built image” the process is complete. Your new container image will now be available in your local Docker image repository. You can list the images in your local repository with the command
> docker images REPOSITORY TAG IMAGE ID CREATED SIZE benwilcock/python-sample 1.0.0 59843a212207 40 years ago 651MB
Already we’re benefiting from buildpack engineering! Notice that the
CREATEDdate says “40 years ago”. This is a best practice whereby the timestamps of layers are ‘zeroed’ to make container builds more reproducible, cache-able, and to avoid unnecessary image downloads. You can read more here.
Test The Container Image
Testing the container is no more difficult than running the image with the
docker run command as follows:
> docker run -d -ePORT=8080 -p8080:8080 --name python-sample benwilcock/python-sample:1.0.0
Now the container image of your application is running in the background, simply query the
http://localhost:8080 endpoint, either using a command-line tool like Httpie as shown below, or a regular web browser.
> http localhost:8080/
Your application will respond with the legend “Hello, World!” like so:
HTTP/1.0 200 OK Content-Length: 13 Content-Type: text/html; charset=utf-8 Date: Tue, 21 Jul 2020 10:27:47 GMT Server: Werkzeug/1.0.1 Python/3.6.11 Hello, World!
And you’re done! You built your sample Python application into an OCI compliant Docker container image without resorting to a
Dockerfile. Heroku will take care of most of the underlying maintenance tasks on the operating system and the Python interpreter. All you need to do is re-run
pack build and you’ll get a fresh image. If nothing changed, neither will your image.
You can stop and remove the container from Docker as follows:
> docker stop python-sample > docker rm python-sample
Learn more about buildpacks right here on the Tanzu Developer Center with these great guides:
Find out more about what the
pack tool can do by using the
--help command and browsing the Buildpacks.io website.
If you liked this guide, you might find these others in our ‘Python Like A Pro’ series useful:
- Install Python Like A Pro!
- Managing Python Project Dependencies Like A Pro!
- Managing Python Global Packages Like A Pro
- Run Python In Production Like A Pro!
And if you’d prefer to see the buildpack in action without actually following the steps above, check out the accompanying YouTube video guide: