Deploying containerized applications at scale
Scope: Container orchestration with Kubernetes - Pods, Deployments, Services, ConfigMaps, Secrets, Ingress Lines: 378 Last Updated: 2025-10-18 Format Version: 1.0 (Atomic)
Activate when:
Prerequisites:
brew install kubectl or from kubernetes.io)Common scenarios:
# pod.yaml - Smallest deployable unit
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: nginx
environment: production
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: http
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
env:
- name: ENVIRONMENT
value: "production"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
restartPolicy: Always
# deployment.yaml - Manages ReplicaSets and rolling updates
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Max pods above desired count
maxUnavailable: 0 # Max pods unavailable during update
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
version: v1.0.0
spec:
containers:
- name: web-app
image: myregistry/web-app:1.0.0
ports:
- containerPort: 8080
name: http
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
- name: APP_CONFIG
valueFrom:
configMapKeyRef:
name: app-config
key: config.json
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
# service-clusterip.yaml - Internal service
apiVersion: v1
kind: Service
metadata:
name: web-app-service
spec:
type: ClusterIP # Only accessible within cluster
selector:
app: web-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
---
# service-loadbalancer.yaml - External load balancer
apiVersion: v1
kind: Service
metadata:
name: web-app-lb
spec:
type: LoadBalancer # Cloud provider load balancer
selector:
app: web-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
---
# service-nodeport.yaml - Exposes on node IP
apiVersion: v1
kind: Service
metadata:
name: web-app-nodeport
spec:
type: NodePort # Accessible on node IP:NodePort
selector:
app: web-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 30000-32767 range
# configmap.yaml - Configuration data
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# Key-value pairs
log_level: "info"
max_connections: "100"
# File-like keys
config.json: |
{
"apiUrl": "https://api.example.com",
"timeout": 30,
"retries": 3
}
nginx.conf: |
server {
listen 80;
server_name _;
location / {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
---
# Use ConfigMap in Pod
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:1.0
# Environment variables from ConfigMap
envFrom:
- configMapRef:
name: app-config
# Individual env vars
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: log_level
# Mount as volume
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: nginx.conf
path: nginx.conf
# secret.yaml - Sensitive data (base64 encoded)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # echo -n 'admin' | base64
password: cGFzc3dvcmQxMjM= # echo -n 'password123' | base64
url: cG9zdGdyZXM6Ly9hZG1pbjpwYXNzd29yZDEyM0BkYi5leGFtcGxlLmNvbTo1NDMyL215ZGI=
---
# Use Secret in Pod
apiVersion: v1
kind: Pod
metadata:
name: db-client
spec:
containers:
- name: client
image: postgres:15
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# Mount as files
volumeMounts:
- name: db-secret
mountPath: /etc/secrets
readOnly: true
volumes:
- name: db-secret
secret:
secretName: db-credentials
# ingress.yaml - HTTP/HTTPS routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-cert
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
# Sidecar pattern - logging container
apiVersion: v1
kind: Pod
metadata:
name: web-app-with-logging
spec:
containers:
# Main application container
- name: web-app
image: myapp:1.0
ports:
- containerPort: 8080
volumeMounts:
- name: shared-logs
mountPath: /var/log/app
# Sidecar logging container
- name: log-shipper
image: fluent/fluent-bit:2.0
volumeMounts:
- name: shared-logs
mountPath: /var/log/app
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
volumes:
- name: shared-logs
emptyDir: {}
- name: fluent-bit-config
configMap:
name: fluent-bit-config
# cronjob.yaml - Scheduled jobs
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM UTC
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool:1.0
env:
- name: BACKUP_TARGET
value: "s3://backups/daily"
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: aws-credentials
key: access_key_id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: aws-credentials
key: secret_access_key
restartPolicy: OnFailure
# hpa.yaml - Auto-scaling based on metrics
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
# statefulset.yaml - For stateful applications
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
# networkpolicy.yaml - Network segmentation
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to: # Allow DNS
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
# Cluster info
kubectl cluster-info
kubectl get nodes
kubectl top nodes
# Pods
kubectl get pods
kubectl get pods -o wide
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> -f # Follow logs
kubectl logs <pod-name> -c <container-name> # Multi-container
kubectl exec -it <pod-name> -- /bin/bash
# Deployments
kubectl get deployments
kubectl describe deployment <deployment-name>
kubectl rollout status deployment/<deployment-name>
kubectl rollout history deployment/<deployment-name>
kubectl rollout undo deployment/<deployment-name>
kubectl scale deployment/<deployment-name> --replicas=5
# Services
kubectl get services
kubectl describe service <service-name>
kubectl get endpoints
# ConfigMaps and Secrets
kubectl create configmap app-config --from-file=config.json
kubectl create secret generic db-creds --from-literal=password=secret123
kubectl get configmaps
kubectl get secrets
kubectl describe configmap <name>
# Apply manifests
kubectl apply -f deployment.yaml
kubectl apply -f ./manifests/ # Directory
kubectl delete -f deployment.yaml
# Port forwarding
kubectl port-forward pod/<pod-name> 8080:80
kubectl port-forward service/<service-name> 8080:80
# Labels and selectors
kubectl get pods --selector=app=web
kubectl label pods <pod-name> environment=production
kubectl get pods --show-labels
# Contexts and namespaces
kubectl config get-contexts
kubectl config use-context <context-name>
kubectl get namespaces
kubectl create namespace dev
kubectl config set-context --current --namespace=dev
# Debug
kubectl debug <pod-name> -it --image=busybox
kubectl get events
kubectl get all -n <namespace>
resources:
requests:
memory: "256Mi" # Guaranteed
cpu: "500m" # 0.5 CPU cores
limits:
memory: "512Mi" # Maximum
cpu: "1000m" # 1 CPU core
# CPU units:
# 1 CPU = 1000m (millicores)
# 100m = 0.1 CPU
# 500m = 0.5 CPU
# Memory units:
# 128Mi = 128 Mebibytes
# 1Gi = 1 Gibibyte
# ❌ NEVER: Run as root without necessity
spec:
containers:
- name: app
image: myapp:1.0
# No securityContext - runs as root
# ✅ CORRECT: Run as non-root user
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
# ❌ NEVER: Use 'latest' tag in production
spec:
containers:
- name: app
image: myapp:latest # Unpredictable
# ✅ CORRECT: Use specific version tags
spec:
containers:
- name: app
image: myapp:1.2.3 # Reproducible
imagePullPolicy: IfNotPresent
# ❌ NEVER: Ignore resource limits
spec:
containers:
- name: app
image: myapp:1.0
# No resources defined - can consume unlimited
# ✅ CORRECT: Always define resources
spec:
containers:
- name: app
image: myapp:1.0
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# ❌ Don't expose secrets in environment variables unnecessarily
env:
- name: DB_PASSWORD
value: "hardcoded-password" # Visible in pod spec
# ✅ CORRECT: Use secrets and mount as files when possible
volumeMounts:
- name: db-secret
mountPath: /etc/secrets
readOnly: true
volumes:
- name: db-secret
secret:
secretName: db-credentials
# ❌ Don't skip health checks
spec:
containers:
- name: app
image: myapp:1.0
# No liveness or readiness probes
# ✅ CORRECT: Always implement health checks
spec:
containers:
- name: app
image: myapp:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# ❌ Don't apply without reviewing
kubectl apply -f manifest.yaml # No validation
# ✅ CORRECT: Validate and review first
kubectl apply -f manifest.yaml --dry-run=client -o yaml
kubectl diff -f manifest.yaml
kubectl apply -f manifest.yaml
Infrastructure:
terraform-patterns.md - Infrastructure as Code for Kubernetes resourcesinfrastructure-security.md - RBAC, network policies, security contextscost-optimization.md - Resource sizing, cluster autoscalingDevelopment:
aws-serverless.md - Alternative to container orchestrationcloudflare-workers.md - Edge computing alternativeStandards from CLAUDE.md:
Last Updated: 2025-10-18 Format Version: 1.0 (Atomic)