DevOps

Deploying Python Apps on Kubernetes

Introduction

Kubernetes (K8s) is the industry-standard container orchestration platform. In the Orbital multi-tenant platform, we used Kubernetes on AWS EKS to achieve zero-downtime rolling deployments, automatic scaling, and self-healing infrastructure. This guide walks through containerising a Django app and deploying it on K8s.

Step 1 โ€” Containerise Your Application

First, create a production-ready Dockerfile. Use multi-stage builds and a non-root user for security:

FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .

# Run as non-root
RUN adduser --disabled-password appuser
USER appuser

EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]

Step 2 โ€” Write a Kubernetes Deployment

A Deployment manages your pods, handles rolling updates, and restarts failed containers automatically:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: django-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: django-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: django-app
    spec:
      containers:
        - name: django
          image: myorg/django-app:latest
          ports:
            - containerPort: 8000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: django-secrets
                  key: database-url
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health/
              port: 8000
            initialDelaySeconds: 15
            periodSeconds: 20

๐Ÿ’ก Tip: Always set resources.requests and resources.limits. Without them, a runaway pod can starve other services in the same cluster โ€” a critical issue in multi-tenant environments.

Step 3 โ€” Expose with a Service & Ingress

apiVersion: v1
kind: Service
metadata:
  name: django-service
spec:
  selector:
    app: django-app
  ports:
    - port: 80
      targetPort: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: django-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  rules:
    - host: api.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: django-service
                port:
                  number: 80

Step 4 โ€” Store Secrets Safely

# Create secret from .env file
kubectl create secret generic django-secrets \
  --from-env-file=.env.production \
  --namespace=production

# Verify
kubectl get secrets -n production

Step 5 โ€” Deploy & Roll Out

# Apply manifests
kubectl apply -f k8s/

# Monitor rollout
kubectl rollout status deployment/django-app -n production

# Roll back if needed
kubectl rollout undo deployment/django-app -n production

Conclusion

With Kubernetes, your Python app gains production-grade resilience: automatic restarts on failure, rolling zero-downtime deployments, and horizontal scaling under load. Combined with the CI/CD pipeline from our GitHub Actions article, you have a complete modern DevOps workflow from code commit to live cluster.

← Back to Articles Also Read: CI/CD with GitHub Actions →