Skip to content

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:

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:

yaml
apiVersion: v1
kind: Service
metadata:
  name: python-app
spec:
  selector:
    app: python-app
  ports:
    - port: 80
      targetPort: 5000
  type: ClusterIP

Applying the Manifests

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

4. Configuration Management

ConfigMaps

Create a file named configmap.yaml:

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:

bash
kubectl apply -f configmap.yaml

Update your deployment to use the ConfigMap:

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

Secrets

Create a file named secret.yaml:

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:

bash
kubectl apply -f secret.yaml

Update your deployment to use the Secret:

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

5. 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)

yaml
apiVersion: v1
kind: Service
metadata:
  name: python-app-nodeport
spec:
  selector:
    app: python-app
  ports:
    - port: 80
      targetPort: 5000
      nodePort: 30080
  type: NodePort

Access via http://<node-ip>:30080

LoadBalancer (Cloud Provider Load Balancer)

yaml
apiVersion: v1
kind: Service
metadata:
  name: python-app-lb
spec:
  selector:
    app: python-app
  ports:
    - port: 80
      targetPort: 5000
  type: LoadBalancer

Ingress (Path-Based Routing)

First, ensure you have an Ingress controller installed (like NGINX Ingress).

Create a file named ingress.yaml:

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-secret

Create TLS secret:

bash
kubectl create secret tls app-tls-secret --cert=path/to/cert --key=path/to/key

Apply the Ingress:

bash
kubectl apply -f ingress.yaml

6. Persistent Storage

Creating a PersistentVolumeClaim

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: python-app-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 1Gi

Using the PVC in your deployment:

yaml
spec:
  containers:
    - name: python-app
      # ...
      volumeMounts:
        - name: data
          mountPath: /app/data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: python-app-data

7. Health Checks and Probes

Add health checks to your deployment:

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

8. Scaling Your Application

Manual Scaling

bash
# Scale to 5 replicas
kubectl scale deployment python-app --replicas=5

Horizontal Pod Autoscaler (HPA)

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

Apply the HPA:

bash
kubectl apply -f hpa.yaml

9. Deployment Strategies

Rolling Updates (Default Strategy)

yaml
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%

Blue-Green Deployments

  1. Deploy a new version with different labels:
yaml
metadata:
  name: python-app-green
spec:
  selector:
    matchLabels:
      app: python-app-green
  template:
    metadata:
      labels:
        app: python-app-green
  1. Test the new version, then update the service selector:
yaml
spec:
  selector:
    app: python-app-green

Canary Deployments

  1. Deploy a small number of new version pods:
yaml
metadata:
  name: python-app-v2
spec:
  replicas: 1 # Small subset of traffic
  template:
    metadata:
      labels:
        app: python-app
        version: v2
  1. Both versions receive traffic as they share the same app label.
  2. Gradually increase replicas of the new version and decrease the old one.

10. Jobs and CronJobs

One-time Job

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

Scheduled CronJob

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

11. Monitoring and Logging

Prometheus Integration

Add annotations to your service:

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

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

12. Advanced Patterns

Sidecars

yaml
spec:
  containers:
    - name: python-app
      # ...
    - name: nginx-sidecar
      image: nginx:1.19
      ports:
        - containerPort: 80

Init Containers

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

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: python-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: python-app

13. Stateful Applications

StatefulSets for Ordered Pod Deployment

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

14. Multi-environment Deployment

Using Namespaces

bash
# Create namespaces
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace production

# Deploy to specific namespace
kubectl apply -f deployment.yaml -n dev

Using Kustomize for Environment Overlays

  1. Create base configuration:
my-app/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   └── kustomization.yaml
    ├── staging/
    │   └── kustomization.yaml
    └── production/
        └── kustomization.yaml
  1. Base kustomization.yaml:
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  1. Production overlay kustomization.yaml:
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../../base
namespace: production
patchesStrategicMerge:
  - deployment-patch.yaml
  1. Apply the kustomization:
bash
kubectl apply -k overlays/production

15. Security Best Practices

Pod Security Context

yaml
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
    - name: python-app
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL

Network Policies

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

RBAC (Role-Based Access Control)

yaml
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.io

16. Troubleshooting

Common Issues and Solutions

  1. Pod in CrashLoopBackOff:

    • Check logs: kubectl logs <pod-name>
    • Check container startup: kubectl describe pod <pod-name>
  2. Image Pull Errors:

    • Verify image name and tag
    • Check container registry credentials: kubectl create secret docker-registry regcred --docker-username=<username> --docker-password=<password>
  3. Pod Stuck in Pending:

    • Check cluster resources: kubectl describe node
    • Check PVC availability: kubectl get pvc
  4. Service Not Accessible:

    • Verify service endpoints: kubectl get endpoints <service-name>
    • Check pod labels match service selector
    • Verify pod health with readiness probe

Debugging Commands

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