Single & Multi Container #

Sebagian besar Pod di Kubernetes hanya berisi satu container — dan itu adalah default yang benar untuk mayoritas kasus. Tapi Kubernetes memang mendukung Pod dengan beberapa container, dan ada skenario spesifik di mana pola ini tidak hanya valid tapi juga tepat. Memahami kapan menggunakan mana, dan mengapa, adalah keputusan arsitektur yang punya dampak nyata terhadap deployability dan operasional aplikasi.

Prinsip Dasar #

Sebelum membahas kapan memakai multi-container, ada satu prinsip yang harus selalu menjadi landasan keputusan:

Container dalam satu Pod adalah satu unit deployment. Mereka selalu dijadwalkan ke node yang sama, selalu dimulai dan dihentikan bersama, dan tidak bisa di-scale secara independen. Jika dua komponen perlu lifecycle yang berbeda atau perlu di-scale secara terpisah, mereka harus berada di Pod yang berbeda.

Pertanyaan kunci sebelum menggabungkan container dalam satu Pod:

"Apakah container ini punya lifecycle yang sama?"
  → Kalau salah satunya mati, apakah yang lain juga harus mati?
  → Kalau scale-up, apakah keduanya perlu di-scale?

Jika TIDAK → pisahkan ke Pod yang berbeda
Jika YA   → mungkin valid sebagai multi-container Pod

Single Container Pod #

Ini adalah pola yang digunakan untuk hampir semua workload: satu Pod, satu container, satu concern.

apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api
spec:
  containers:
  - name: api
    image: my-api:v2
    ports:
    - containerPort: 8080
    resources:
      requests:
        cpu: "250m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "256Mi"

Kesederhanaan ini punya keuntungan nyata: mudah di-debug, mudah di-scale, dan lifecycle yang jelas. Ketika Pod ini crash, kamu langsung tahu container mana yang bermasalah.


Apa yang Dibagi Container dalam Satu Pod #

Ketika ada lebih dari satu container dalam Pod, mereka berbagi beberapa hal secara otomatis — dan ini yang membuat multi-container Pod bisa berguna.

Shared Network Namespace #

Semua container dalam satu Pod berbagi network interface yang sama. Mereka punya IP address yang sama dan bisa berkomunikasi satu sama lain via localhost.

Pod dengan dua container:
  Container A (api)         → listen di localhost:8080
  Container B (metrics)     → bisa akses api di localhost:8080
                            → expose metrics di localhost:9090

Dari luar Pod:
  IP Pod: 192.168.1.42
  Akses api:     192.168.1.42:8080
  Akses metrics: 192.168.1.42:9090

Konsekuensi penting: dua container dalam satu Pod tidak boleh menggunakan port yang sama — mereka berbagi network namespace, jadi port conflict akan terjadi.

Shared Volumes #

Container dalam satu Pod bisa mount volume yang sama, memungkinkan berbagi file:

spec:
  volumes:
  - name: shared-data
    emptyDir: {}          # volume sementara, hilang saat Pod mati

  containers:
  - name: app
    image: my-app:v1
    volumeMounts:
    - name: shared-data
      mountPath: /var/app/output    # app menulis file di sini

  - name: log-shipper
    image: fluentd:v1
    volumeMounts:
    - name: shared-data
      mountPath: /var/log/app       # log-shipper membaca dari sini
      readOnly: true

Pola Multi-Container yang Valid #

Ada tiga pola klasik yang sudah diakui komunitas Kubernetes sebagai penggunaan multi-container yang tepat:

Sidecar Pattern #

Container sidecar memperluas atau meningkatkan fungsi container utama tanpa memodifikasi container utama itu sendiri. Sidecar berjalan berdampingan dan biasanya mengakses network atau storage yang sama.

spec:
  containers:
  - name: api                          # container utama
    image: my-api:v2

  - name: envoy-proxy                  # sidecar: service mesh proxy
    image: envoy:v1.28
    # Intercept semua traffic masuk dan keluar dari api
    # Tambahkan mTLS, circuit breaker, observability
    # Tanpa mengubah kode api sama sekali

Contoh penggunaan sidecar yang umum di produksi:

  • Service mesh proxy (Envoy, Linkerd) — menambahkan mTLS, tracing, circuit breaking
  • Log forwarder — mengumpulkan log dari volume dan mengirim ke sistem terpusat
  • Metrics exporter — membaca metrics dari aplikasi dan mengeksposnya ke Prometheus
  • Config watcher — memantau perubahan konfigurasi dan me-reload aplikasi

Ambassador Pattern #

Ambassador adalah container yang bertindak sebagai proxy untuk komunikasi keluar dari container utama. Ia menyederhanakan akses ke layanan eksternal.

