Deploying Python Applications to Kubernetes
This guide explains how to deploy Python applications to Kubernetes clusters, from basic deployments to advanced configurations.
1. Introduction to Kubernetes for Python Applications
Kubernetes is a powerful container orchestration system that automates deployment, scaling, and management of containerized applications. For Python applications, Kubernetes provides:
- Scalability: Easily scale your application based on demand
- High Availability: Ensure your application stays running
- Resource Efficiency: Optimize resource usage
- Rolling Updates: Deploy new versions with zero downtime
- Self-healing: Automatically replace failed containers
2. Prerequisites
- A containerized Python application (see the containerization guide)
- kubectl installed and configured
- Access to a Kubernetes cluster (cloud provider, self-hosted, or local like Minikube/KinD)
3. Basic Deployment
Deployment Manifest
Create a file named deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app
labels:
app: python-app
spec:
replicas: 3
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: python-app
image: your-registry/python-app:v1
ports:
- containerPort: 5000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"Service Manifest
Create a file named service.yaml:
apiVersion: v1
kind: Service
metadata:
name: python-app
spec:
selector:
app: python-app
ports:
- port: 80
targetPort: 5000
type: ClusterIPApplying the Manifests
# Apply the deployment
kubectl apply -f deployment.yaml
# Apply the service
kubectl apply -f service.yaml
# Verify deployment
kubectl get deployments
# Verify service
kubectl get services
# Check pods
kubectl get pods4. Configuration Management
ConfigMaps
Create a file named configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: python-app-config
data:
config.json: |
{
"database_host": "db.example.com",
"log_level": "INFO",
"debug": false
}
app_settings.py: |
DEBUG = False
LOG_LEVEL = "INFO"
DATABASE_HOST = "db.example.com"Apply and use the ConfigMap:
kubectl apply -f configmap.yamlUpdate your deployment to use the ConfigMap:
spec:
containers:
- name: python-app
# ...
volumeMounts:
- name: config
mountPath: /app/config
env:
- name: CONFIG_PATH
value: "/app/config/config.json"
volumes:
- name: config
configMap:
name: python-app-configSecrets
Create a file named secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: python-app-secrets
type: Opaque
data:
db_password: cGFzc3dvcmQxMjM= # base64 encoded 'password123'
api_key: dGhpc2lzc2VjcmV0 # base64 encoded 'thisissecret'Apply and use the Secret:
kubectl apply -f secret.yamlUpdate your deployment to use the Secret:
spec:
containers:
- name: python-app
# ...
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: python-app-secrets
key: db_password
- name: API_KEY
valueFrom:
secretKeyRef:
name: python-app-secrets
key: api_key5. Exposing Your Application
ClusterIP (Internal Access)
This is the default service type, which makes your application accessible only within the cluster.
NodePort (External Access via Node IP)
apiVersion: v1
kind: Service
metadata:
name: python-app-nodeport
spec:
selector:
app: python-app
ports:
- port: 80
targetPort: 5000
nodePort: 30080
type: NodePortAccess via http://<node-ip>:30080
LoadBalancer (Cloud Provider Load Balancer)
apiVersion: v1
kind: Service
metadata:
name: python-app-lb
spec:
selector:
app: python-app
ports:
- port: 80
targetPort: 5000
type: LoadBalancerIngress (Path-Based Routing)
First, ensure you have an Ingress controller installed (like NGINX Ingress).
Create a file named ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: python-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: app.example.com
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: python-app
port:
number: 80
tls:
- hosts:
- app.example.com
secretName: app-tls-secretCreate TLS secret:
kubectl create secret tls app-tls-secret --cert=path/to/cert --key=path/to/keyApply the Ingress:
kubectl apply -f ingress.yaml6. Persistent Storage
Creating a PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: python-app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 1GiUsing the PVC in your deployment:
spec:
containers:
- name: python-app
# ...
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: python-app-data7. Health Checks and Probes
Add health checks to your deployment:
spec:
containers:
- name: python-app
# ...
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 5000
initialDelaySeconds: 5
periodSeconds: 108. Scaling Your Application
Manual Scaling
# Scale to 5 replicas
kubectl scale deployment python-app --replicas=5Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: python-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: python-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Apply the HPA:
kubectl apply -f hpa.yaml9. Deployment Strategies
Rolling Updates (Default Strategy)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%Blue-Green Deployments
- Deploy a new version with different labels:
metadata:
name: python-app-green
spec:
selector:
matchLabels:
app: python-app-green
template:
metadata:
labels:
app: python-app-green- Test the new version, then update the service selector:
spec:
selector:
app: python-app-greenCanary Deployments
- Deploy a small number of new version pods:
metadata:
name: python-app-v2
spec:
replicas: 1 # Small subset of traffic
template:
metadata:
labels:
app: python-app
version: v2- Both versions receive traffic as they share the same
applabel. - Gradually increase replicas of the new version and decrease the old one.
10. Jobs and CronJobs
One-time Job
apiVersion: batch/v1
kind: Job
metadata:
name: python-data-migration
spec:
template:
spec:
containers:
- name: migration
image: your-registry/python-app:v1
command: ["python", "manage.py", "migrate"]
restartPolicy: OnFailure
backoffLimit: 4Scheduled CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: python-backup
spec:
schedule: "0 2 * * *" # 2:00 AM every day
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: your-registry/python-app:v1
command: ["python", "manage.py", "db_backup"]
restartPolicy: OnFailure11. Monitoring and Logging
Prometheus Integration
Add annotations to your service:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
prometheus.io/port: "5000"Ensure your Python app exposes metrics using a library like prometheus_client.
Logging Configuration
Consider using a logging sidecar container:
spec:
containers:
- name: python-app
# ...
volumeMounts:
- name: logs
mountPath: /app/logs
- name: log-collector
image: fluent/fluentd:latest
volumeMounts:
- name: logs
mountPath: /logs
- name: fluentd-config
mountPath: /etc/fluentd
volumes:
- name: logs
emptyDir: {}
- name: fluentd-config
configMap:
name: fluentd-config12. Advanced Patterns
Sidecars
spec:
containers:
- name: python-app
# ...
- name: nginx-sidecar
image: nginx:1.19
ports:
- containerPort: 80Init Containers
spec:
initContainers:
- name: init-db-check
image: busybox:1.32
command:
[
"sh",
"-c",
"until nc -z db-service 5432; do echo waiting for db; sleep 2; done;",
]
containers:
- name: python-app
# ...Pod Disruption Budget (PDB)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: python-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: python-app13. Stateful Applications
StatefulSets for Ordered Pod Deployment
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: python-stateful-app
spec:
serviceName: "python-stateful"
replicas: 3
selector:
matchLabels:
app: python-stateful
template:
metadata:
labels:
app: python-stateful
spec:
containers:
- name: python-app
image: your-registry/python-stateful-app:v1
volumeMounts:
- name: data
mountPath: /app/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 1Gi14. Multi-environment Deployment
Using Namespaces
# Create namespaces
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace production
# Deploy to specific namespace
kubectl apply -f deployment.yaml -n devUsing Kustomize for Environment Overlays
- Create base configuration:
my-app/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── production/
└── kustomization.yaml- Base kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml- Production overlay kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
patchesStrategicMerge:
- deployment-patch.yaml- Apply the kustomization:
kubectl apply -k overlays/production15. Security Best Practices
Pod Security Context
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: python-app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALLNetwork Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: python-app-network-policy
spec:
podSelector:
matchLabels:
app: python-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 5000
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432RBAC (Role-Based Access Control)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: ServiceAccount
name: my-app-service-account
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io16. Troubleshooting
Common Issues and Solutions
Pod in CrashLoopBackOff:
- Check logs:
kubectl logs <pod-name> - Check container startup:
kubectl describe pod <pod-name>
- Check logs:
Image Pull Errors:
- Verify image name and tag
- Check container registry credentials:
kubectl create secret docker-registry regcred --docker-username=<username> --docker-password=<password>
Pod Stuck in Pending:
- Check cluster resources:
kubectl describe node - Check PVC availability:
kubectl get pvc
- Check cluster resources:
Service Not Accessible:
- Verify service endpoints:
kubectl get endpoints <service-name> - Check pod labels match service selector
- Verify pod health with readiness probe
- Verify service endpoints:
Debugging Commands
# Get detailed info about a pod
kubectl describe pod <pod-name>
# View container logs
kubectl logs <pod-name> -c <container-name>
# Connect to a running container
kubectl exec -it <pod-name> -- /bin/bash
# Port-forward for local testing
kubectl port-forward <pod-name> 8080:5000
# Check DNS resolution
kubectl run -it --rm debug --image=busybox -- nslookup <service-name>Conclusion
Deploying Python applications to Kubernetes provides a robust, scalable, and automated infrastructure. This guide covered basic deployments, configuration management, scaling, advanced patterns, and troubleshooting techniques. By implementing these practices, you can ensure your Python applications run reliably and efficiently in Kubernetes environments.