FastAPI Kubernetes Deployment
Introduction
Kubernetes has become the industry standard for container orchestration, providing powerful tools to deploy, scale, and manage containerized applications. In this tutorial, we'll explore how to deploy a FastAPI application on Kubernetes, enabling you to create scalable, resilient web services.
By the end of this guide, you'll understand:
- The basics of Kubernetes and its core components
- How to containerize your FastAPI application
- Steps to deploy your application to a Kubernetes cluster
- Strategies for scaling and managing your deployment
Prerequisites
Before we begin, make sure you have:
- Basic knowledge of FastAPI
- Docker installed on your system
- A Kubernetes cluster (Minikube for local development or a cloud-based solution)
- kubectl CLI tool installed and configured
- A FastAPI application ready for deployment
Understanding Kubernetes Concepts
Kubernetes (often abbreviated as K8s) is an open-source platform designed to automate deploying, scaling, and operating application containers. Let's understand some key Kubernetes concepts before we dive into deployment:
- Pod: The smallest deployable unit in Kubernetes, representing one or more containers that share storage and network resources
- Deployment: Manages the creation and updating of pod instances
- Service: An abstraction that defines a logical set of pods and a policy to access them
- ConfigMap/Secret: Resources for storing configuration data and sensitive information
- Ingress: Manages external access to services, typically HTTP
Containerizing Your FastAPI Application
Before deploying to Kubernetes, we need to containerize our FastAPI application using Docker.
1. Create a Dockerfile
Create a Dockerfile
in your project root:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app /app/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
2. Build and Test the Docker Image
# Build the Docker image
docker build -t fastapi-app:latest .
# Run the container locally to test
docker run -p 8000:8000 fastapi-app:latest
If everything works correctly, you should be able to access your FastAPI application at http://localhost:8000.
3. Push Your Image to a Registry
For Kubernetes to access your image, it needs to be in a container registry:
# Tag the image (replace with your registry)
docker tag fastapi-app:latest your-registry.com/fastapi-app:latest
# Push to registry
docker push your-registry.com/fastapi-app:latest
Deploying to Kubernetes
Now, let's deploy our FastAPI application to Kubernetes.
1. Create a Deployment Configuration
Create a file named fastapi-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
labels:
app: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: fastapi
image: your-registry.com/fastapi-app:latest
ports:
- containerPort: 8000
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
Make sure you add a /health
endpoint to your FastAPI app for the liveness and readiness probes:
@app.get("/health")
def health_check():
return {"status": "ok"}
2. Create a Service Configuration
Create a file named fastapi-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
type: ClusterIP
3. Apply the Configurations to Your Cluster
# Apply the deployment
kubectl apply -f fastapi-deployment.yaml
# Apply the service
kubectl apply -f fastapi-service.yaml
4. Verify the Deployment
# Check deployment status
kubectl get deployments
# Check running pods
kubectl get pods
# Check the service
kubectl get services
Example output:
NAME READY STATUS RESTARTS AGE
fastapi-app-7b8d9f8d9-xvz2w 1/1 Running 0 5m
fastapi-app-7b8d9f8d9-kl2zp 1/1 Running 0 5m
fastapi-app-7b8d9f8d9-j3n5x 1/1 Running 0 5m
Exposing Your FastAPI Application
You have multiple options to expose your FastAPI application externally:
Option 1: Using NodePort
Update your service configuration:
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
nodePort: 30080
type: NodePort
Apply the updated configuration:
kubectl apply -f fastapi-service.yaml
Option 2: Using Ingress (Recommended for Production)
First, create an Ingress controller if you don't have one. Then create a file named fastapi-ingress.yaml
:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: fastapi.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
Apply the Ingress configuration:
kubectl apply -f fastapi-ingress.yaml
Configuring Your FastAPI Application
Using ConfigMaps for Application Configuration
Create a file named fastapi-configmap.yaml
:
apiVersion: v1
kind: ConfigMap
metadata:
name: fastapi-config
data:
DATABASE_URL: "postgresql://user:password@postgres-service:5432/fastapi"
API_KEY: "development-api-key"
LOG_LEVEL: "info"
Apply the ConfigMap:
kubectl apply -f fastapi-configmap.yaml
Update your deployment to use the ConfigMap:
# In your deployment.yaml file
containers:
- name: fastapi
image: your-registry.com/fastapi-app:latest
envFrom:
- configMapRef:
name: fastapi-config
Using Secrets for Sensitive Information
Create a file named fastapi-secrets.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: fastapi-secrets
type: Opaque
data:
DATABASE_PASSWORD: cGFzc3dvcmQ= # Base64 encoded "password"
JWT_SECRET: c3VwZXJzZWNyZXRrZXk= # Base64 encoded "supersecretkey"
Apply the Secret:
kubectl apply -f fastapi-secrets.yaml
Update your deployment to use the Secret:
# In your deployment.yaml file
containers:
- name: fastapi
image: your-registry.com/fastapi-app:latest
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: fastapi-secrets
key: DATABASE_PASSWORD
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: fastapi-secrets
key: JWT_SECRET
Scaling Your FastAPI Application
One of the key benefits of Kubernetes is easy horizontal scaling:
Manual Scaling
kubectl scale deployment fastapi-app --replicas=5
Autoscaling
Create a file named fastapi-hpa.yaml
:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fastapi-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fastapi-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Apply the HorizontalPodAutoscaler:
kubectl apply -f fastapi-hpa.yaml
Real-World Example: Complete FastAPI API Service
Let's put everything together to create a more comprehensive example of a FastAPI service deployed on Kubernetes with a PostgreSQL database.
1. Create a ConfigMap for FastAPI Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: fastapi-config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
POSTGRES_HOST: "postgres-service"
POSTGRES_DB: "fastapi_db"
POSTGRES_USER: "fastapi_user"
2. Create Secrets for Sensitive Data
apiVersion: v1
kind: Secret
metadata:
name: fastapi-secrets
type: Opaque
data:
POSTGRES_PASSWORD: cGFzc3dvcmQxMjM= # Base64 encoded "password123"
JWT_SECRET_KEY: c3VwZXJzZWNyZXRrZXkxMjM= # Base64 encoded "supersecretkey123"
3. Deploy PostgreSQL Database
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: fastapi-config
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
configMapKeyRef:
name: fastapi-config
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: fastapi-secrets
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-service
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
4. Create Persistent Volume Claim for PostgreSQL
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
5. Deploy FastAPI Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: fastapi
image: your-registry.com/fastapi-app:latest
ports:
- containerPort: 8000
envFrom:
- configMapRef:
name: fastapi-config
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: fastapi-secrets
key: POSTGRES_PASSWORD
- name: JWT_SECRET_KEY
valueFrom:
secretKeyRef:
name: fastapi-secrets
key: JWT_SECRET_KEY
- name: DATABASE_URL
value: "postgresql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_HOST):5432/$(POSTGRES_DB)"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
6. Create Service and Ingress for FastAPI
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
7. Apply All Resources
kubectl apply -f postgres-pvc.yaml
kubectl apply -f fastapi-configmap.yaml
kubectl apply -f fastapi-secrets.yaml
kubectl apply -f postgres-deployment.yaml
kubectl apply -f fastapi-deployment.yaml
kubectl apply -f fastapi-service-ingress.yaml
Monitoring Your FastAPI Application
To ensure your FastAPI application is running smoothly, add monitoring using Prometheus and Grafana:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
# Other deployment settings...
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics"
Make sure to add metrics to your FastAPI app using the prometheus-fastapi-instrumentator
package:
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
# Setup Prometheus metrics
@app.on_event("startup")
async def startup():
Instrumentator().instrument(app).expose(app)
Troubleshooting Common Issues
1. Pod is in CrashLoopBackOff state
# Check the pod logs
kubectl logs <pod-name>
# Describe the pod for more details
kubectl describe pod <pod-name>
2. Unable to access the service
# Check if the service is running
kubectl get svc
# Ensure endpoints exist for the service
kubectl get endpoints fastapi-service
# Port-forward the service to test locally
kubectl port-forward svc/fastapi-service 8080:80
3. Ingress is not working
# Verify Ingress controller is running
kubectl get pods -n ingress-nginx
# Check Ingress configuration
kubectl describe ingress fastapi-ingress
Best Practices for FastAPI on Kubernetes
- Health Checks: Always implement health check endpoints for Kubernetes probes
- Resource Limits: Set appropriate CPU and memory limits
- Configuration Management: Use ConfigMaps and Secrets for configuration
- Graceful Shutdown: Handle termination signals properly in your FastAPI application
- Statelessness: Design your application to be stateless for easier scaling
- Logging: Configure structured JSON logging for better integration with Kubernetes
Updating Your Deployment
When you make changes to your FastAPI application, follow these steps to update your deployment:
- Build and push a new Docker image with a new tag:
docker build -t your-registry.com/fastapi-app:v2 .
docker push your-registry.com/fastapi-app:v2
- Update the image in your deployment:
kubectl set image deployment/fastapi-app fastapi=your-registry.com/fastapi-app:v2
Or edit your deployment manifest and apply:
kubectl apply -f fastapi-deployment.yaml
- Monitor the rollout:
kubectl rollout status deployment/fastapi-app
- If issues occur, rollback:
kubectl rollout undo deployment/fastapi-app
Summary
In this guide, we've covered how to deploy a FastAPI application on Kubernetes, from containerization to deployment, configuration, scaling, and monitoring. We explored:
- Containerizing FastAPI applications
- Creating Kubernetes deployment files
- Exposing services with Service and Ingress resources
- Configuring applications with ConfigMaps and Secrets
- Scaling applications manually and automatically
- Setting up monitoring
- Implementing a complete real-world example
Kubernetes provides a robust platform for deploying FastAPI applications, enabling you to build scalable, resilient web services that can handle production workloads.
Additional Resources
- Kubernetes Official Documentation
- FastAPI Documentation
- Kubernetes Patterns Book
- Prometheus FastAPI Instrumentator
Exercises
- Modify the FastAPI deployment to include environment-specific configurations (dev, test, prod)
- Implement database migrations as Kubernetes Jobs that run before the FastAPI application starts
- Set up a CI/CD pipeline to automatically build and deploy your FastAPI application to Kubernetes
- Implement a blue-green deployment strategy for zero-downtime updates
- Add autoscaling based on custom metrics from your FastAPI application
Happy deploying! Kubernetes can seem complex at first, but it provides powerful tools to manage your FastAPI applications at scale. Start small, and gradually incorporate more Kubernetes features as you become comfortable with the platform.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)