Tanpa ambassador:
  app-container → langsung terhubung ke database cluster
               → harus tahu logic connection pooling, retry, failover

Dengan ambassador:
  app-container → localhost:5432 (ambassador)
                              → ambassador menangani connection pooling,
                                retry logic, dan routing ke database yang benar
  containers:
  - name: app
    image: my-app:v1
    env:
    - name: DB_HOST
      value: "localhost"    # cukup konek ke localhost
    - name: DB_PORT
      value: "5432"

  - name: db-ambassador     # proxy ke database cluster
    image: pgbouncer:1.21
    # menangani connection pooling ke PostgreSQL cluster

Adapter Pattern #

Adapter mentransformasi output container utama menjadi format yang dibutuhkan sistem lain. Berguna ketika kamu tidak bisa memodifikasi container utama (misalnya third-party image) tapi perlu mengubah format output-nya.

Contoh: monitoring system butuh format Prometheus,
tapi aplikasi lama hanya mengeluarkan format custom

app-container → /tmp/metrics.json (format custom)
adapter       → baca /tmp/metrics.json, transform ke format Prometheus
              → expose di :9090/metrics

Anti-Pattern: Apa yang Bukan Multi-Container #

Lebih penting dari tahu kapan pakai multi-container adalah tahu kapan tidak:

ANTI-PATTERN: gabungkan dua microservice dalam satu Pod

spec:
  containers:
  - name: user-service
    image: user-service:v1
  - name: order-service      # ← SALAH
    image: order-service:v1

Masalah:
  ✗ Tidak bisa scale user-service dan order-service secara independen
  ✗ Update salah satu → restart keduanya → downtime tidak perlu
  ✗ Crash salah satu → Pod restart → keduanya mati
  ✗ Log dan monitoring campur aduk

BENAR: pisahkan ke Deployment yang berbeda
  Deployment: user-service (replicas: 5)
  Deployment: order-service (replicas: 2)
  → Scale, deploy, dan monitor secara independen
ANTI-PATTERN: satukan aplikasi dan database dalam satu Pod

spec:
  containers:
  - name: app
    image: my-app:v1
  - name: database       # ← SALAH
    image: postgres:15

Masalah:
  ✗ Database butuh persistent storage, tapi emptyDir hilang saat Pod mati
  ✗ Scale app → scale database juga (tidak masuk akal)
  ✗ Database crash → seluruh Pod restart → downtime app
  ✗ Tidak bisa share database ini dengan Pod/service lain

BENAR: database sebagai StatefulSet terpisah

Urutan Startup dan Dependency #

Ketika Pod punya beberapa container, Kubernetes menjalankan semua container secara bersamaan — tidak ada jaminan urutan startup antar container biasa.

ANTI-PATTERN: mengandalkan container lain sudah siap

spec:
  containers:
  - name: app           # startup langsung, konek ke proxy
    image: my-app:v1
  - name: proxy         # mungkin belum siap saat app startup
    image: envoy:v1.28

Masalah: app bisa gagal konek ke proxy karena proxy belum ready

Untuk dependency startup yang ketat, gunakan init container:

  initContainers:
  - name: wait-for-proxy          # berjalan lebih dulu, harus selesai sukses
    image: busybox
    command:
    - sh
    - -c
    - "until nc -z localhost 15000; do sleep 1; done"

  containers:
  - name: app                     # baru jalan setelah init container selesai
    image: my-app:v1
  - name: proxy
    image: envoy:v1.28
Kubernetes 1.29 ke atas mendukung sidecar containers — container yang ditandai sebagai sidecar dimulai sebelum container utama dan diberi perlakuan khusus dalam lifecycle Pod. Ini menyelesaikan masalah urutan startup tanpa perlu init container untuk kasus sidecar.

Ringkasan #

  • Single container adalah default yang benar — untuk mayoritas workload; lebih mudah di-debug, di-scale, dan dipahami.
  • Multi-container Pod berbagi network dan storage — semua container punya IP yang sama dan bisa berkomunikasi via localhost; volume bisa di-mount bersama.
  • Tiga pola valid: sidecar, ambassador, adapter — sidecar memperluas fungsi, ambassador mem-proxy akses keluar, adapter mentransform output.
  • Lifecycle yang sama adalah syarat utama — jika dua komponen perlu di-scale atau di-deploy secara independen, pisahkan ke Pod yang berbeda.
  • Tidak ada jaminan urutan startup — gunakan init container jika container utama bergantung pada container lain yang harus siap lebih dulu.
  • Microservice berbeda harus di Pod berbeda — menggabungkan dua service dalam satu Pod menghilangkan seluruh keuntungan microservice architecture.

← Sebelumnya: Anatomi Pod   Berikutnya: Init Container →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact