Deploying to Kubernetes — From Docker Image to Running Service
Why Deploying to Kubernetes Matters
Knowing how to deploy applications to Kubernetes is a critical skill for modern DevOps and platform engineers. This chapter walks through the complete deployment workflow — from building a Docker image to running a scalable, production-grade service on Kubernetes.
Why this matters for your career:
- Deployment automation is the foundation of DevOps and CI/CD
- Kubernetes deployment skills are required for most cloud engineering roles
- Understanding the deployment lifecycle helps you debug production issues
- Freelance infrastructure projects often involve migrating to or from Kubernetes
The Deployment Workflow
Code → Docker Image → Push to Registry → Kubernetes Manifest → kubectl apply → Running Service
Step 1: Build a Docker Image
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
Build and push:
# Build
docker build -t my-app:latest .
# Tag for registry
docker tag my-app:latest ghcr.io/myorg/my-app:v1.0.0
# Push to container registry
docker push ghcr.io/myorg/my-app:v1.0.0
Step 2: Write Kubernetes Manifests
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: my-app
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-app
spec:
replicas: 3
revisionHistoryLimit: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: ghcr.io/myorg/my-app:v1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
namespace: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: my-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
Step 3: Apply to Cluster
kubectl apply -f namespace.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
# Or apply all at once
kubectl apply -f k8s/
# Check status
kubectl get pods -n my-app -w
kubectl get deployments -n my-app
kubectl get ingress -n my-app
Rolling Updates and Rollbacks
Rolling Update
# Update to new version
kubectl set image deployment/my-app app=ghcr.io/myorg/my-app:v2.0.0 -n my-app
# Or update the manifest and re-apply
kubectl apply -f deployment.yaml
# Monitor rollout
kubectl rollout status deployment/my-app -n my-app
Rollback
# Undo last rollout
kubectl rollout undo deployment/my-app -n my-app
# Rollback to specific revision
kubectl rollout undo deployment/my-app --to-revision=2 -n my-app
# View rollout history
kubectl rollout history deployment/my-app -n my-app
Secrets and ConfigMaps
# Create ConfigMap from literal values
kubectl create configmap app-config -n my-app \
--from-literal=APP_NAME=my-app \
--from-literal=LOG_LEVEL=info
# Create Secret from literal values
kubectl create secret generic app-secrets -n my-app \
--from-literal=database-url=postgresql://user:pass@host/db
# Or from a file
kubectl create secret generic app-secrets -n my-app \
--from-file=./secrets.env
Deploying with Helm
Helm is the package manager for Kubernetes:
# Add a repo
helm repo add bitnami https://charts.bitnami.com/bitnami
# Install a chart
helm install my-release bitnami/nginx
# Create your own chart
helm create my-chart
# Install your chart
helm install my-app ./my-chart \
--set image.tag=v1.0.0 \
--set replicaCount=3 \
-n my-app
# Upgrade
helm upgrade my-app ./my-chart --set image.tag=v2.0.0
# Rollback
helm rollback my-app 1
Deployment Strategies Comparison
| Strategy | Description | Zero Downtime | Rollback | Use Case | |----------|-------------|--------------|----------|----------| | RollingUpdate | Replace pods gradually | ✅ Yes | ✅ Yes | Default for most apps | | Recreate | Kill all pods, then create new | ❌ No | ⚠️ Manual | Stateful apps, DB migrations | | Blue/Green | Two environments, switch traffic | ✅ Yes | ✅ Instant | Critical production apps | | Canary | Gradual traffic shift to new version | ✅ Yes | ✅ Fast | Testing with real traffic | | A/B Testing | Route based on headers/cookies | ✅ Yes | ✅ Fast | Feature experimentation |
Common Troubleshooting Commands
| Command | Purpose |
|---------|--------|
| kubectl describe pod pod-name -n my-app | Detailed pod status and events |
| kubectl logs pod-name -n my-app --tail=100 | View last 100 log lines |
| kubectl logs pod-name -n my-app -f | Stream logs in real-time |
| kubectl exec -it pod-name -n my-app -- sh | Open shell in container |
| kubectl get events -n my-app --sort-by='.lastTimestamp' | View cluster events |
| kubectl port-forward pod-name 8080:3000 -n my-app | Tunnel to a pod locally |
Summary
Deploying to Kubernetes involves building a Docker image, writing Kubernetes manifests (Deployment, Service, Ingress), and applying them with kubectl. Rolling updates enable zero-downtime deployments. Helm simplifies package management. Understanding this workflow is essential for any DevOps or platform engineering role.
Key takeaways:
- Build minimal Docker images using multi-stage builds
- Write Deployment, Service, and Ingress manifests
- Use kubectl apply to deploy and rollout for updates
- RollingUpdate strategy provides zero-downtime deployments
- ConfigMaps and Secrets manage configuration separately from images
- Helm charts package and version Kubernetes applications
- Blue/Green and Canary strategies reduce deployment risk
- Use describe, logs, exec for troubleshooting
What's Next: Monitoring & Scaling
The next chapter covers monitoring and scaling in Kubernetes — metrics, HPA (Horizontal Pod Autoscaler), resource quotas, and cluster monitoring with Prometheus.