Belajar Kubernetes - Episode 20 - Multi-Container Pod Patterns

Belajar Kubernetes - Episode 20 - Multi-Container Pod Patterns

Di episode ini kita akan coba bahas multi-container Pod di Kubernetes dan common design pattern. Kita akan mempelajari sidecar, ambassador, adapter pattern, dan bagaimana container share resource dalam Pod.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 24, 2026
0 views
8 min read

Pendahuluan

Catatan

Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 19.1 di bawah ini

Episode 19.1Episode 19.1

Di episode sebelumnya kita sudah belajar tentang Ingress dan Gateway API untuk sophisticated HTTP/HTTPS, gRPC, dan TCP/UDP routing. Selanjutnya di episode 20 kali ini, kita akan coba bahas Multi-Container Pod, sebuah architectural pattern penting dimana multiple container run together dalam single Pod.

Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.

Sementara kebanyakan Pod run single container, Kubernetes support running multiple container dalam satu Pod. Container-container ini share network namespace yang sama, storage volume, dan lifecycle, enable powerful design pattern untuk building modular, maintainable application.

Apa Itu Multi-Container Pod?

Multi-Container Pod adalah Pod yang run dua atau lebih container yang work together sebagai single unit. Semua container di Pod share network namespace yang sama (IP address dan port), bisa communicate via localhost, dan bisa share storage volume.

Bayangkan Pod seperti logical host - sama seperti multiple process bisa run di server yang sama dan communicate via localhost, multiple container dalam Pod bisa communicate efficiently sambil tetap isolated dari container di Pod lain.

Karakteristik kunci Multi-Container Pod:

  • Shared network - Semua container share IP dan port space yang sama
  • Shared storage - Container bisa share volume
  • Shared lifecycle - Container start dan stop together
  • Localhost communication - Container communicate via 127.0.0.1
  • Atomic scheduling - Semua container scheduled di node yang sama
  • Tightly coupled - Container depend on each other

Kenapa Gunakan Multi-Container Pod?

Multi-container Pod solve beberapa architectural challenge:

  • Separation of concerns - Split functionality ke specialized container
  • Reusability - Gunakan standard sidecar container across application
  • Independent scaling - Update sidecar tanpa change main app
  • Technology diversity - Gunakan different language/tool per container
  • Shared resources - Efficient resource sharing via localhost
  • Simplified deployment - Deploy related component together
  • Enhanced functionality - Add capability tanpa modify main app

Tanpa multi-container Pod, kalian harus:

  • Build semua functionality ke satu container
  • Gunakan complex inter-Pod networking
  • Duplicate sidecar logic across application
  • Manage separate deployment untuk related component

Container Communication

Container dalam Pod bisa communicate dengan beberapa cara:

Via Localhost

Container share network namespace:

Kuberneteslocalhost-communication.yml
apiVersion: v1
kind: Pod
metadata:
    name: multi-container-pod
spec:
    containers:
        - name: web-app
          image: nginx:1.25
          ports:
              - containerPort: 80
        - name: log-agent
          image: busybox:1.36
          command:
              - sh
              - -c
              - while true; do wget -q -O- http://localhost:80; sleep 5; done

Container log-agent access web-app via localhost:80.

Via Shared Volume

Container bisa share file melalui volume:

Kubernetesshared-volume.yml
apiVersion: v1
kind: Pod
metadata:
    name: shared-volume-pod
spec:
    containers:
        - name: writer
          image: busybox:1.36
          command:
              - sh
              - -c
              - while true; do date >> /data/log.txt; sleep 5; done
          volumeMounts:
              - name: shared-data
                mountPath: /data
        - name: reader
          image: busybox:1.36
          command:
              - sh
              - -c
              - tail -f /data/log.txt
          volumeMounts:
              - name: shared-data
                mountPath: /data
    volumes:
        - name: shared-data
          emptyDir: {}

Kedua container read/write ke volume yang sama.

Via Environment Variable

Share configuration melalui environment:

Kubernetesshared-env.yml
apiVersion: v1
kind: Pod
metadata:
    name: env-pod
spec:
    containers:
        - name: app
          image: nginx:1.25
          env:
              - name: APP_PORT
                value: "80"
        - name: sidecar
          image: busybox:1.36
          env:
              - name: APP_PORT
                value: "80"

Multi-Container Pod Pattern

Ada tiga common design pattern untuk multi-container Pod:

1. Sidecar Pattern

Sidecar container enhance atau extend functionality main container.

Use case:

  • Log shipping dan aggregation
  • Monitoring dan metrics collection
  • Configuration synchronization
  • Security proxy
  • Service mesh proxy

Contoh: Log Sidecar

Kubernetessidecar-logging.yml
apiVersion: v1
kind: Pod
metadata:
    name: web-with-logging
