Deploying Python Applications to Kubernetes in Docker (KinD)
This guide walks through setting up a local Kubernetes environment using KinD and deploying Python applications to it.
1. Introduction to KinD
KinD (Kubernetes in Docker) is a tool that runs local Kubernetes clusters inside Docker containers. It's designed for testing and development of Kubernetes applications without needing a full-scale cluster.
Advantages of KinD
- Lightweight: Runs Kubernetes nodes as Docker containers
- Fast: Creates clusters in seconds
- Disposable: Easy to create and delete clusters
- Multi-node: Can simulate multi-node clusters
- Consistent: Provides a reliable Kubernetes experience
2. Installing KinD
Prerequisites
- Docker installed and running
- kubectl installed
Installation Steps
Linux
# Download KinD binary
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
# Verify installation
kind versionmacOS
# Using Homebrew
brew install kind
# Verify installation
kind versionWindows (PowerShell)
# Using Chocolatey
choco install kind
# Using curl
curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.20.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
# Verify installation
kind version3. Creating a KinD Cluster
Basic Cluster Creation
# Create a cluster with default name 'kind'
kind create cluster
# Create a cluster with custom name
kind create cluster --name python-app-cluster
# Verify the cluster is running
kubectl cluster-info --context kind-python-app-cluster
# View nodes
kubectl get nodesMulti-Node Cluster
Create a config file kind-config.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: workerCreate the cluster with this config:
kind create cluster --name python-multi --config kind-config.yaml
# Verify nodes
kubectl get nodes4. Preparing Your Python Application
Containerize Your Application
First, create a Dockerfile as shown in the containerization guide. Then build and push the image:
# Build the image
docker build -t my-python-app:v1 .
# Load the image into KinD
kind load docker-image my-python-app:v1 --name python-app-clusterThis step is crucial as KinD clusters can't automatically pull from your local Docker daemon.
5. Deploying to KinD
Creating Kubernetes Manifests
Deployment (deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app
labels:
app: python-app
spec:
replicas: 2
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: python-app
image: my-python-app:v1
ports:
- containerPort: 5000
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /
port: 5000
initialDelaySeconds: 5
periodSeconds: 10Service (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 and service
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# Verify deployment and service
kubectl get deployments
kubectl get services
kubectl get pods
# View logs from a pod
kubectl logs -l app=python-app6. Accessing Your Application
Port Forwarding
# Forward local port to service
kubectl port-forward service/python-app 8080:80
# Access in browser: http://localhost:8080Using Ingress
- Enable ingress in KinD:
# kind-ingress-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker- Create the cluster with ingress support:
kind create cluster --name ingress-cluster --config kind-ingress-config.yaml- Install NGINX Ingress Controller:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# Wait for it to be ready
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90s- Create an Ingress resource:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: python-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: python-app
port:
number: 80- Apply the Ingress:
kubectl apply -f ingress.yaml
# Verify the Ingress
kubectl get ingressNow you can access your application at http://localhost/.
7. Configuration and Secrets
ConfigMaps for Configuration
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: python-app-config
data:
APP_ENV: "development"
DEBUG: "true"
CONFIG_PATH: "/etc/config/app-config.json"
app-config.json: |
{
"feature_flags": {
"new_feature": true,
"experimental": false
}
}Apply and use in deployment:
kubectl apply -f configmap.yamlUpdate your deployment to use the ConfigMap:
spec:
containers:
- name: python-app
# ...
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: python-app-config
key: APP_ENV
- name: DEBUG
valueFrom:
configMapKeyRef:
name: python-app-config
key: DEBUG
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: python-app-configSecrets for Sensitive Data
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: python-app-secrets
type: Opaque
data:
db_password: cGFzc3dvcmQxMjM= # Base64 encoded password123
api_key: c2VjcmV0X2tleV8xMjM= # Base64 encoded secret_key_123Apply and use in deployment:
kubectl apply -f secret.yamlUpdate deployment:
spec:
containers:
- name: python-app
# ...
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: python-app-secrets
key: db_password
- name: API_KEY
valueFrom:
secretKeyRef:
name: python-app-secrets
key: api_key8. Persistent Storage
Creating a PersistentVolumeClaim
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: python-app-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1GiUpdate deployment:
spec:
containers:
- name: python-app
# ...
volumeMounts:
- name: data-volume
mountPath: /app/data
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: python-app-data9. Scaling and Updates
Scaling Deployments
# Scale to 3 replicas
kubectl scale deployment python-app --replicas=3
# Verify
kubectl get podsRolling Updates
Update your application code, build a new image, and load it to KinD:
# Build new version
docker build -t my-python-app:v2 .
# Load into KinD
kind load docker-image my-python-app:v2 --name python-app-cluster
# Update deployment
kubectl set image deployment/python-app python-app=my-python-app:v2
# Watch the rollout
kubectl rollout status deployment/python-app10. Debugging in KinD
Viewing Logs
# Get logs from a specific pod
kubectl logs python-app-7d6f8df57c-abcde
# Follow logs (like tail -f)
kubectl logs -f deployment/python-app
# Show logs from all pods with a specific label
kubectl logs -l app=python-app --all-containers=trueExecuting Commands in Containers
# Open a shell in a pod
kubectl exec -it python-app-7d6f8df57c-abcde -- /bin/bash
# Run a one-off command
kubectl exec python-app-7d6f8df57c-abcde -- python -c "import sys; print(sys.version)"Debugging with Ephemeral Containers
# Enable the feature if needed (newer Kubernetes has it enabled by default)
# Then add a debug container to a running pod
kubectl debug -it python-app-7d6f8df57c-abcde --image=python:3.9-slim --target=python-app11. Advanced KinD Configuration
Custom Resource Limits
Create a config file with resource limits:
# kind-resources-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
system-reserved: memory=2Gi
eviction-hard: memory.available<500MiRegistry Integration
Set up a local registry:
# Create a registry container
docker run -d --name registry -p 5000:5000 registry:2
# Create KinD cluster with registry
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]
endpoint = ["http://registry:5000"]
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
EOF
# Connect the containers
docker network connect kind registry
# Push to local registry and use in K8s
docker tag my-python-app:v1 localhost:5000/my-python-app:v1
docker push localhost:5000/my-python-app:v1
# Update deployment to use this image
# image: localhost:5000/my-python-app:v112. Testing with KinD
Integration Testing
Create a script that:
- Creates a KinD cluster
- Loads your application image
- Deploys your application
- Runs tests against it
- Tears down the cluster
Example script:
#!/bin/bash
set -e
# Create cluster
kind create cluster --name test-cluster
# Load image
kind load docker-image my-python-app:test --name test-cluster
# Deploy app
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# Wait for deployment to be ready
kubectl wait --for=condition=available --timeout=60s deployment/python-app
# Run tests
# Port forward in background
kubectl port-forward service/python-app 8080:80 &
PF_PID=$!
# Wait for port-forward to be ready
sleep 3
# Run actual tests
pytest integration_tests/
# Clean up
kill $PF_PID
kind delete cluster --name test-clusterConclusion
KinD provides a powerful and lightweight environment for developing and testing Kubernetes applications locally. By following this guide, you've learned how to create KinD clusters, deploy Python applications, configure resources, and debug effectively. This workflow allows you to develop Kubernetes applications confidently without needing access to an expensive cloud-based cluster.