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.