✨ Kubernetes Best Practices

Production-ready guidelines, reliability patterns, and enterprise-grade operational excellence

Resource Management

Resource Requests and Limits

CPU and Memory Configuration

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:1.0
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        
        # Good practice: Set requests = limits for guaranteed QoS
        # resources:
        #   requests:
        #     memory: "512Mi"
        #     cpu: "500m"
        #   limits:
        #     memory: "512Mi"
        #     cpu: "500m"

Quality of Service Classes

# Guaranteed QoS (highest priority)
resources:
  requests:
    memory: "1Gi"
    cpu: "1"
  limits:
    memory: "1Gi"
    cpu: "1"

# Burstable QoS (medium priority)
resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "1Gi"
    cpu: "1"

# BestEffort QoS (lowest priority - avoid in production)
# No resources specified

Resource Quotas

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-quota
  namespace: production
spec:
  hard:
    requests.cpu: "100"
    requests.memory: 200Gi
    limits.cpu: "200"
    limits.memory: 400Gi
    persistentvolumeclaims: "10"
    services.loadbalancers: "2"
    services.nodeports: "0"  # Disable NodePort in production

---
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
  namespace: production
spec:
  limits:
  - default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 256Mi
      cpu: 250m
    max:
      memory: 2Gi
      cpu: 2
    min:
      memory: 128Mi
      cpu: 100m
    type: Container

⚠️ Resource Management Pitfalls

  • Never run containers without resource limits in production
  • Avoid BestEffort QoS for critical workloads
  • Don't set CPU limits too low - causes throttling
  • Memory limits too low cause OOMKilled pods
  • Always test resource settings under load

Health Checks

Probe Configuration

Liveness Probe

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: Custom-Header
      value: Awesome
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  successThreshold: 1
  failureThreshold: 3

Readiness Probe

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
  timeoutSeconds: 3
  successThreshold: 1
  failureThreshold: 3

Startup Probe

# For slow-starting containers
startupProbe:
  httpGet:
    path: /startup
    port: 8080
  initialDelaySeconds: 0
  periodSeconds: 10
  timeoutSeconds: 3
  successThreshold: 1
  failureThreshold: 30  # 30 * 10 = 300s max startup

Complete Health Check Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: web
        image: webapp:2.0
        ports:
        - containerPort: 8080
          name: http
        
        # Startup probe for slow initialization
        startupProbe:
          httpGet:
            path: /startup
            port: http
          periodSeconds: 10
          failureThreshold: 30
        
        # Liveness probe to restart unhealthy containers
        livenessProbe:
          httpGet:
            path: /healthz
            port: http
          initialDelaySeconds: 0
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        
        # Readiness probe to control traffic
        readinessProbe:
          httpGet:
            path: /ready
            port: http
          initialDelaySeconds: 0
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
        
        # Graceful shutdown
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 15"]

Auto-Scaling

Horizontal Pod Autoscaler (HPA)

# HPA based on CPU
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment
  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:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
      - type: Pods
        value: 2
        periodSeconds: 60
      selectPolicy: Min
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
      - type: Pods
        value: 4
        periodSeconds: 60
      selectPolicy: Max

Vertical Pod Autoscaler (VPA)

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment
  updatePolicy:
    updateMode: "Auto"  # or "Off" for recommendation only
  resourcePolicy:
    containerPolicies:
    - containerName: web
      minAllowed:
        cpu: 100m
        memory: 128Mi
      maxAllowed:
        cpu: 2
        memory: 2Gi
      controlledResources: ["cpu", "memory"]

Cluster Autoscaler

# Cluster autoscaler deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-autoscaler
  namespace: kube-system
spec:
  template:
    spec:
      containers:
      - image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.21.0
        name: cluster-autoscaler
        command:
        - ./cluster-autoscaler
        - --v=4
        - --stderrthreshold=info
        - --cloud-provider=aws
        - --skip-nodes-with-local-storage=false
        - --expander=least-waste
        - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster
        - --balance-similar-node-groups
        - --skip-nodes-with-system-pods=false

Reliability Patterns

Pod Disruption Budgets

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2
  # OR use maxUnavailable
  # maxUnavailable: 1
  selector:
    matchLabels:
      app: web
  
  # For stateful services
  unhealthyPodEvictionPolicy: IfHealthyBudget

Anti-Affinity Rules

Pod Anti-Affinity

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - web
      topologyKey: kubernetes.io/hostname
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - web
        topologyKey: failure-domain.beta.kubernetes.io/zone

Node Affinity

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: node-type
          operator: In
          values:
          - production
        - key: kubernetes.io/arch
          operator: In
          values:
          - amd64
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 50
      preference:
        matchExpressions:
        - key: disk-type
          operator: In
          values:
          - ssd

Priority Classes

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000
globalDefault: false
description: "Critical production workloads"

---
apiVersion: v1
kind: Pod
metadata:
  name: critical-app
spec:
  priorityClassName: high-priority
  containers:
  - name: app
    image: critical:1.0

Security Best Practices

Pod Security Standards

# Pod Security Policy (deprecated, use Pod Security Standards)
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

---
# Security Context
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:1.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: cache
      mountPath: /app/cache
  volumes:
  - name: tmp
    emptyDir: {}
  - name: cache
    emptyDir: {}

Network Policies

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: production
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

Security Checklist

  • Enable RBAC and use least privilege
  • Use Pod Security Standards (restricted)
  • Implement Network Policies
  • Scan images for vulnerabilities
  • Use private image registries
  • Enable audit logging
  • Rotate secrets regularly
  • Use service mesh for mTLS
  • Implement admission controllers
  • Regular security updates

Observability

Logging Best Practices

Structured Logging