spec:
    containers:
        # Main application container
        - name: web-app
          image: nginx:1.25
          ports:
              - containerPort: 80
          volumeMounts:
              - name: logs
                mountPath: /var/log/nginx
        # Sidecar container untuk log shipping
        - name: log-shipper
          image: fluent/fluentd:v1.16
          volumeMounts:
              - name: logs
                mountPath: /var/log/nginx
          env:
              - name: FLUENTD_CONF
                value: fluent.conf
    volumes:
        - name: logs
          emptyDir: {}

Log-shipper sidecar read log dari shared volume dan ship ke central logging system.

Contoh: Metrics Sidecar

Kubernetessidecar-metrics.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-metrics
spec:
    containers:
        # Main application
        - name: application
          image: myapp:latest
          ports:
              - containerPort: 8080
        # Metrics exporter sidecar
        - name: metrics-exporter
          image: prom/nginx-exporter:latest
          ports:
              - containerPort: 9113
          args:
              - -nginx.scrape-uri=http://localhost:8080/metrics

2. Ambassador Pattern

Ambassador container proxy network connection untuk main container.

Use case:

  • Database connection pooling
  • Circuit breaking
  • Retry logic
  • Protocol translation
  • Service discovery

Contoh: Database Ambassador

Kubernetesambassador-database.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-db-ambassador
spec:
    containers:
        # Main application
        - name: application
          image: myapp:latest
          env:
              - name: DATABASE_HOST
                value: "localhost"
              - name: DATABASE_PORT
                value: "5432"
        # Ambassador untuk database connection
        - name: db-ambassador
          image: haproxy:2.8
          ports:
              - containerPort: 5432
          volumeMounts:
              - name: config
                mountPath: /usr/local/etc/haproxy
    volumes:
        - name: config
          configMap:
              name: haproxy-config

Application connect ke localhost:5432, dan ambassador proxy ke actual database dengan connection pooling dan retry logic.

Contoh: Service Mesh Ambassador

Kubernetesambassador-service-mesh.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-proxy
spec:
    containers:
        # Main application
        - name: application
          image: myapp:latest
          ports:
              - containerPort: 8080
        # Envoy proxy ambassador
        - name: envoy-proxy
          image: envoyproxy/envoy:v1.28
          ports:
              - containerPort: 9901
          volumeMounts:
              - name: envoy-config
                mountPath: /etc/envoy
    volumes:
        - name: envoy-config
          configMap:
              name: envoy-configuration

3. Adapter Pattern

Adapter container transform output main container untuk match external requirement.

Use case:

  • Log format standardization
  • Metrics format conversion
  • Data transformation
  • Protocol adaptation

Contoh: Log Adapter

Kubernetesadapter-logging.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-log-adapter
spec:
    containers:
        # Main application (write custom log format)
        - name: application
          image: legacy-app:latest
          volumeMounts:
              - name: logs
                mountPath: /var/log/app
        # Adapter untuk convert log ke standard format
        - name: log-adapter
          image: log-transformer:latest
          volumeMounts:
              - name: logs
                mountPath: /var/log/app
              - name: transformed-logs
                mountPath: /var/log/transformed
          command:
              - sh
              - -c
              - |
                  while true; do
                    if [ -f /var/log/app/app.log ]; then
                      tail -f /var/log/app/app.log | \
                      awk '{print "{\"timestamp\":\""$1"\",\"level\":\""$2"\",\"message\":\""$3"\"}"}' \
                      > /var/log/transformed/app.json
                    fi
                    sleep 1
                  done
    volumes:
        - name: logs
          emptyDir: {}
        - name: transformed-logs
          emptyDir: {}

Contoh: Metrics Adapter

Kubernetesadapter-metrics.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-metrics-adapter
spec:
    containers:
        # Application expose custom metrics
        - name: application
          image: myapp:latest
          ports:
              - containerPort: 8080
        # Adapter untuk convert ke Prometheus format
        - name: metrics-adapter
          image: metrics-converter:latest
          ports:
              - containerPort: 9090
          env:
              - name: SOURCE_METRICS_URL
                value: "http://localhost:8080/stats"
              - name: TARGET_FORMAT
                value: "prometheus"

Init Container

Init container run sebelum main container dan harus complete successfully.

Basic Init Container

Kubernetesinit-container.yml
apiVersion: v1
kind: Pod
metadata:
    name: pod-with-init
spec:
    initContainers:
        - name: init-setup
          image: busybox:1.36
          command:
              - sh
              - -c
              - |
                  echo "Initializing..."
                  sleep 5
                  echo "Setup complete" > /work-dir/ready.txt
          volumeMounts:
              - name: workdir
                mountPath: /work-dir
    containers:
        - name: main-app
          image: nginx:1.25
          volumeMounts:
              - name: workdir
                mountPath: /work-dir
    volumes:
        - name: workdir
          emptyDir: {}

