Containerize a Python application
Prerequisites
- Ensure you have the latest Docker Desktop installed.
- Have a git client installed (command-line based in the examples).
Overview
This guide will help you containerize and run a Python application using Docker.
Step 1: Get the Sample Application
The example uses the Flask framework. To start, clone the sample application repository using the following command in your terminal:
git clone https://github.com/docker/python-docker
Step 2: Initialize Docker Assets
Now that you have the application, use the docker init
command inside the python-docker
directory. Follow the prompts and provide the necessary information:
cd python-docker
docker init
Answer the prompts like this:
- Application platform: Python
- Python version: 3.11.4
- Port for your app to listen on: 5000
- Command to run your app:
python3 -m flask run --host=0.0.0.0
After this, your directory should contain the following files: python-docker/ ├── app.py ├── requirements.txt ├── .dockerignore ├── compose.yaml ├── Dockerfile ├── README.Docker.md └── README.md
Learn more about these files by exploring the Docker-related files:
Dockerfile
.dockerignore
compose.yaml
Step 3: Run the Application
In the python-docker
directory, run the following command in the terminal:
docker compose up --build
Open a browser and view the application at http://localhost:5000. You should see a simple Flask application. To stop the application, press ctrl+c
in the terminal.
Step 4: Run the Application in the Background
To run the application detached from the terminal, add the -d
option:
docker compose up --build -d
Open a browser and view the application at http://localhost:5000. To stop the application, run the following command:
docker compose down
Explore more Compose commands in the Compose CLI reference.
By following these steps, you have successfully containerized and run a Python application using Docker.
Using Containers for Python Development
Prerequisites
Make sure you've completed the "Containerize a Python Application" guide.
Overview
In this section, we'll delve into setting up a development environment for your containerized application. This involves:
- Adding a local database and persisting data.
- Configuring Compose to automatically update running services as you edit and save your code.
Step 1: Get the Sample Application
Clone a new repository to obtain a sample application with database connectivity logic:
git clone https://github.com/docker/python-docker-dev
In the cloned repository's directory, run docker init
to generate necessary Docker files:
cd python-docker-dev
docker init
Follow the prompts:
- Application platform: Python
- Python version: 3.11.4
- Port for your app to listen on: 5000
- Command to run your app:
python3 -m flask run --host=0.0.0.0
Step 2: Add a Local Database and Persist Data
Utilize containers to set up local services, such as a database. Update the compose.yaml
file to define a database service and a volume for data persistence.
Open the compose.yaml
file in an IDE or text editor. Uncomment all the database instructions and add the database password as an environment variable to the server service.
# Updated compose.yaml file
services:
server:
# ... (existing configurations)
environment:
- POSTGRES_PASSWORD=mysecretpassword
# ... (existing configurations)
db:
image: postgres
restart: always
user: postgres
# ... (existing configurations)
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
# ... (existing configurations)
# ... (volumes and secrets configurations)
Create a password.txt
file in the db
directory with the database password:
mkdir db
echo "mysecretpassword" > db/password.txt
Step 3: Run the Application
Execute the following command to start your application:
docker compose up --build
Test your API endpoint:
curl http://localhost:5000/initdb
curl http://localhost:5000/widgets
You should receive an empty response as the database is currently empty. Stop the application with ctrl+c
.
Step 4: Automatically Update Services
Use Compose Watch to auto-update running services as you edit and save your code. Open your compose.yaml
file and add the Compose Watch instructions:
# Updated compose.yaml file with Compose Watch
services:
server:
# ... (existing configurations)
develop:
watch:
- action: rebuild
path: .
db:
# ... (existing configurations)
# ... (volumes and secrets configurations)
Run the following command to utilize Compose Watch:
docker compose watch
In a terminal, curl the application:
curl http://localhost:5000
Changes to source files are immediately reflected in the running container. Open python-docker-dev/app.py
and update the text. For example, change:
return 'Hello, Docker!'
to:
return 'Hello, Docker!!!'
Save the changes and wait for the application to rebuild. Curl the application again to see the updated text:
curl http://localhost:5000
You should now see "Hello, Docker!!!" in the response. Press ctrl+c to stop your application.
Configuring CI/CD for Your Python Application
Prerequisites
Ensure you have completed all the previous sections of this guide, starting with "Containerize a Python Application." You must also have a GitHub account and a Docker account to proceed.
Overview
In this section, you'll learn how to set up and use GitHub Actions to build, test, and push your Docker image to Docker Hub. The following steps will be covered:
- Create a new repository on GitHub.
- Define the GitHub Actions workflow.
- Run the workflow.
Step One: Create the Repository
1. Create a new repository on GitHub:
- Open GitHub and create a new repository.
2. Configure Docker Hub secrets:
- Open the repository Settings and navigate to Secrets and variables > Actions.
- Create a new secret named
DOCKER_USERNAME
with your Docker ID as the value. - Create a new Personal Access Token (PAT) for Docker Hub (e.g.,
python-docker
) and add it as a secret namedDOCKERHUB_TOKEN
in your GitHub repository.
3. Push your source code:
- In your local repository, change the origin to the newly created repository:
git remote set-url origin https://github.com/your-username/your-repository.git
- Stage, commit, and push your local repository to GitHub:
git add -A git commit -m "my commit" git push -u origin main
Step Two: Set Up the Workflow
1. Set up GitHub Actions workflow:
- Go to your repository on GitHub and select the Actions tab.
- Choose "set up a workflow yourself" to create a new GitHub Actions workflow file (usually under .github/workflows/main.yml).
2. Copy and paste the following YAML configuration into the editor:
name: ci
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest
For more information about the YAML syntax used here, see Workflow syntax for GitHub Actions.
Step Three: Run the Workflow
1. Save the workflow file and run the job:
- Commit changes and push them to the main branch.
- After pushing the commit, the workflow starts automatically.
2. Check the progress:
- Go to the Actions tab on GitHub to view the running workflow.
- Once the workflow is complete, check your repositories on Docker Hub.
3. Verify the image push:
- If you see the new repository on Docker Hub, it indicates that GitHub Actions successfully pushed the image to Docker Hub.
Testing Your Python Deployment
Prerequisites
Make sure you've completed all the previous sections of this guide, starting with "Containerize a Python Application." Additionally, ensure that Kubernetes is turned on in Docker Desktop.
Overview
In this section, you'll use Docker Desktop to deploy your application to a fully-featured Kubernetes environment on your development machine. This enables you to test and debug your workloads on Kubernetes locally before deploying them.
Create a Kubernetes YAML File
In your python-docker-dev
directory, create a file named docker-python-kubernetes.yaml
. Open the file in an IDE or text editor and add the following contents. Replace DOCKER_USERNAME/REPO_NAME
with your Docker username and the name of the repository created in "Configure CI/CD for your Python application."
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-python-demo
namespace: default
spec:
replicas: 1
selector:
matchLabels:
service: flask
template:
metadata:
labels:
service: flask
spec:
containers:
- name: flask-service
image: DOCKER_USERNAME/REPO_NAME
imagePullPolicy: Always
env:
- name: POSTGRES_PASSWORD
value: mysecretpassword
---
apiVersion: v1
kind: Service
metadata:
name: service-entrypoint
namespace: default
spec:
type: NodePort
selector:
service: flask
ports:
- port: 5000
targetPort: 5000
nodePort: 30001
In this Kubernetes YAML file, there are two objects, separated by ---
:
-
A Deployment, describing a scalable group of identical pods. In this case, there's just one replica, or copy of your pod. The pod is created from the image built by GitHub Actions in "Configure CI/CD for your Python application."
-
A NodePort service, routing traffic from port 30001 on your host to port 5000 inside the pods it routes to, allowing you to reach your app from the network.
To learn more about Kubernetes objects, refer to the Kubernetes documentation.
Deploy and Check Your Application
In a terminal, navigate to python-docker-dev
and deploy your application to Kubernetes.
kubectl apply -f docker-python-kubernetes.yaml
You should see output indicating that your Kubernetes objects were created successfully.
deployment.apps/docker-python-demo created
service/service-entrypoint created
Ensure everything worked by listing your deployments.
kubectl get deployments
Your deployment should be listed as follows:
NAME READY UP-TO-DATE AVAILABLE AGE
docker-python-demo 1/1 1 1 15s
This indicates that one of the pods you specified in your YAML is up and running. Perform the same check for your services.
kubectl get services
You should get output like the following.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h
service-entrypoint NodePort 10.99.128.230 <none> 5000:30001/TCP 75s
In addition to the default Kubernetes service, you can see your service-entrypoint
service, accepting traffic on port 30001/TCP
.
In a terminal, curl the service. Note that a database was not deployed in this example.
curl http://localhost:30001/
You should receive the following response:
Hello, Docker!!!
Run the following command to tear down your application.
kubectl delete -f docker-python-kubernetes.yaml