# Application logging configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: logging-config
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush        5
        Daemon       Off
        Log_Level    info
    
    [INPUT]
        Name              tail
        Path              /var/log/containers/*.log
        Parser            docker
        Tag               kube.*
        Refresh_Interval  5
        Mem_Buf_Limit     50MB
        Skip_Long_Lines   On
    
    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc:443
        Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token
        Merge_Log           On
        K8S-Logging.Parser  On
        K8S-Logging.Exclude On
    
    [OUTPUT]
        Name   elasticsearch
        Match  *
        Host   elasticsearch.logging
        Port   9200
        Index  kubernetes
        Type   _doc

Metrics and Monitoring

# ServiceMonitor for Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: app-metrics
  namespace: production
spec:
  selector:
    matchLabels:
      app: web
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
    relabelings:
    - sourceLabels: [__meta_kubernetes_pod_label_version]
      targetLabel: version
    - sourceLabels: [__meta_kubernetes_pod_label_environment]
      targetLabel: environment
    metricRelabelings:
    - sourceLabels: [__name__]
      regex: '(request_duration_seconds.*|request_total)'
      action: keep

Distributed Tracing

# OpenTelemetry configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-config
data:
  otel-collector-config.yaml: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318
    
    processors:
      batch:
        timeout: 1s
        send_batch_size: 1024
      memory_limiter:
        check_interval: 1s
        limit_mib: 512
      resource:
        attributes:
        - key: cluster.name
          value: production
          action: insert
    
    exporters:
      jaeger:
        endpoint: jaeger-collector:14250
        tls:
          insecure: true
    
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch, resource]
          exporters: [jaeger]

CI/CD Best Practices

GitOps with ArgoCD

# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/k8s-config
    targetRevision: HEAD
    path: overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
    - Validate=true
    - CreateNamespace=false
    - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Progressive Delivery

# Flagger Canary Deployment
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: web-canary
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  progressDeadlineSeconds: 60
  service:
    port: 80
    targetPort: 8080
    gateways:
    - public-gateway.istio-system.svc.cluster.local
    hosts:
    - app.example.com
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 1m
    webhooks:
    - name: load-test
      url: http://loadtester/
      timeout: 5s
      metadata:
        cmd: "hey -z 1m -q 10 -c 2 http://web-canary.production/"

✅ CI/CD Best Practices

  • Use GitOps for declarative deployments
  • Implement progressive delivery (canary, blue-green)
  • Automate testing in CI pipeline
  • Version everything (images, configs, charts)
  • Use semantic versioning
  • Implement rollback strategies
  • Separate config from code
  • Use Helm or Kustomize for templating

Troubleshooting Guide

Common Issues and Solutions

Issue Symptoms Diagnosis Solution
Pod CrashLoopBackOff Pod repeatedly crashes and restarts kubectl logs pod-name --previous Fix application errors, check resources
ImagePullBackOff Cannot pull container image kubectl describe pod pod-name Check image name, registry credentials
Pending Pods Pods stuck in Pending state kubectl describe pod pod-name Check resources, node capacity, PVCs
OOMKilled Container killed due to memory kubectl describe pod pod-name Increase memory limits, optimize app
Service not accessible Cannot reach service endpoints kubectl get endpoints service-name Check selectors, readiness probes
Node NotReady Node in NotReady state kubectl describe node node-name Check kubelet, disk pressure, memory

Debugging Commands

# Pod debugging
kubectl logs pod-name -c container-name --previous
kubectl exec -it pod-name -- /bin/bash
kubectl debug pod-name -it --image=busybox --share-processes --copy-to=debug-pod

# Service debugging
kubectl port-forward service/my-service 8080:80
kubectl run curl --image=curlimages/curl -it --rm -- sh
nslookup my-service.default.svc.cluster.local

# Node debugging
kubectl get nodes -o wide
kubectl top nodes
kubectl describe node node-name
kubectl get events --sort-by='.lastTimestamp'

# Cluster debugging
kubectl cluster-info dump --output-directory=/tmp/cluster-dump
kubectl get componentstatuses
kubectl get all --all-namespaces
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n namespace

❌ Common Anti-patterns to Avoid

  • Using latest tag: Always use specific version tags
  • No resource limits: Set appropriate requests and limits
  • Single replica: Run at least 2 replicas for HA
  • No health checks: Always configure probes
  • Hardcoded configs: Use ConfigMaps and Secrets
  • Root containers: Run as non-root user
  • No PodDisruptionBudget: Define PDBs for critical apps
  • Ignoring labels: Use consistent labeling strategy

Production Readiness Checklist

Application Readiness

  • Health checks configured (liveness, readiness, startup)
  • Graceful shutdown handling
  • Resource requests and limits set
  • Horizontal Pod Autoscaler configured
  • Pod Disruption Budget defined
  • Anti-affinity rules for HA
  • Rolling update strategy configured
  • Secrets managed securely

Observability Readiness

  • Structured logging implemented
  • Metrics exposed and collected
  • Distributed tracing configured
  • Alerts and dashboards created
  • SLIs and SLOs defined
  • Error budgets established
  • Runbooks documented

Security Readiness

  • Pod Security Standards enforced
  • Network Policies implemented
  • RBAC configured with least privilege
  • Image scanning in CI/CD
  • Secrets encryption at rest
  • Audit logging enabled
  • Service mesh with mTLS (if applicable)
  • Regular security updates scheduled

Operational Readiness

  • GitOps workflow established
  • Backup and restore procedures tested
  • Disaster recovery plan documented
  • Monitoring and alerting configured
  • On-call rotation established
  • Documentation up to date
  • Load testing completed
  • Chaos engineering practices
← Back to Kubernetes Overview