Multiple Init Container

Init container run sequentially:

Kubernetesmultiple-init-containers.yml
apiVersion: v1
kind: Pod
metadata:
    name: multi-init-pod
spec:
    initContainers:
        # First init container
        - name: download-config
          image: curlimages/curl:latest
          command:
              - sh
              - -c
              - curl -o /config/app.conf https://config-server/app.conf
          volumeMounts:
              - name: config
                mountPath: /config
        # Second init container (run setelah first complete)
        - name: validate-config
          image: busybox:1.36
          command:
              - sh
              - -c
              - |
                  if [ -f /config/app.conf ]; then
                    echo "Config valid"
                  else
                    echo "Config missing" && exit 1
                  fi
          volumeMounts:
              - name: config
                mountPath: /config
    containers:
        - name: application
          image: myapp:latest
          volumeMounts:
              - name: config
                mountPath: /etc/app
    volumes:
        - name: config
          emptyDir: {}

Contoh Praktis

Contoh 1: Web Application dengan Logging dan Metrics

Kubernetesweb-app-complete.yml
apiVersion: v1
kind: Pod
metadata:
    name: web-app-complete
    labels:
        app: web
spec:
    containers:
        # Main web application
        - name: nginx
          image: nginx:1.25
          ports:
              - containerPort: 80
          volumeMounts:
              - name: logs
                mountPath: /var/log/nginx
              - name: html
                mountPath: /usr/share/nginx/html
          resources:
              requests:
                  memory: "128Mi"
                  cpu: "100m"
              limits:
                  memory: "256Mi"
                  cpu: "200m"
        # Log shipping sidecar
        - name: log-shipper
          image: fluent/fluentd:v1.16
          volumeMounts:
              - name: logs
                mountPath: /var/log/nginx
          resources:
              requests:
                  memory: "64Mi"
                  cpu: "50m"
              limits:
                  memory: "128Mi"
                  cpu: "100m"
        # Metrics exporter sidecar
        - name: nginx-exporter
          image: nginx/nginx-prometheus-exporter:latest
          ports:
              - containerPort: 9113
          args:
              - -nginx.scrape-uri=http://localhost:80/stub_status
          resources:
              requests:
                  memory: "32Mi"
                  cpu: "25m"
              limits:
                  memory: "64Mi"
                  cpu: "50m"
    volumes:
        - name: logs
          emptyDir: {}
        - name: html
          emptyDir: {}

Contoh 2: Application dengan Database Proxy

Kubernetesapp-with-db-proxy.yml
apiVersion: v1
kind: Pod
metadata:
    name: app-with-db-proxy
spec:
    containers:
        # Main application
        - name: application
          image: myapp:latest
          env:
              - name: DB_HOST
                value: "127.0.0.1"
              - name: DB_PORT
                value: "5432"
          ports:
              - containerPort: 8080
        # Cloud SQL Proxy sidecar
        - name: cloud-sql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:latest
          command:
              - /cloud_sql_proxy
              - -instances=project:region:instance=tcp:5432
          securityContext:
              runAsNonRoot: true

Contoh 3: Git Sync Sidecar

Kubernetesgit-sync-sidecar.yml
apiVersion: v1
kind: Pod
metadata:
    name: web-with-git-sync
spec:
    initContainers:
        # Clone repository initially
        - name: git-clone
          image: alpine/git:latest
          command:
              - git
              - clone
              - https://github.com/user/repo.git
              - /git
          volumeMounts:
              - name: git-repo
                mountPath: /git
    containers:
        # Web server serving git content
        - name: nginx
          image: nginx:1.25
          ports:
              - containerPort: 80
          volumeMounts:
              - name: git-repo
                mountPath: /usr/share/nginx/html
        # Git sync sidecar
        - name: git-sync
          image: k8s.gcr.io/git-sync:v3.6.3
          env:
              - name: GIT_SYNC_REPO
                value: "https://github.com/user/repo.git"
              - name: GIT_SYNC_DEST
                value: "repo"
              - name: GIT_SYNC_WAIT
                value: "60"
          volumeMounts:
              - name: git-repo
                mountPath: /tmp/git
    volumes:
        - name: git-repo
          emptyDir: {}

Container Lifecycle

Memahami container startup order dan dependency:

Startup Order

  1. Init container run sequentially (satu setelah yang lain)
  2. Main container start in parallel setelah semua init container complete
  3. Readiness probe determine kapan container ready
  4. Liveness probe monitor container health

Contoh dengan Lifecycle Hook

Kuberneteslifecycle-hooks.yml
apiVersion: v1
kind: Pod
metadata:
    name: lifecycle-pod
