1. Masalah yang Diselesaikan Service Mesh
Pola ketahanan di modul sebelumnya — retry, timeout, circuit breaker — harus diterapkan di setiap layanan. Bila layanan Anda ditulis dalam Go, Java, Python, dan Node.js, Anda menulis dan memelihara logika yang sama empat kali, dengan perilaku yang tidak pernah benar-benar sama.
TANPA mesh: DENGAN mesh: ┌─────────────┐ ┌─────────────┐ │ Aplikasi │ │ Aplikasi │ ← hanya logika bisnis │ + retry │ ├─────────────┤ │ + timeout │ │ Sidecar │ ← retry, timeout, mTLS, │ + mTLS │ │ Proxy │ circuit breaker, metrik │ + metrik │ └─────────────┘ └─────────────┘ Ditulis ulang di tiap bahasa Satu konfigurasi untuk semua bahasa
2. Data Plane dan Control Plane
- Data plane — kumpulan sidecar proxy yang benar-benar meneruskan lalu lintas. Semua paket melewatinya.
- Control plane — otak yang menyebarkan konfigurasi, sertifikat, dan kebijakan ke seluruh proxy.
- mTLS otomatis antar layanan, lengkap dengan rotasi sertifikat.
- Retry, timeout, circuit breaker yang seragam lintas bahasa.
- Traffic splitting berbasis persentase atau header — fondasi canary.
- Metrik emas otomatis: rate, error, duration untuk setiap pasangan layanan.
- Kebijakan otorisasi: "hanya Layanan Pesanan yang boleh memanggil Layanan Pembayaran".
- Injeksi kesalahan untuk chaos engineering, tanpa menyentuh kode.
3. Harga yang Harus Dibayar
- Latensi bertambah — setiap panggilan melewati dua proxy tambahan.
- Sumber daya bertambah — satu sidecar per pod berarti tambahan CPU dan memori dikalikan jumlah pod.
- Debugging jadi lebih sulit — kini ada lapisan tambahan yang bisa menjadi penyebab masalah.
- Kurva belajar curam — model konfigurasinya sendiri sudah merupakan bidang keahlian tersendiri.
- Beban upgrade — memutakhirkan mesh di kluster produksi adalah operasi yang menegangkan.
Untuk di bawah 10 layanan, mesh hampir selalu berlebihan. Pustaka ketahanan di dalam aplikasi sudah cukup. Pertimbangkan mesh ketika jumlah layanan sudah puluhan dan ditulis dalam banyak bahasa berbeda, atau ketika mTLS antar layanan menjadi kewajiban kepatuhan.
4. Fondasi Kubernetes untuk Microservices
apiVersion: apps/v1
kind: Deployment
metadata:
name: layanan-pesanan
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # jangan pernah turun di bawah kapasitas
template:
spec:
containers:
- name: pesanan
image: registry.internal/pesanan:2.4.0 # JANGAN pakai :latest
ports:
- containerPort: 8080
resources:
requests: # dipakai penjadwal untuk menempatkan pod
cpu: 100m
memory: 128Mi
limits: # batas keras
memory: 512Mi
# CPU limit sengaja TIDAK disetel -- lihat catatan di bawah
livenessProbe: # "haruskah pod ini dibunuh & diganti?"
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe: # "bolehkah pod ini menerima trafik?"
httpGet: { path: /readyz, port: 8080 }
periodSeconds: 5
startupProbe: # untuk aplikasi yang lambat menyala
httpGet: { path: /healthz, port: 8080 }
failureThreshold: 30
periodSeconds: 5
Liveness menjawab "apakah proses ini rusak dan perlu dibunuh?". Readiness menjawab "apakah pod ini siap menerima trafik sekarang?".
Kesalahan yang sangat merusak: menjadikan liveness probe memeriksa dependensi eksternal seperti database. Ketika database sedang lambat, seluruh pod gagal liveness dan Kubernetes membunuh semuanya sekaligus — mengubah gangguan kecil menjadi pemadaman total. Liveness hanya boleh memeriksa proses itu sendiri. Ketergantungan eksternal diperiksa oleh readiness, yang cukup menghentikan trafik tanpa membunuh pod.
Menyetel limits.cpu menyebabkan CFS throttling: kontainer dipaksa berhenti meski node masih punya CPU menganggur, sehingga latensi p99 melonjak. Praktik yang banyak dianut sekarang: selalu setel memory limit (karena memori tidak bisa dikompres — kehabisan berarti OOM kill), tetapi hanya setel CPU request tanpa limit, dan andalkan request untuk pembagian yang adil.
5. Mematikan Pod dengan Anggun
Kubernetes menghapus pod: 1. Pod ditandai Terminating, dikeluarkan dari daftar endpoint Service 2. Hook preStop dijalankan (bila ada) 3. Sinyal SIGTERM dikirim ke proses 4. Menunggu terminationGracePeriodSeconds (default 30 detik) 5. SIGKILL -- dipaksa mati
lifecycle:
preStop:
exec:
command: ["sleep", "5"] # beri waktu load balancer menyadari
terminationGracePeriodSeconds: 45
Penghapusan endpoint dan pengiriman SIGTERM terjadi hampir bersamaan. Bila aplikasi langsung berhenti menerima koneksi, permintaan yang sudah terlanjur diarahkan ke pod itu akan gagal. Jeda preStop beberapa detik memberi waktu agar semua load balancer memperbarui daftar endpoint sebelum aplikasi benar-benar berhenti. Aplikasi juga harus menangani SIGTERM: berhenti menerima koneksi baru, selesaikan yang sedang berjalan, lalu keluar.
6. Strategi Rilis
| Strategi | Cara kerja | Pertimbangan |
|---|---|---|
| Rolling update | Ganti pod bertahap | Bawaan Kubernetes; dua versi berjalan bersamaan |
| Blue-green | Siapkan lingkungan baru utuh, alihkan trafik sekaligus | Mundur seketika; butuh sumber daya dua kali lipat |
| Canary | Alihkan 5% trafik, amati, lalu naikkan bertahap | Paling aman; perlu metrik yang baik untuk menilai |
| Shadow | Gandakan trafik ke versi baru, responsnya dibuang | Nol risiko bagi pengguna; hati-hati efek samping ganda |
rute:
- tujuan: layanan-pesanan
subset: v2.4.0
bobot: 5 # 5% ke versi baru
- tujuan: layanan-pesanan
subset: v2.3.0
bobot: 95
# Naikkan bertahap: 5% -> 25% -> 50% -> 100%
# Pada tiap tahap, bandingkan tingkat error & latensi p99
# antara kedua subset. Bila memburuk, kembalikan ke 0%.
Pada rolling update maupun canary, versi lama dan baru selalu berjalan bersamaan untuk sementara. Konsekuensinya wajib dipikirkan: perubahan skema database harus kompatibel ke belakang (tambah kolom dulu, isi datanya, baru pakai — jangan pernah menghapus kolom di rilis yang sama); format pesan di broker harus bisa dibaca kedua versi; dan API tidak boleh menghapus field yang masih dipakai versi lama.
7. Menjaga Ketersediaan Saat Pemeliharaan Kluster
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: pdb-pesanan
spec:
minAvailable: 2 # atau: maxUnavailable: 1
selector:
matchLabels:
app: layanan-pesanan
Tanpa PDB, satu perintah kubectl drain saat memutakhirkan node bisa mengusir semua replika sekaligus dan mematikan layanan Anda. PDB memberi tahu Kubernetes berapa banyak pod yang wajib tetap hidup selama gangguan terencana.
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: layanan-pesanan
Tiga replika yang kebetulan dijadwalkan di satu node yang sama tidak memberi ketersediaan apa pun. Aturan penyebaran memastikan replika tersebar ke node dan zona yang berbeda.