Kubernetes Design Patterns: Building Cloud-Native Applications

Shunku

Kubernetes design patterns are reusable solutions for common challenges in containerized applications. Understanding these patterns helps you build more maintainable, scalable, and robust systems.

Pattern Categories

flowchart TB
    subgraph Foundational["Foundational Patterns"]
        A1["Health Probes"]
        A2["Managed Lifecycle"]
        A3["Declarative Deployment"]
    end

    subgraph Behavioral["Behavioral Patterns"]
        B1["Batch Job"]
        B2["Periodic Job"]
        B3["Daemon Service"]
    end

    subgraph Structural["Structural Patterns"]
        C1["Init Container"]
        C2["Sidecar"]
        C3["Ambassador"]
        C4["Adapter"]
    end

    style Foundational fill:#3b82f6,color:#fff
    style Behavioral fill:#8b5cf6,color:#fff
    style Structural fill:#22c55e,color:#fff

Init Container Pattern

Init containers run before main containers, handling setup tasks.

flowchart LR
    A["Init Container 1"] --> B["Init Container 2"]
    B --> C["Main Container"]

    style A fill:#f59e0b,color:#fff
    style B fill:#f59e0b,color:#fff
    style C fill:#22c55e,color:#fff

Use Cases

  • Wait for dependent services
  • Clone repositories
  • Download configuration
  • Database migrations

Example: Wait for Database

apiVersion: v1
kind: Pod
metadata:
  name: web-app
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          until nc -z postgres-service 5432; do
            echo "Waiting for database..."
            sleep 2
          done
    - name: run-migrations
      image: myapp:1.0
      command: ["./migrate.sh"]
  containers:
    - name: app
      image: myapp:1.0

Example: Clone Configuration

apiVersion: v1
kind: Pod
metadata:
  name: web-server
spec:
  initContainers:
    - name: git-clone
      image: alpine/git
      command:
        - git
        - clone
        - https://github.com/example/config.git
        - /config
      volumeMounts:
        - name: config-volume
          mountPath: /config
  containers:
    - name: nginx
      image: nginx:1.25
      volumeMounts:
        - name: config-volume
          mountPath: /usr/share/nginx/html
  volumes:
    - name: config-volume
      emptyDir: {}

Sidecar Pattern

A helper container that extends the main container's functionality.

flowchart TB
    subgraph Pod
        A["Main Container"] <--> B["Sidecar Container"]
        A --> V["Shared Volume"]
        B --> V
    end

    style A fill:#3b82f6,color:#fff
    style B fill:#8b5cf6,color:#fff

Use Cases

  • Log collection and forwarding
  • Monitoring and metrics
  • Configuration sync
  • TLS termination

Example: Log Shipping Sidecar

apiVersion: v1
kind: Pod
metadata:
  name: app-with-logging
spec:
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
    - name: log-shipper
      image: fluent/fluent-bit:2.1
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
        - name: fluent-config
          mountPath: /fluent-bit/etc
  volumes:
    - name: logs
      emptyDir: {}
    - name: fluent-config
      configMap:
        name: fluent-bit-config

Example: Monitoring Sidecar

apiVersion: v1
kind: Pod
metadata:
  name: app-with-metrics
spec:
  containers:
    - name: app
      image: myapp:1.0
      ports:
        - containerPort: 8080
    - name: prometheus-exporter
      image: prom/statsd-exporter:v0.24.0
      ports:
        - containerPort: 9102
      args:
        - --statsd.listen-udp=:9125
        - --web.listen-address=:9102

Ambassador Pattern

A proxy container that handles external communication.

flowchart LR
    A["External Service"] <--> B["Ambassador"]
    B <--> C["Main Container"]

    subgraph Pod
        B
        C
    end

    style B fill:#f59e0b,color:#fff
    style C fill:#3b82f6,color:#fff

Use Cases

  • Database connection pooling
  • Service discovery abstraction
  • Rate limiting
  • Circuit breaking

Example: Database Proxy

apiVersion: v1
kind: Pod
metadata:
  name: app-with-db-proxy
spec:
  containers:
    - name: app
      image: myapp:1.0
      env:
        - name: DATABASE_HOST
          value: "localhost"
        - name: DATABASE_PORT
          value: "5432"
    - name: cloud-sql-proxy
      image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.1
      args:
        - --structured-logs
        - --port=5432
        - my-project:us-central1:my-instance
      securityContext:
        runAsNonRoot: true

