Service mesh adalah lapisan infrastruktur yang mengurus komunikasi antar service (timeout, retry, enkripsi, dan pencatatan) tanpa mengubah kode aplikasinya. Bayangin gedung kantor dengan banyak divisi, dan tiap divisi punya resepsionis sendiri di pintunya. Semua surat keluar-masuk lewat resepsionis: dia mengecek kartu identitas pengirim, mengirim ulang surat yang mental, berhenti menunggu balasan yang kelamaan, dan mencatat semuanya di buku tamu. Staf divisi cukup menulis isi suratnya. Buku aturan untuk semua resepsionis datang dari satu kantor pusat. Resepsionis itu sidecar proxy, kantor pusatnya control plane.
🧪 Tebakan cepat:
bayar-2, satu dari tiga pod servicebayar, menjawab503untuk setiap request, jadi sepertiga pembayaran gagal. Bisakah error-nya jadi nol tanpa mengubah satu baris pun di kodeorder? Tebak dulu, jawabannya ada di bagian "Retry dan timeout pindah ke config".
TiketNada sekarang punya empat service
TiketNada, aplikasi tiket konser kita, sudah dipecah jadi microservices yang jalan di Kubernetes (dasarnya ada di lesson Kubernetes Basics dan Microservices Architecture di track DevOps):
| Service | Bahasa | Pod | Tugasnya |
|---|---|---|---|
order |
Node.js | 2 | menerima pesanan |
bayar |
Go | 3 | memotong kartu |
tiket |
Java | 2 | memegang kursi |
notif |
Python | 1 | mengirim e-tiket |
Tiap panggilan antar service butuh hal yang sama: timeout (batas menunggu), retry (coba lagi kalau gagalnya sementara), TLS (enkripsi, supaya isi request gak bisa dibaca orang yang menyadap jaringan), dan metrics (berapa request, berapa yang error, berapa lama). Kalau semuanya ditulis di kode, itu 4 fitur × 4 bahasa = 16 implementasi, dengan library berbeda dan bug berbeda. Mengubah aturan retry berarti mengubah dan men-deploy ulang empat service.
Sidecar: proxy di samping tiap service
Service mesh memindahkan urusan itu keluar dari kode. Tiap pod mendapat container tambahan, sidecar proxy, yang jalan di sebelah container aplikasinya. Aturan iptables di dalam pod membelokkan semua traffic masuk dan keluar ke proxy itu. Jadi saat order memanggil http://bayar:8080/charges dengan HTTP biasa, jalannya begini:
order → sidecar order → (mTLS) → sidecar bayar → bayar
ordertetap memanggilhttp://bayar:8080tanpa TLS dan tanpa retry. Kodenya gak tahu ada mesh.- Sidecar
ordermemilih podbayar, membungkus request dengan mTLS, menerapkan timeout dan retry, lalu mencatat metrics. - Sidecar
bayarmembuka enkripsinya, memeriksa siapa pengirimnya, lalu meneruskan request ke containerbayardi pod yang sama.
Semua sidecar bersama-sama disebut data plane: merekalah yang benar-benar memegang traffic. Yang mengatur mereka disebut control plane: dia membagikan aturan (route, retry, timeout) dan sertifikat ke setiap sidecar. Di Istio, control plane-nya bernama istiod dan sidecar-nya Envoy, proxy open-source buatan Lyft yang juga dipakai banyak mesh lain. Linkerd memakai proxy-nya sendiri yang ditulis dengan Rust. Menyalakan Istio di namespace TiketNada:
kubectl label namespace tiketnada istio-injection=enabled
kubectl rollout restart deployment -n tiketnada
- Baris pertama menyuruh Istio menyuntikkan sidecar ke setiap pod baru di namespace
tiketnada. - Baris kedua membuat ulang pod yang sudah jalan. Setelahnya
kubectl get podsmenampilkan2/2di kolom READY: container aplikasi plus containeristio-proxy.
Retry dan timeout pindah ke config
Aturan traffic ditulis sebagai YAML, lalu control plane mengirimnya ke semua sidecar. Ini aturan untuk semua panggilan ke bayar:
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: bayar
namespace: tiketnada
spec:
hosts:
- bayar
http:
- route:
- destination:
host: bayar
timeout: 2s
retries:
attempts: 2
perTryTimeout: 500ms
retryOn: gateway-error,connect-failure,refused-stream
timeout: 2s: total waktu menunggu satu panggilan, semua retry-nya ikut dihitung.attempts: 2: boleh mencoba ulang dua kali, jadi paling banyak 3 percobaan. Retry dikirim ke pod lain.perTryTimeout: 500ms: satu percobaan yang lewat 500 ms dipotong, lalu dicoba lagi.retryOn: kapan retry.gateway-errorartinya jawaban 502, 503, atau 504 (percobaan yang kenaperTryTimeouttermasuk);connect-failuredanrefused-streamartinya koneksinya gagal sebelum request terkirim.
Ini 6 pembayaran, pesanan #1001 sampai #1006, yang dibagi bergiliran ke tiga pod sementara bayar-2 menjawab 503. Angkanya model contoh: jaringan 10 ms sekali jalan, memotong kartu 120 ms, bayar-2 menolak dalam 2 ms, sidecar menambah 1 ms per panggilan, jeda sebelum retry 25 ms.
| Pesanan | Pod | Tanpa mesh | Dengan mesh |
|---|---|---|---|
| #1001, #1004 | bayar-1 | 201 dalam 140 ms | 201 dalam 141 ms |
| #1002, #1005 | bayar-2 | 503 dalam 22 ms | 503 (23 ms), jeda 25 ms, retry ke bayar-3: 201, total 189 ms |
| #1003, #1006 | bayar-3 | 201 dalam 140 ms | 201 dalam 141 ms |
Tanpa mesh, 2 dari 6 pembayaran gagal. Dengan mesh, 0 gagal, dan kode order gak berubah satu baris pun. Itu jawaban tebakannya: bisa, karena yang me-retry adalah sidecar, bukan kode. Kalau bayar-2 bukan error tapi macet 30 detik, perTryTimeout memotongnya di 500 ms dan retry ke pod sehat: selesai dalam 500 + 25 + 141 = 666 ms, bukan 30.020 ms.
Tanpa VirtualService pun, Istio sudah me-retry 2 kali secara default, tapi gak memasang timeout sama sekali. Timeout harus kamu tulis sendiri.
mTLS: tiap service punya KTP
Di HTTPS biasa, cuma server yang menunjukkan sertifikat. Di mTLS (mutual TLS), dua-duanya menunjukkan sertifikat. Control plane bertindak sebagai CA (pihak yang menerbitkan sertifikat): tiap pod dapat sertifikat berisi identitasnya, misalnya spiffe://cluster.local/ns/tiketnada/sa/order, yang berlaku 24 jam dan diganti otomatis. Hasilnya dua: isi request terenkripsi (penyadap cuma melihat byte acak), dan bayar tahu pasti pemanggilnya order, bukan pod lain yang menyamar.
Istio mulai di mode PERMISSIVE: sidecar ke sidecar sudah mTLS, tapi request polos dari pod tanpa sidecar masih diterima, supaya pemasangan mesh gak memutus apa-apa. Setelah semua pod punya sidecar, kunci dengan STRICT:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: tiketnada
spec:
mtls:
mode: STRICT
Lalu tes dari pod debug yang gak punya sidecar:
$ kubectl exec debug -n tiketnada -- curl -s -o /dev/null -w "%{http_code}\n" http://bayar:8080/healthz
000
command terminated with exit code 56
000artinya curl gak menerima jawaban HTTP apa pun. Exit code 56 artinya koneksinya diputus di tengah: sidecarbayarmenolak koneksi yang gak membawa sertifikat.- Sebelum STRICT, perintah yang sama mencetak
200: siapa pun di dalam cluster bisa memanggilbayar.
Mau lebih ketat lagi, misalnya "cuma order yang boleh memanggil bayar"? Itu tugas AuthorizationPolicy, yang memakai identitas dari sertifikat tadi.
Harga sebuah mesh
Mesh gak gratis. Dengan angka model contoh untuk 8 pod TiketNada:
- 8 sidecar tambahan × 50 MB RAM = 400 MB, ditambah control plane-nya sendiri.
- Tiap panggilan lewat dua sidecar: tambah sekitar 1 ms.
- Satu sistem lagi yang harus di-upgrade, dimonitor, dan dipahami tim. Saat ada error, tempat mencarinya bertambah: kode, sidecar, atau YAML-nya?
Istio sekarang juga punya ambient mode: tanpa sidecar per pod. mTLS diurus satu proxy per node (ztunnel), dan proxy untuk retry dan routing (waypoint) dipasang cuma untuk service yang butuh. Lebih hemat, idenya sama: urusan jaringan keluar dari kode.
🎯 Kapan pakai mesh, kapan jangan
- Monolith, atau 2-3 service dari satu tim dengan satu bahasa: belum perlu. Timeout dan retry di kode (lesson Resilience Patterns) plus HTTPS sudah cukup.
- Gak pakai Kubernetes: biasanya belum perlu. Mesh paling mudah dipasang di K8s.
- Belasan service dalam beberapa bahasa, dan semua panggilan wajib terenkripsi (misalnya karena audit keamanan atau data kartu): pakai mesh.
- Butuh canary (10% traffic ke versi baru) atau routing per header tanpa mengubah kode: mesh membantu.
- Tim platform-nya kecil? Mulai dari yang paling ringan: Linkerd, atau Istio ambient mode.
⚠️ Jebakan yang sering ketemu
- Retry di mesh untuk request yang gak idempotent. Sidecar gak tahu
POST /chargesmemotong kartu. Kalau percobaan pertama sudah memotong kartu tapi kenaperTryTimeout, retry-nya memotong lagi. Kirim Idempotency-Key (lesson Idempotency & Retry Safety), atau batasiretryOnke kegagalan koneksi. - Retry dobel. Kode sudah mencoba 3 kali, mesh juga 3 kali: satu klik bisa jadi 9 request ke service yang sedang kesulitan. Pilih satu tempat.
- Lupa timeout. Default Istio gak punya timeout. Tanpa
timeout, pod yang macet tetap ditunggu. - STRICT sebelum semua pod punya sidecar. Pod tanpa sidecar (job lama, monitoring) langsung putus. Mulai PERMISSIVE, cek, baru STRICT.
- Mesh untuk 3 service. Biaya RAM, latency, dan belajarnya lebih besar dari masalah yang diselesaikan.
TL;DR: Service mesh adalah lapisan infrastruktur yang menyuntikkan sidecar proxy (di Istio: Envoy) ke setiap pod, sehingga semua komunikasi antar service lewat proxy itu tanpa mengubah kode aplikasi. Sidecar-sidecar itu data plane; control plane (istiod) membagikan aturan dan sertifikat. Keuntungan utamanya: retry, timeout, mTLS, dan metrics ditangani konsisten di infrastruktur, gak bergantung pada bahasa atau library tiap service. Retry dan timeout ditulis di VirtualService (attempts, perTryTimeout, timeout); dengan bayar-2 error, pembayaran TiketNada gagal 0 dari 6 tanpa mengubah kode order. mTLS memberi tiap service identitas; mode STRICT menolak pod tanpa sidecar (000, exit code 56). Harganya RAM per sidecar, sedikit latency, dan satu sistem lagi untuk dirawat, jadi mesh untuk banyak service lintas bahasa, bukan untuk 3 service. Retry di mesh tetap butuh idempotency untuk request yang mengubah data.