spec:
    containers:
        - name: main-app
          image: nginx:1.25
          lifecycle:
              postStart:
                  exec:
                      command:
                          - sh
                          - -c
                          - echo "Container started" > /usr/share/message
              preStop:
                  exec:
                      command:
                          - sh
                          - -c
                          - nginx -s quit; sleep 10
        - name: sidecar
          image: busybox:1.36
          command:
              - sh
              - -c
              - while true; do sleep 3600; done

Resource Management

Set resource untuk setiap container:

Kubernetesmulti-container-resources.yml
apiVersion: v1
kind: Pod
metadata:
    name: resource-managed-pod
spec:
    containers:
        - name: main-app
          image: myapp:latest
          resources:
              requests:
                  memory: "512Mi"
                  cpu: "500m"
              limits:
                  memory: "1Gi"
                  cpu: "1000m"
        - name: sidecar
          image: sidecar:latest
          resources:
              requests:
                  memory: "128Mi"
                  cpu: "100m"
              limits:
                  memory: "256Mi"
                  cpu: "200m"

Total Pod resource = sum dari semua container resource.

Kesalahan Umum dan Pitfall

Kesalahan 1: Port Conflict

Problem: Multiple container trying gunakan port yang sama.

Solusi: Gunakan different port untuk setiap container:

Kubernetesyml
containers:
    - name: app1
      ports:
          - containerPort: 8080
    - name: app2
      ports:
          - containerPort: 8081  # Different port

Kesalahan 2: Tidak Share Volume

Problem: Container tidak bisa access shared data.

Solusi: Mount volume yang sama di kedua container:

Kubernetesyml
volumeMounts:
    - name: shared-data
      mountPath: /data

Kesalahan 3: Tight Coupling

Problem: Container terlalu dependent on each other.

Solusi: Gunakan multi-container Pod hanya ketika container truly need co-located.

Kesalahan 4: Resource Starvation

Problem: Satu container consuming semua resource.

Solusi: Set resource limit untuk setiap container.

Kesalahan 5: Complex Debugging

Problem: Hard troubleshoot multi-container issue.

Solusi: Check log untuk setiap container:

Kubernetesbash
sudo kubectl logs <pod-name> -c <container-name>

Best Practice

Gunakan Multi-Container Pod Sparingly

Hanya ketika container harus co-located:

Kubernetesyml
# Bagus: Tightly coupled (app + log shipper)
# Buruk: Loosely coupled (frontend + backend)

Set Resource Limit

Selalu define resource untuk setiap container:

Kubernetesyml
resources:
    requests:
        memory: "128Mi"
        cpu: "100m"
    limits:
        memory: "256Mi"
        cpu: "200m"

Gunakan Init Container untuk Setup

Separate initialization dari runtime:

Kubernetesyml
initContainers:
    - name: setup
      # Setup task
containers:
    - name: app
      # Main application

Implement Health Check

Add probe untuk setiap container:

Kubernetesyml
livenessProbe:
    httpGet:
        path: /health
        port: 8080
readinessProbe:
    httpGet:
        path: /ready
        port: 8080

Gunakan Meaningful Container Name

Pilih descriptive name:

Kubernetesyml
# Bagus
- name: web-server
- name: log-shipper
- name: metrics-exporter
 
# Hindari
- name: container1
- name: sidecar
- name: c2

Melihat Detail Multi-Container Pod

Get Pod dengan Container Info

Kubernetesbash
sudo kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].name}'

View Log dari Specific Container

Kubernetesbash
sudo kubectl logs <pod-name> -c <container-name>

Execute Command di Specific Container

Kubernetesbash
sudo kubectl exec -it <pod-name> -c <container-name> -- sh

Describe Pod

Kubernetesbash
sudo kubectl describe pod <pod-name>

Show semua container, init container, dan state mereka.

Penutup

Pada episode 20 ini, kita telah membahas Multi-Container Pod di Kubernetes. Kita sudah belajar tentang common design pattern, container communication, dan best practice untuk building modular application.

Key takeaway:

  • Multi-container Pod run multiple container sebagai single unit
  • Container share network namespace dan storage volume
  • Tiga main pattern: Sidecar, Ambassador, Adapter
  • Init container run sebelum main container
  • Container communicate via localhost dan shared volume
  • Setiap container harus punya resource limit
  • Gunakan multi-container Pod untuk tightly coupled component saja
  • Sidecar enhance main container (logging, monitoring)
  • Ambassador proxy connection (database, service mesh)
  • Adapter transform output (log format, metrics)
  • Selalu set resource request dan limit per container

Multi-container Pod enable powerful architectural pattern di Kubernetes. Dengan memahami pattern ini, kalian bisa build modular, maintainable application dengan clear separation of concern sambil maintain tight integration dimana needed.

Bagaimana, makin jelas kan tentang Multi-Container Pod di Kubernetes? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!


Related Posts