Add FastAPI example for python docs (#20396)

* Add python docker example based on fastapi app

* Add gitignore example in python docs

* ✏️ Fix port mapping in docker init section

* ♻️ Edit refactor to map container ports from 5000-5001 to 8000-8001

* ✏️ Fix some grammatical structures

* 📝 Add some notes about kubernetes stuff
This commit is contained in:
Esteban Maya 2024-07-24 19:04:36 -05:00 committed by GitHub
parent 6488f1465e
commit f092419ca0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 444 additions and 86 deletions

View File

@ -18,12 +18,12 @@ This section walks you through containerizing and running a Python application.
## Get the sample application
The sample application uses the popular [Flask](https://flask.palletsprojects.com/) framework.
The sample application uses the popular [FastAPI](https://fastapi.tiangolo.com) framework.
Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository:
```console
$ git clone https://github.com/docker/python-docker
$ git clone https://github.com/estebanx64/python-docker-example
```
## Initialize Docker assets
@ -35,9 +35,9 @@ feature to help streamline the process, or you can manually create the assets.
{{< tabs >}}
{{< tab name="Use Docker Init" >}}
Inside the `python-docker` directory, run the `docker init` command. `docker
Inside the `python-docker-example` directory, run the `docker init` command. `docker
init` provides some default configuration, but you'll need to answer a few
questions about your application. For example, this application uses Flask to
questions about your application. For example, this application uses FastAPI to
run. Refer to the following example to answer the prompts from `docker init` and
use the same answers for your prompts.
@ -56,7 +56,66 @@ Let's get started!
? What application platform does your project use? Python
? What version of Python do you want to use? 3.11.4
? What port do you want your app to listen on? 8000
? What is the command to run your app? python3 -m flask run --host=0.0.0.0 --port=8000
? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8000
```
Create a file named `.gitignore` with the following contents.
```text {collapse=true,title=".gitignore"}
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
```
{{< /tab >}}
@ -118,7 +177,7 @@ COPY . .
EXPOSE 8000
# Run the application.
CMD python3 -m flask run --host=0.0.0.0 --port=8000
CMD python3 -m uvicorn app:app --host=0.0.0.0 --port=8000
```
Create a file named `compose.yaml` with the following contents.
@ -139,39 +198,6 @@ services:
context: .
ports:
- 8000:8000
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
# db:
# condition: service_healthy
# db:
# image: postgres
# restart: always
# user: postgres
# secrets:
# - db-password
# volumes:
# - db-data:/var/lib/postgresql/data
# environment:
# - POSTGRES_DB=example
# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
# expose:
# - 5432
# healthcheck:
# test: [ "CMD", "pg_isready" ]
# interval: 10s
# timeout: 5s
# retries: 5
# volumes:
# db-data:
# secrets:
# db-password:
# file: db/password.txt
```
Create a file named `.dockerignore` with the following contents.
@ -212,17 +238,77 @@ Create a file named `.dockerignore` with the following contents.
LICENSE
README.md
```
Create a file named `.gitignore` with the following contents.
```text {collapse=true,title=".gitignore"}
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
```
{{< /tab >}}
{{< /tabs >}}
You should now have the following contents in your `python-docker`
You should now have the following contents in your `python-docker-example`
directory.
```text
├── python-docker/
├── python-docker-example/
│ ├── app.py
│ ├── requirements.txt
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ └── README.md
@ -231,25 +317,26 @@ directory.
To learn more about the files, see the following:
- [Dockerfile](../../reference/dockerfile.md)
- [.dockerignore](../../reference/dockerfile.md#dockerignore-file)
- [.gitignore](https://git-scm.com/docs/gitignore)
- [compose.yaml](../../compose/compose-file/_index.md)
## Run the application
Inside the `python-docker` directory, run the following command in a
Inside the `python-docker-example` directory, run the following command in a
terminal.
```console
$ docker compose up --build
```
Open a browser and view the application at [http://localhost:8000](http://localhost:8000). You should see a simple Flask application.
Open a browser and view the application at [http://localhost:8000](http://localhost:8000). You should see a simple FastAPI application.
In the terminal, press `ctrl`+`c` to stop the application.
### Run the application in the background
You can run the application detached from the terminal by adding the `-d`
option. Inside the `python-docker` directory, run the following command
option. Inside the `python-docker-example` directory, run the following command
in a terminal.
```console
@ -258,7 +345,9 @@ $ docker compose up --build -d
Open a browser and view the application at [http://localhost:8000](http://localhost:8000).
You should see a simple Flask application.
To see the OpenAPI docs you can go to [http://localhost:8000/docs](http://localhost:8000/docs).
You should see a simple FastAPI application.
In the terminal, run the following command to stop the application.

View File

@ -6,7 +6,7 @@ description: Learn how to develop locally using Kubernetes
## Prerequisites
- Complete all the previous sections of this guide, starting with [Containerize a Python application](containerize.md).
- Complete all the previous sections of this guide, starting with [Use containers for python development](develop.md).
- [Turn on Kubernetes](/desktop/kubernetes/#install-and-turn-on-kubernetes) in Docker Desktop.
## Overview
@ -15,12 +15,85 @@ In this section, you'll learn how to use Docker Desktop to deploy your applicati
## 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
In your `python-docker-dev-example` directory, create a file named `docker-postgres-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 that you created in [Configure CI/CD for
your Python application](configure-ci-cd.md).
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: example
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_PASSWORD
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: default
spec:
ports:
- port: 5432
selector:
app: postgres
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
namespace: default
type: Opaque
data:
POSTGRES_PASSWORD: cG9zdGdyZXNfcGFzc3dvcmQ= # Base64 encoded password (e.g., 'postgres_password')
```
In your `python-docker-dev-example` directory, create a file named `docker-python-kubernetes.yaml`.
```yaml
apiVersion: apps/v1
kind: Deployment
@ -31,19 +104,32 @@ spec:
replicas: 1
selector:
matchLabels:
service: flask
service: fastapi
template:
metadata:
labels:
service: flask
service: fastapi
spec:
containers:
- name: flask-service
image: DOCKER_USERNAME/REPO_NAME
imagePullPolicy: Always
env:
- name: POSTGRES_PASSWORD
value: mysecretpassword
- name: fastapi-service
image: technox64/python-docker-dev-example-test:latest
imagePullPolicy: Always
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_PASSWORD
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_DB
value: example
- name: POSTGRES_SERVER
value: postgres
- name: POSTGRES_PORT
value: "5432"
ports:
- containerPort: 8001
---
apiVersion: v1
kind: Service
@ -53,38 +139,60 @@ metadata:
spec:
type: NodePort
selector:
service: flask
service: fastapi
ports:
- port: 8001
targetPort: 8001
nodePort: 30001
```
In this Kubernetes YAML file, there are two objects, separated by the `---`:
In these Kubernetes YAML file, there are various objects, separated by the `---`:
- A Deployment, describing a scalable group of identical pods. In this case,
you'll get just one replica, or copy of your pod. That pod, which is
described under `template`, has just one container in it. The
container is created from the image built by GitHub Actions in [Configure CI/CD for
your Python application](configure-ci-cd.md).
- A Service, which will define how the ports are mapped in the containers.
- A PersistentVolumeClaim, to define a storage that will be persistent through restarts for the database.
- A Secret, Keeping the database password as a example using secret kubernetes resource.
- A NodePort service, which will route traffic from port 30001 on your host to
port 8001 inside the pods it routes to, allowing you to reach your app
from the network.
To learn more about Kubernetes objects, see the [Kubernetes documentation](https://kubernetes.io/docs/home/).
> **Note**
>
> * The `NodePort` service is good for development/testing purposes. For production you should implement an [ingress-controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/).
## Deploy and check your application
1. In a terminal, navigate to `python-docker-dev` and deploy your application to
1. In a terminal, navigate to `python-docker-dev-example` and deploy your database to
Kubernetes.
```console
$ kubectl apply -f docker-python-kubernetes.yaml
$ kubectl apply -f docker-postgres-kubernetes.yaml
```
You should see output that looks like the following, indicating your Kubernetes objects were created successfully.
```shell
```console
deployment.apps/postgres created
service/postgres created
persistentvolumeclaim/postgres-pvc created
secret/postgres-secret created
```
Now, deploy your python application.
```console
kubectl apply -f docker-python-kubernetes.yaml
```
You should see output that looks like the following, indicating your Kubernetes objects were created successfully.
```console
deployment.apps/docker-python-demo created
service/service-entrypoint created
```
@ -97,9 +205,10 @@ To learn more about Kubernetes objects, see the [Kubernetes documentation](https
Your deployment should be listed as follows:
```shell
```console
NAME READY UP-TO-DATE AVAILABLE AGE
docker-python-demo 1/1 1 1 15s
docker-python-demo 1/1 1 1 48s
postgres 1/1 1 1 2m39s
```
This indicates all one of the pods you asked for in your YAML are up and running. Do the same check for your services.
@ -110,13 +219,14 @@ To learn more about Kubernetes objects, see the [Kubernetes documentation](https
You should get output like the following.
```shell
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> 8001:30001/TCP 75s
```console
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 13h
postgres ClusterIP 10.43.209.25 <none> 5432/TCP 3m10s
service-entrypoint NodePort 10.43.67.120 <none> 8001:30001/TCP 79s
```
In addition to the default `kubernetes` service, you can see your `service-entrypoint` service, accepting traffic on port 30001/TCP.
In addition to the default `kubernetes` service, you can see your `service-entrypoint` service, accepting traffic on port 30001/TCP and the internal `ClusterIP` `postgres` with the port `5432` open to accept connections from you python app.
3. In a terminal, curl the service. Note that a database was not deployed in
this example.
@ -126,10 +236,11 @@ To learn more about Kubernetes objects, see the [Kubernetes documentation](https
Hello, Docker!!!
```
4. Run the following command to tear down your application.
4. Run the following commands to tear down your application.
```console
$ kubectl delete -f docker-python-kubernetes.yaml
$ kubectl delete -f docker-postgres-kubernetes.yaml
```
## Summary

View File

@ -22,7 +22,7 @@ You'll need to clone a new repository to get a sample application that includes
1. Change to a directory where you want to clone the repository and run the following command.
```console
$ git clone https://github.com/docker/python-docker-dev
$ git clone https://github.com/estebanx64/python-docker-dev-example
```
2. In the cloned repository's directory, manually create the Docker assets or run `docker init` to create the necessary Docker assets.
@ -36,27 +36,86 @@ You'll need to clone a new repository to get a sample application that includes
```console
$ docker init
Welcome to the Docker Init CLI!
This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
- README.Docker.md
Let's get started!
? What application platform does your project use? Python
? What version of Python do you want to use? 3.11.4
? What port do you want your app to listen on? 8001
? What is the command to run your app? python3 -m flask run --host=0.0.0.0 --port=8001
? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8001
```
Create a file named `.gitignore` with the following contents.
```text {collapse=true,title=".gitignore"}
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
```
{{< /tab >}}
{{< tab name="Manually create assets" >}}
If you don't have Docker Desktop installed or prefer creating the assets
manually, you can create the following files in your project directory.
Create a file named `Dockerfile` with the following contents.
```dockerfile {collapse=true,title=Dockerfile}
@ -110,7 +169,7 @@ You'll need to clone a new repository to get a sample application that includes
EXPOSE 8001
# Run the application.
CMD python3 -m flask run --host=0.0.0.0 --port=8001
CMD python3 -m uvicorn app:app --host=0.0.0.0 --port=8001
```
Create a file named `compose.yaml` with the following contents.
@ -204,6 +263,65 @@ You'll need to clone a new repository to get a sample application that includes
LICENSE
README.md
```
Create a file named `.gitignore` with the following contents.
```text {collapse=true,title=".gitignore"}
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
```
{{< /tab >}}
{{< /tabs >}}
@ -217,7 +335,7 @@ In the `compose.yaml` file, you need to uncomment all of the database instructio
The following is the updated `compose.yaml` file.
```yaml {hl_lines="7-36"}
```yaml {hl_lines="7-43"}
services:
server:
build:
@ -225,6 +343,9 @@ services:
ports:
- 8001:8001
environment:
- POSTGRES_SERVER=db
- POSTGRES_USER=postgres
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
depends_on:
db:
@ -271,16 +392,18 @@ mysecretpassword
Save and close the `password.txt` file.
You should now have the following contents in your `python-docker-dev`
You should now have the following contents in your `python-docker-dev-example`
directory.
```text
├── python-docker-dev/
├── python-docker-dev-example/
│ ├── db/
│ │ └── password.txt
│ ├── app.py
│ ├── config.py
│ ├── requirements.txt
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
@ -295,18 +418,50 @@ $ docker compose up --build
Now test your API endpoint. Open a new terminal then make a request to the server using the curl commands:
Let's create an object with a post method
```console
$ curl http://localhost:8001/initdb
$ curl http://localhost:8001/widgets
$ curl -X 'POST' \
'http://0.0.0.0:8001/heroes/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"id": 1,
"name": "my hero",
"secret_name": "austing",
"age": 12
}'
```
You should receive the following response:
```json
[]
{
"age": 12,
"id": 1,
"name": "my hero",
"secret_name": "austing"
}
```
The response is empty because your database is empty.
Let's make a get request with the next curl command:
```console
curl -X 'GET' \
'http://0.0.0.0:8001/heroes/' \
-H 'accept: application/json'
```
You should receive the same response as above because it's the only one object we have in database.
```json
{
"age": 12,
"id": 1,
"name": "my hero",
"secret_name": "austing"
}
```
Press `ctrl+c` in the terminal to stop your application.
@ -319,7 +474,7 @@ Watch](../../compose/file-watch.md).
Open your `compose.yaml` file in an IDE or text editor and then add the Compose
Watch instructions. The following is the updated `compose.yaml` file.
```yaml {hl_lines="14-17"}
```yaml {hl_lines="17-20"}
services:
server:
build:
@ -327,6 +482,9 @@ services:
ports:
- 8001:8001
environment:
- POSTGRES_SERVER=db
- POSTGRES_USER=postgres
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
depends_on:
db:
@ -377,7 +535,7 @@ Hello, Docker!
Any changes to the application's source files on your local machine will now be immediately reflected in the running container.
Open `python-docker-dev/app.py` in an IDE or text editor and update the `Hello, Docker!` string by adding a few more exclamation marks.
Open `python-docker-dev-example/app.py` in an IDE or text editor and update the `Hello, Docker!` string by adding a few more exclamation marks.
```diff
- return 'Hello, Docker!'