Deployment #
Di dalam operasional sistem skala besar, salah satu tantangan terbesar adalah bagaimana kita melakukan pembaruan (update) versi kode program tanpa mengganggu kenyamanan pengguna. Bayangkan jika setiap kali tim developer melakukan rilis fitur baru, aplikasi web atau API kita harus mengalami mati mendadak (downtime) selama beberapa menit. Hal ini tentu akan merusak reputasi bisnis. Di Kubernetes, untuk mengotomatisasi daur hidup aplikasi tanpa downtime, kita mengandalkan objek abstraksi utama bernama Deployment.
Deployment adalah pengontrol (controller) tingkat tinggi yang dirancang khusus untuk mengelola aplikasi tanpa status (stateless). Ia bertindak sebagai deklarator yang mengatur pembuatan dan penghapusan objek ReplicaSet di balik layar. Memahami arsitektur internal, parameter kalkulasi rilis, serta strategi rollback Deployment adalah pondasi utama untuk membangun pipeline CI/CD yang stabil dan aman di produksi.
Mengapa Memilih Abstraksi Deployment? #
Sebelum adanya objek Deployment, administrator Kubernetes harus mengelola siklus rilis menggunakan objek ReplicationController atau ReplicaSet secara manual.
Tindakan ini sangat rentan kesalahan karena:
- Update Manual: Kita harus melakukan scaling down pengontrol lama dan scaling up pengontrol baru secara manual lewat serangkaian perintah CLI yang rawan terputus jaringan.
- Bypass Rollback: Jika versi baru mengalami crash loop, tidak ada cara otomatis untuk mengembalikan (rollback) ke versi sebelumnya selain menulis ulang manifes YAML asal.
Deployment memecahkan seluruh masalah tersebut dengan menyediakan deklarasi daur hidup terpusat:
- Self-Healing: Jika Pod mati akibat kesalahan runtime atau node hang, Deployment segera mendeteksi kegagalan tersebut dan berkoordinasi dengan ReplicaSet untuk menciptakan Pod baru.
- Zero-Downtime Updates: Melakukan rolling update bertahap yang menjamin selalu ada replika aplikasi yang aktif melayani traffic selama proses update berlangsung.
- Declarative Rollback: Menyimpan riwayat perubahan konfigurasi (revision history) sehingga kita dapat mengembalikan kluster ke kondisi sehat sebelumnya dalam hitungan detik.
Struktur Manifes Deployment Secara Mendalam #
Untuk memanfaatkan seluruh fitur otomatisasi di atas, kita harus menyusun manifest Deployment dengan parameter spesifikasi yang tepat. Berikut adalah contoh manifes Deployment tingkat produksi:
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api-deploy
namespace: production
labels:
app.kubernetes.io/name: payment-api
spec:
replicas: 4 # Jumlah target replika Pod yang dijamin aktif
revisionHistoryLimit: 10 # Jumlah ReplicaSet lama yang disimpan di etcd
progressDeadlineSeconds: 600 # Batas waktu update sebelum dinyatakan gagal (10 menit)
selector:
matchLabels:
app: payment-api # Kunci penghubung untuk mengelola Pod
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # Jumlah Pod ekstra maksimum selama update
maxUnavailable: 0 # Jumlah Pod mati maksimum selama update
template:
metadata:
labels:
app: payment-api # WAJIB sama persis dengan selector di atas
spec:
containers:
- name: api-container
image: payment-api:v2.1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
Parameter Kunci Spesifikasi Spec: #
revisionHistoryLimit: Membatasi jumlah objek ReplicaSet kosong (kapasitas 0) yang disimpan di etcd. Disarankan memberi nilai default10agar kita memiliki riwayat rollback yang cukup tanpa membebani ukuran database etcd.progressDeadlineSeconds: Menentukan batas waktu maksimal bagi Kubelet untuk menyelesaikan proses start-up Pod baru. Jika dalam waktu tersebut Pod baru gagal lulus uji readiness probe, Deployment Controller akan mengubah status kondisinya menjadiFalsedengan alasanProgressDeadlineExceeded, memicu notifikasi kegagalan ke sistem monitoring.
Strategi Rilis Tanpa Downtime: Rolling Update #
RollingUpdate adalah strategi rilis default Kubernetes yang melakukan penggantian versi Pod lama ke versi Pod baru secara bertahap. Pipa rilis dikendalikan oleh dua parameter kritis: maxSurge dan maxUnavailable.
Berikut adalah visualisasi transisi bertahap Pod v1 ke Pod v2 dengan target replika 4, maxSurge: 25% (1 Pod tambahan), dan maxUnavailable: 0:
flowchart TD
subgraph Step0 ["Kondisi Awal (v1 Aktif)"]
P1["Pod v1"] & P2["Pod v1"] & P3["Pod v1"] & P4["Pod v1"]
end
subgraph Step1 ["Step 1: Surge Pod v2"]
P1 & P2 & P3 & P4
S1["Pod v2 (Surge)"]
end
subgraph Step2 ["Step 2: v2 Ready, Hapus v1"]
P1 & P2 & P3
S1
end
subgraph Step3 ["Step 3: Surge Pod v2 Kedua"]
P1 & P2 & P3
S1 & S2["Pod v2"]
end
subgraph Step4 ["Kondisi Akhir (v2 Penuh)"]
S1 & S2 & S3["Pod v2"] & S4["Pod v2"]
end
Step0 -->|Buat 1 Pod Baru| Step1
Step1 -->|v2 Lolos Readiness, Hapus 1 v1| Step2
Step2 -->|Buat v2 Berikutnya| Step3
Step3 -->|Ulangi hingga v1 Habis| Step4
Memilih Kombinasi Parameter Rilis yang Tepat #
Kita dapat mengonfigurasi nilai maxSurge dan maxUnavailable menggunakan angka bulat absolut (misal 1) atau persentase (misal 25%). Pemilihan nilainya memiliki trade-off sebagai berikut:
| Kombinasi Nilai | Kebutuhan Resource Ekstra | Durasi Update | Jaminan Zero-Downtime | Penjelasan |
|---|---|---|---|---|
maxSurge: 1maxUnavailable: 0 |
Sangat Kecil (+1 Pod) | Lambat / Bertahap | 100% Terjamin | Pilihan paling aman untuk kluster produksi yang hemat resource. |
maxSurge: 0maxUnavailable: 1 |
Nol (Tidak butuh) | Sedang | Riskan (Downtime kecil) | Mengurangi kapasitas total selama update. Pod lama dimatikan dulu sebelum yang baru dibuat. |
maxSurge: 50%maxUnavailable: 0 |
Tinggi (+50% kapasitas) | Sangat Cepat | 100% Terjamin | Sangat tepat jika kita memiliki kapasitas node besar dan menginginkan rilis selesai cepat. |
maxSurge: 100%maxUnavailable: 0 |
Sangat Tinggi (+100% kapasitas) | Instan | 100% Terjamin | Pola Blue-Green lokal. Seluruh Pod baru dibuat sekaligus sebelum yang lama dimatikan. |
Strategi Rilis Recreate: Kapan Downtime Dibutuhkan? #
Strategi Recreate bekerja secara drastis: ia langsung membunuh seluruh Pod versi lama sekaligus, menunggu hingga statusnya mati total, barulah membuat kelompok Pod versi baru secara bersamaan. Tindakan ini secara otomatis memicu terjadinya downtime (layanan tidak dapat diakses) selama rentang waktu transisi kontainer.
Meskipun memicu downtime, strategi Recreate wajib dipilih pada skenario berikut:
- Aplikasi Non-Concurrent: Aplikasi kita tidak dirancang untuk berjalan berdampingan lintas versi (dual-version incompatibility). Misalnya, kode versi baru melakukan penulisan skema database yang memicu crash jika diakses oleh kode versi lama.
- Keterbatasan IP / Port Jaringan: Aplikasi menggunakan port host (
hostPort) yang sama, sehingga kontainer versi baru tidak dapat menyala sebelum kontainer lama membebaskan port jaringan tersebut.
Siklus Hidup Rilis di Lapangan: CLI Commands #
Di lingkungan produksi sesungguhnya, perubahan disarankan menggunakan pendekatan GitOps (merubah file YAML di Git). Namun, untuk keperluan uji coba cepat di lingkungan development/staging, kita dapat mengandalkan perintah CLI berikut:
# 1. Update image aplikasi secara langsung
kubectl set image deployment/payment-api-deploy api-container=payment-api:v2.2.0
# 2. Pantau proses update secara realtime
kubectl rollout status deployment/payment-api-deploy
# 3. Lihat riwayat revisi perubahan
kubectl rollout history deployment/payment-api-deploy
# 4. Tambahkan catatan audit agar history mudah dibaca
kubectl annotate deployment/payment-api-deploy \
kubernetes.io/change-cause="Rilis Fitur Keamanan: Update image ke v2.2.0 guna mengatasi celah CVE-2026-xxx"
Teknik Pause & Resume (Canary Manual Sederhana) #
Jika kita ingin melakukan rilis tipe Canary secara sederhana tanpa bantuan service mesh, kita dapat menahan proses update di tengah jalan guna melakukan uji coba pada sebagian kecil traffic pengguna:
# Ubah image untuk memicu update
kubectl set image deployment/payment-api-deploy api-container=payment-api:v2.2.0
# Segera jeda proses update (setelah 1 atau 2 Pod baru menyala)
kubectl rollout pause deployment/payment-api-deploy
# Lakukan pengujian. Sebagian traffic akan diarahkan ke Pod baru, sebagian ke Pod lama.
# Jika ditemukan bug, batalkan dan lakukan rollback:
kubectl rollout undo deployment/payment-api-deploy
# Jika semua aman, lanjutkan proses rilis hingga selesai:
kubectl rollout resume deployment/payment-api-deploy
Pemulihan Bencana Menggunakan Rollback #
Jika setelah pembaruan versi selesai dilakukan, aplikasi kita tiba-tiba mengalami degradasi performa atau kebocoran memori (memory leak), kita harus segera mengambil tindakan penyelamatan untuk mengembalikan layanan ke kondisi stabil sebelumnya.
# Rollback langsung ke versi REVISION terakhir
kubectl rollout undo deployment/payment-api-deploy
# Rollback ke REVISION spesifik di masa lalu (misal revisi 2)
kubectl rollout undo deployment/payment-api-deploy --to-revision=2
Siklus rollback ini berjalan sangat cepat karena Deployment Controller hanya perlu membalikkan arah sinkronisasi ReplicaSet: menaikkan kapasitas ReplicaSet lama yang sehat kembali ke angka target, dan menurunkan kapasitas ReplicaSet baru yang bermasalah menjadi 0.
Mendiagnosis Rilis yang Stuck atau Gagal #
Salah satu masalah klasik di Kubernetes adalah proses rolling update yang macet di tengah jalan. Hal ini biasanya ditandai dengan perintah kubectl rollout status yang terus menggantung tanpa batas waktu.
Langkah Sistematis Diagnosis: #
- Temukan Pod yang Bermasalah:
Perhatikan apakah ada Pod baru yang statusnya
kubectl get pods -l app=payment-apiImagePullBackOff(salah menulis nama/tag image) atauCrashLoopBackOff(aplikasi crash saat booting akibat konfigurasi salah). - Analisis Event Kubelet:
Membaca log events di bagian bawah output describe akan mengungkapkan apakah node kehabisan memori atau gagal mengunduh image dari registry rahasia (private registry).
kubectl describe pod <nama-pod-baru-yang-gagal> - Periksa Logs Aplikasi:
# Jika container terus-menerus crash, baca log dari eksekusi sebelumnya kubectl logs <nama-pod> --previous
Anti-Pattern dalam Manajemen Deployment #
Kesalahan-kesalahan konfigurasi Deployment yang sering kali memicu kecelakaan operasional di kluster produksi:
Anti-Pattern 1: Mengabaikan Pengisian Readiness Probe dalam Rolling Update #
Mendeploy aplikasi tanpa menyediakan mekanisme pengujian kesiapan kontainer.
ANTI-PATTERN: Tidak Menuliskan readinessProbe pada Manifes Deployment
// KITA MELAKUKAN:
- Menjalankan rolling update image aplikasi dari `:v1` ke `:v2`.
- Di dalam template kontainer, kita tidak menuliskan konfigurasi `readinessProbe`.
- Image `:v2` ternyata rusak dan langsung crash (exit code 1) dalam waktu 5 detik setelah booting.
// KONSEKUENSI DI PRODUKSI:
- Downtime Total: Karena tidak ada readiness probe, Kubernetes mengasumsikan Pod baru langsung "sehat"
begitu kontainer berhasil dinyalakan (meskipun aplikasinya belum selesai loading).
- Deployment Controller akan terus melanjutkan penghapusan Pod lama `:v1` secara membabi buta.
- Dalam hitungan menit, seluruh Pod sehat `:v1` telah dihapus dan digantikan oleh Pod rusak `:v2` yang crash.
- Layanan lumpuh total.
✓ SOLUSI YANG BENAR:
- Selalu sediakan endpoint uji kesehatan khusus (misal `/healthz/ready`) di dalam kode aplikasi Anda.
- Konfigurasikan `readinessProbe` secara akurat pada manifest Deployment.
- Kubernetes dijamin tidak akan pernah mematikan Pod lama sebelum Pod baru benar-benar lolos uji readiness probe.
Anti-Pattern 2: Menggunakan Skrip Imperatif Skala Besar untuk Update #
Mengandalkan perintah CLI manual secara beruntun di pipeline CI/CD.
ANTI-PATTERN: Menggunakan kubectl scale dan kubectl set image Secara Berantai
// KITA MELAKUKAN:
- Menuliskan skrip bash di Jenkins/GitLab CI:
`kubectl set image deployment/my-deploy app=my-image:tag && kubectl scale deployment/my-deploy --replicas=10`
// KONSEKUENSI DI PRODUKSI:
- Kehilangan State Riil: Perintah imperatif memicu bentrokan jika dijalankan bersamaan dari dua pipeline berbeda.
- Jika manifes git repository tidak diperbarui, saat terjadi pemulihan bencana otomatis oleh GitOps (seperti ArgoCD),
seluruh konfigurasi manual di kluster produksi akan terhapus dan ditimpa kembali ke konfigurasi usang di repositori Git.
✓ SOLUSI YANG BENAR:
- Berpegang teguh pada prinsip GitOps.
- Perbarui tag image atau jumlah replika langsung pada berkas manifest YAML di dalam repositori Git.
- Terapkan perubahan ke kluster menggunakan `kubectl apply -f deployment.yaml` secara deklaratif tunggal.
Ringkasan #
- Abstraksi Stateless Utama — Deployment adalah pengontrol utama untuk aplikasi stateless yang mengelola pembuatan, scaling, dan penghapusan ReplicaSet otomatis.
- Zero-Downtime via RollingUpdate — Gunakan strategi
RollingUpdateguna melakukan pembaruan versi aplikasi secara bertahap tanpa mematikan layanan ke pengguna.- Kaidah maxSurge dan maxUnavailable — Atur parameter rilis secara cermat:
maxUnavailable: 0menjamin tidak ada downtime, sedangkanmaxSurgemengendalikan kebutuhan kapasitas memori ekstra selama update.- Strategi Recreate untuk Ketidakcocokan — Gunakan strategi
Recreatehanya jika aplikasi versi lama dan baru tidak boleh aktif bersamaan di database yang sama.- Rollback Instan Berbasis Riwayat — Deployment menyimpan objek ReplicaSet lama guna memfasilitasi proses rollback instan lewat perintah
kubectl rollout undo.- Kemampuan Audit via Change-Cause — Gunakan anotasi
kubernetes.io/change-causepada Deployment untuk mencatat riwayat alasan rilis demi memudahkan audit tim DevOps.- Wajib Gunakan Readiness Probe — Jangan pernah melakukan rolling update tanpa mendefinisikan
readinessProbeguna mencegah kontainer rusak menggantikan kontainer sehat.- Terapkan Prinsip Deklaratif — Hindari modifikasi imperatif (
kubectl scale/set image) di produksi; perbarui file manifes YAML Git untuk menjaga single source of truth.