Skip to main content

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:

dockerfile
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

bash
# 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:

bash
# 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:

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
tip

Make sure you add a /health endpoint to your FastAPI app for the liveness and readiness probes:

python
@app.get("/health")
def health_check():
return {"status": "ok"}

2. Create a Service Configuration

Create a file named fastapi-service.yaml:

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

bash
# Apply the deployment
kubectl apply -f fastapi-deployment.yaml

# Apply the service
kubectl apply -f fastapi-service.yaml

4. Verify the Deployment

bash
# 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:

yaml
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:

bash
kubectl apply -f fastapi-service.yaml

First, create an Ingress controller if you don't have one. Then create a file named fastapi-ingress.yaml:

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:

bash
kubectl apply -f fastapi-ingress.yaml

Configuring Your FastAPI Application

Using ConfigMaps for Application Configuration

Create a file named fastapi-configmap.yaml:

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:

bash
kubectl apply -f fastapi-configmap.yaml

Update your deployment to use the ConfigMap:

yaml
# 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:

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:

bash
kubectl apply -f fastapi-secrets.yaml

Update your deployment to use the Secret:

yaml
# 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

bash
kubectl scale deployment fastapi-app --replicas=5

Autoscaling

Create a file named fastapi-hpa.yaml:

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:

bash
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

yaml
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

yaml
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

yaml
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

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

5. Deploy FastAPI Application

yaml
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

yaml
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

bash
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:

yaml
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:

python
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

bash
# 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

bash
# 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

bash
# 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

  1. Health Checks: Always implement health check endpoints for Kubernetes probes
  2. Resource Limits: Set appropriate CPU and memory limits
  3. Configuration Management: Use ConfigMaps and Secrets for configuration
  4. Graceful Shutdown: Handle termination signals properly in your FastAPI application
  5. Statelessness: Design your application to be stateless for easier scaling
  6. 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:

  1. Build and push a new Docker image with a new tag:
bash
docker build -t your-registry.com/fastapi-app:v2 .
docker push your-registry.com/fastapi-app:v2
  1. Update the image in your deployment:
bash
kubectl set image deployment/fastapi-app fastapi=your-registry.com/fastapi-app:v2

Or edit your deployment manifest and apply:

bash
kubectl apply -f fastapi-deployment.yaml
  1. Monitor the rollout:
bash
kubectl rollout status deployment/fastapi-app
  1. If issues occur, rollback:
bash
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

Exercises

  1. Modify the FastAPI deployment to include environment-specific configurations (dev, test, prod)
  2. Implement database migrations as Kubernetes Jobs that run before the FastAPI application starts
  3. Set up a CI/CD pipeline to automatically build and deploy your FastAPI application to Kubernetes
  4. Implement a blue-green deployment strategy for zero-downtime updates
  5. 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! :)