Kubernetes (disingkat K8s) adalah platform orkestrasi container: kamu menulis keadaan yang kamu mau, misalnya "3 container catatan-api dari image e4b2d90", dan Kubernetes terus-menerus menjaga supaya kenyataannya sama, di banyak server sekaligus. Bayangin supervisor minimarket dengan papan bertuliskan "3 kasir buka". Dia gak menunggu disuruh. Tiap beberapa menit dia melirik ke depan: kalau ada kasir yang pulang sakit, dia memanggil pengganti; kalau papannya diganti jadi "5 kasir", dia membuka dua kasir lagi; kasir baru yang masih menghitung uang di laci belum ditunjuk untuk melayani antrean. Satu hal jujur di awal: catatan-api yang sekarang jalan di satu VPS web-1 dengan Docker Compose belum butuh Kubernetes. Pelajaran ini menunjukkan cara kerjanya, dan di akhir kita bahas kapan dia layak dipakai.
🧪 Tebakan cepat: Deployment catatan-api punya
replicas: 3, dan ketiga pod-nya sehat. Kamu menghapus satu pod dengankubectl delete pod. Dua puluh detik kemudian,kubectl get podsmenampilkan berapa pod yang1/1, dan apakah nama pod yang kamu hapus muncul lagi? Tebak dulu, jawabannya ada di bagian "Hapus satu pod: reconcile loop beraksi".
Desired state: kamu menulis tujuan, bukan langkah
Dengan Compose, docker compose up -d menjalankan perintah sekali. Kalau container-nya mati karena server-nya mati, gak ada yang menyalakannya di server lain. Kubernetes bekerja beda: kamu menyimpan desired state (keadaan yang diinginkan) di cluster, lalu controller menjalankan reconcile loop tanpa henti:
- Lihat keadaan sebenarnya (actual state): ada berapa pod, image-nya apa, sehat atau gak.
- Bandingkan dengan desired state.
- Kalau beda, bertindak sekecil mungkin supaya sama. Kalau sama, diam.
| Desired | Actual | Yang dilakukan controller |
|---|---|---|
| 3 pod | 3 pod sehat | gak ada |
| 3 pod | 2 pod (satu dihapus) | buat 1 pod baru |
| 5 pod | 3 pod | buat 2 pod baru |
| 1 pod | 3 pod | hapus 2 pod, yang terbaru dulu |
Istilah dasarnya: cluster adalah sekumpulan server yang dikelola Kubernetes, dan tiap server disebut node. Di Lab, cluster-nya punya dua node, node-1 dan node-2.
Pod, Deployment, Service
Ini catatan-api dalam bahasa Kubernetes, sama dengan yang ada di Lab:
apiVersion: apps/v1
kind: Deployment
metadata:
name: catatan-api
spec:
replicas: 3 # desired state: 3 pod
selector:
matchLabels:
app: catatan-api
template: # cetakan tiap pod
metadata:
labels:
app: catatan-api
spec:
containers:
- name: app
image: ghcr.io/budi/catatan-api:e4b2d90
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: catatan-api
spec:
selector:
app: catatan-api # kirim request ke pod berlabel ini
ports:
- port: 80
targetPort: 3000
- Pod adalah unit terkecil yang dijalankan Kubernetes: satu container (atau beberapa yang selalu berdampingan) dengan satu IP. Pod itu sekali pakai: kalau mati, dia diganti pod baru, bukan dihidupkan lagi.
- Deployment menyimpan desired state:
replicas: 3dantemplate(image, port). Dia membuat ReplicaSet, controller yang menjaga jumlah pod tetap 3. - Service memberi satu alamat tetap (
catatan-api:80) dan membagi request ke pod yang labelnya cocok denganselectordan sedang ready. IP pod berganti terus, alamat Service tetap. ---memisahkan dua object di satu file. Terapkan dengankubectl apply -f catatan-api.yaml.
Hapus satu pod: reconcile loop beraksi
$ kubectl apply -f catatan-api.yaml
deployment.apps/catatan-api created
service/catatan-api created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
catatan-api-7c9d8b6f5d-fprwl 1/1 Running 0 4m
catatan-api-7c9d8b6f5d-jckhz 1/1 Running 0 4m
catatan-api-7c9d8b6f5d-kw44q 1/1 Running 0 4m
$ kubectl delete pod catatan-api-7c9d8b6f5d-fprwl
pod "catatan-api-7c9d8b6f5d-fprwl" deleted
- Nama pod = nama Deployment, lalu hash template (
7c9d8b6f5d, berubah kalau image berubah), lalu 5 karakter acak. READY 1/1artinya 1 dari 1 container siap menerima request.RESTARTSmenghitung berapa kali container-nya dinyalakan ulang.
Begitu pod fprwl mulai dihapus, ReplicaSet melihat 2 pod aktif padahal desired-nya 3, dan langsung membuat pod baru. Ini kubectl get pods untuk pod pengganti itu, persis seperti di Lab:
| Detik | STATUS pod baru | READY | Kenapa |
|---|---|---|---|
| 0 | Pending |
0/1 |
dibuat, menunggu ditempatkan di node |
| 5 | ContainerCreating |
0/1 |
node menarik image dan menyiapkan container |
| 10 | Running |
0/1 |
container jalan, app masih booting (10 detik) |
| 20 | Running |
1/1 |
readiness probe lulus, Service mulai mengirim request |
Jadi di detik 20, tiga pod kembali 1/1, tapi yang ketiga bernama baru, misalnya catatan-api-7c9d8b6f5d-nn9rd. Pod yang dihapus gak pernah kembali. Itu jawaban tebakannya. Kubernetes gak peduli pod yang mana, dia peduli jumlahnya sama dengan desired state.
Perintah lain yang kamu pakai di Lab:
$ kubectl scale deployment catatan-api --replicas=5
deployment.apps/catatan-api scaled
$ kubectl set image deployment/catatan-api app=ghcr.io/budi/catatan-api:a41c9e2
deployment.apps/catatan-api image updated
scalecuma mengubah angkareplicas. Sisanya dikerjakan reconcile loop: 2 pod baru dibuat.set imagemengganti image di template, jadi hash template-nya berubah (5f4b8d7c9b). Deployment melakukan rolling update: dengan 3 replicas, dia boleh menambah 1 pod di atas desired (maxSurge25%, dibulatkan ke atas) dan gak boleh mengurangi pod yang ready (maxUnavailable25%, dibulatkan ke bawah, jadi 0). Pod lama baru dimatikan setelah pod baru ready, satu per satu. Di Lab, rollout-nya selesai dalam sekitar satu menit dengan 0 request gagal.
Readiness vs liveness
Kubernetes tahu pod-nya sehat dari probe, request yang dikirim ke container secara berkala:
readinessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 5
| readinessProbe | livenessProbe | |
|---|---|---|
| Pertanyaannya | sudah siap menerima request? | masih hidup? |
| Kalau gagal | pod dikeluarkan dari Service, gak di-restart | setelah 3 kali gagal, container di-restart (RESTARTS naik) |
| Melindungi dari | request ke app yang masih booting | app yang hang selamanya |
- Tanpa readiness probe, pod dianggap ready begitu container-nya
Running, padahal app masih booting. Di Lab, rolling update dari keadaan awal tanpa readiness probe menggagalkan 51 request. initialDelaySeconds: 15memberi app waktu booting sebelum liveness mulai mengecek. Tanpa itu, app yang booting 10 detik bisa dibunuh sebelum sempat jalan.failureThresholdbawaannya 3: probe harus gagal 3 kali berturut-turut, di sini 15 detik, baru dianggap gagal.- Probe adalah bagian dari template pod. Menambah atau membuangnya mengubah hash template, jadi memicu rolling update, sama seperti ganti image.
Kapan butuh Kubernetes, kapan belum
catatan-api hari ini: satu VPS, tiga container (app, Postgres, Redis), satu tim kecil. Compose dengan restart: unless-stopped sudah menyalakan ulang container yang crash, dan deploy lewat GitHub Actions sudah otomatis. Kubernetes menambah control plane, YAML yang jauh lebih panjang, dan hal baru untuk dipelajari dan dibayar. Untuk catatan-api, itu biaya tanpa manfaat.
Kubernetes mulai sepadan saat: aplikasimu jalan di banyak server dan harus pindah otomatis kalau satu server mati, ada banyak service dari beberapa tim yang di-deploy sendiri-sendiri, atau beban naik turun dan kamu butuh autoscaling. Di tengah-tengahnya ada PaaS (pelajaran Platform as a Service), yang mengurus server untukmu tanpa YAML Kubernetes.
Latihan: simulateScaling
Latihan lesson ini menulis bagian "bertindak" dari reconcile loop. Pod diberi nama sederhana app-1, app-2, dan seterusnya. simulateScaling(currentPods, desiredReplicas) mengembalikan daftar aksi berupa object { action, pod }:
simulateScaling(["app-1", "app-2"], 4): kurang 2, buat dari nomor berikutnya:[{ action: "create", pod: "app-3" }, { action: "create", pod: "app-4" }].simulateScaling(["app-1", "app-2", "app-3"], 1): lebih 2, hapus dari yang terbaru:[{ action: "terminate", pod: "app-3" }, { action: "terminate", pod: "app-2" }].- Jumlahnya sudah sama:
[].
⚠️ Jebakan yang sering ketemu
- Mengira pod yang dihapus akan hidup lagi. Pod itu sekali pakai. Yang kembali adalah jumlahnya, dengan pod baru bernama baru. Data yang disimpan di dalam pod ikut hilang, sama seperti lapisan tulis container di pelajaran Docker Volumes.
- Lupa readiness probe. Pod langsung dapat request walau app-nya masih booting, dan tiap deploy menghasilkan error 502 yang gak kelihatan di laptop.
- Liveness probe mengecek database. Kalau Postgres lambat, semua pod gagal liveness bersamaan dan di-restart bersamaan, jadi gangguan kecil berubah jadi app mati total. Liveness cukup memastikan proses app-nya masih menjawab.
- Pakai Kubernetes karena tren. Untuk satu server dan satu tim kecil, Compose lebih sederhana dan lebih murah.
TL;DR: Kubernetes (K8s) adalah platform orkestrasi container yang menjaga desired state lewat reconcile loop: lihat actual state, bandingkan, bertindak. Pod adalah unit terkecil dan sekali pakai; Deployment menyimpan replicas dan template-nya; Service memberi alamat tetap ke pod yang ready. kubectl get pods menampilkan NAME, READY, STATUS, RESTARTS, AGE. Pod yang dihapus diganti pod baru bernama baru (Pending, ContainerCreating, Running 0/1, lalu 1/1). kubectl scale mengubah jumlah, kubectl set image memicu rolling update yang mematikan pod lama setelah pod baru ready. Readiness probe menentukan kapan pod menerima request, liveness probe me-restart container yang hang. catatan-api di satu VPS belum butuh Kubernetes; dia layak saat ada banyak server, banyak service, dan banyak tim.