Example: Redis Proxy

apiVersion: v1
kind: Pod
metadata:
  name: app-with-redis-proxy
spec:
  containers:
    - name: app
      image: myapp:1.0
      env:
        - name: REDIS_HOST
          value: "localhost"
    - name: twemproxy
      image: twemproxy:latest
      ports:
        - containerPort: 6379
      volumeMounts:
        - name: config
          mountPath: /etc/twemproxy
  volumes:
    - name: config
      configMap:
        name: twemproxy-config

Adapter Pattern

Transforms output from the main container to a standard format.

flowchart LR
    A["Main Container"] -->|"Custom Format"| B["Adapter"]
    B -->|"Standard Format"| C["Monitoring System"]

    subgraph Pod
        A
        B
    end

    style A fill:#3b82f6,color:#fff
    style B fill:#22c55e,color:#fff

Use Cases

  • Metrics format conversion
  • Log format standardization
  • Protocol translation

Example: Prometheus Adapter

apiVersion: v1
kind: Pod
metadata:
  name: legacy-app
spec:
  containers:
    - name: legacy-app
      image: legacy:1.0
      ports:
        - containerPort: 8080
    - name: prometheus-adapter
      image: nginx/nginx-prometheus-exporter:0.11
      args:
        - -nginx.scrape-uri=http://localhost:8080/nginx_status
      ports:
        - containerPort: 9113

Example: Log Adapter

apiVersion: v1
kind: Pod
metadata:
  name: app-with-log-adapter
spec:
  containers:
    - name: app
      image: legacy:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
    - name: log-adapter
      image: alpine:3.18
      command:
        - sh
        - -c
        - |
          tail -f /var/log/app/custom.log | \
          while read line; do
            echo "{\"timestamp\":\"$(date -Iseconds)\",\"message\":\"$line\"}"
          done
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
  volumes:
    - name: logs
      emptyDir: {}

Pattern Comparison

Pattern Purpose Container Relationship
Init Container Setup before main Sequential, exits
Sidecar Extend functionality Parallel, same lifecycle
Ambassador Proxy external Proxy between main and external
Adapter Transform output Receives from main

Combining Patterns

apiVersion: v1
kind: Pod
metadata:
  name: full-pattern-example
spec:
  # Init Container Pattern
  initContainers:
    - name: wait-for-deps
      image: busybox:1.36
      command: ["sh", "-c", "until nc -z redis 6379; do sleep 1; done"]

  containers:
    # Main Application
    - name: app
      image: myapp:1.0
      ports:
        - containerPort: 8080
      volumeMounts:
        - name: logs
          mountPath: /var/log/app

    # Sidecar Pattern: Log collection
    - name: log-collector
      image: fluent/fluent-bit:2.1
      volumeMounts:
        - name: logs
          mountPath: /var/log/app

    # Ambassador Pattern: Database proxy
    - name: db-proxy
      image: cloud-sql-proxy:2.1
      args: ["--port=5432", "project:region:instance"]

    # Adapter Pattern: Metrics conversion
    - name: metrics-adapter
      image: prom/statsd-exporter:v0.24.0
      ports:
        - containerPort: 9102

  volumes:
    - name: logs
      emptyDir: {}

Best Practices

Practice Recommendation
Single responsibility Each container does one thing well
Shared volumes for data Use emptyDir for inter-container communication
Resource limits Set limits on all containers including sidecars
Lifecycle alignment Ensure sidecars don't outlive main containers
Keep sidecars lightweight Minimize resource overhead

When to Use Each Pattern

flowchart TB
    A["Need Setup?"] -->|Yes| B["Init Container"]
    A -->|No| C["Need Helper?"]
    C -->|Yes| D["External Communication?"]
    C -->|No| E["Single Container"]
    D -->|Yes| F["Ambassador"]
    D -->|No| G["Format Conversion?"]
    G -->|Yes| H["Adapter"]
    G -->|No| I["Sidecar"]

    style B fill:#f59e0b,color:#fff
    style F fill:#8b5cf6,color:#fff
    style H fill:#22c55e,color:#fff
    style I fill:#3b82f6,color:#fff

Key Takeaways

  1. Init Containers for setup - Run once before main containers
  2. Sidecars extend functionality - Logging, monitoring, sync
  3. Ambassadors handle external communication - Proxies and connection pooling
  4. Adapters standardize output - Format conversion for monitoring
  5. Combine patterns as needed - Real applications often use multiple patterns

References