Kubernetes Container Orchestration
Stack Versions
Kubernetes: 1.29+
kubectl: 1.29+
Helm: 3.14+
Kustomize: 5.3+
Project Structure
k8s/
├── base/
│ ├── namespace.yaml
│ ├── configmap.yaml
│ ├── secrets.yaml
│ └── app/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── hpa.yaml
├── overlays/
│ ├── development/
│ ├── staging/
│ └── production/
│ ├── kustomization.yaml
│ ├── patches/
│ └── ingress.yaml
└── helm/
└── charts/
Core Manifests
Namespace
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
app.kubernetes.io/name: myapp
environment: production
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: myapp
data:
LOG_LEVEL: "info"
LOG_FORMAT: "json"
API_PORT: "8080"
METRICS_PORT: "9090"
Secrets (with External Secrets)
# Basic Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: myapp
type: Opaque
stringData:
DATABASE_URL: "postgres://user:pass@postgres:5432/db"
JWT_SECRET: "your-jwt-secret"
---
# External Secrets (production)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: myapp
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secrets
data:
- secretKey: DATABASE_URL
remoteRef:
key: myapp/database-url
Deployment Pattern
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: myapp
labels:
app: api
spec:
replicas: 3
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
serviceAccountName: api
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: api
image: ghcr.io/org/api:v1.0.0
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
- name: metrics
containerPort: 9090
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health/live
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet:
path: /health/live
port: http
failureThreshold: 30
periodSeconds: 5
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: api
topologyKey: kubernetes.io/hostname
Service
apiVersion: v1
kind: Service
metadata:
name: api
namespace: myapp
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: http
- name: metrics
port: 9090
targetPort: metrics
selector:
app: api
HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
namespace: myapp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 120
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: myapp
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rate-limit-rps: "50"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
Kustomize
Base Kustomization
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: myapp
resources:
- namespace.yaml
- configmap.yaml
- secrets.yaml
- deployment.yaml
- service.yaml
- hpa.yaml
commonLabels:
app.kubernetes.io/part-of: myapp
Production Overlay
# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: myapp-prod
resources:
- ../../base
- ingress.yaml
namePrefix: prod-
commonLabels:
environment: production
images:
- name: ghcr.io/org/api
newTag: v1.5.0
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
configMapGenerator:
- name: app-config
behavior: merge
literals:
- LOG_LEVEL=warn
Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: myapp
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-ingress
namespace: myapp
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
namespace: myapp
spec:
minAvailable: 2
selector:
matchLabels:
app: api
Commands Reference
# Apply with kustomize
kubectl apply -k k8s/overlays/production
# Preview changes
kubectl diff -k k8s/overlays/production
# Rollout management
kubectl rollout status deployment/api -n myapp
kubectl rollout undo deployment/api -n myapp
kubectl rollout history deployment/api -n myapp
# Scaling
kubectl scale deployment/api --replicas=5 -n myapp
# Debugging
kubectl logs -f deployment/api -n myapp
kubectl exec -it deployment/api -n myapp -- /bin/sh
kubectl port-forward svc/api 8080:80 -n myapp
# Resource inspection
kubectl get all -n myapp
kubectl get pods -n myapp -o wide
kubectl top pods -n myapp
kubectl describe pod <pod-name> -n myapp
Best Practices
- Always set resource limits - Prevents resource starvation
- Use all three probes - liveness, readiness, startup
- Security context - runAsNonRoot, readOnlyRootFilesystem
- Pod anti-affinity - Spread across nodes/zones
- Specific image tags - Never use :latest in production
- Network policies - Default deny, explicit allow
- PodDisruptionBudgets - Maintain availability during updates
Related Skills
docker-containers: Container build patterns
traefik-proxy: Ingress and routing
devops-infra: CI/CD pipelines
opentelemetry: Cluster observability