Rolling Update #

Dalam operasional aplikasi modern, memperbarui kode atau konfigurasi secara berkala adalah rutinitas harian. Tantangan terbesarnya adalah bagaimana kita dapat meluncurkan versi baru tersebut tanpa mengganggu pengguna aktif sama sekali (zero-downtime deployment). Kubernetes memecahkan tantangan ini secara bawaan menggunakan strategi Rolling Update. Dibandingkan dengan metode deployment tradisional yang sering kali membutuhkan jendela pemeliharaan (maintenance window) di tengah malam, Rolling Update di Kubernetes bekerja dengan cara mengganti Pod lama dengan Pod baru secara berangsur-angsur.

Namun, mencapai zero-downtime yang sesungguhnya tidak sesederhana sekadar membiarkan konfigurasi default berjalan. Kita harus mengonfigurasi toleransi kegagalan secara matematis, menyelaraskan siklus hidup pemutusan koneksi (graceful shutdown), serta mendesain probe kesehatan (readiness probes) yang akurat. Artikel ini membedah mekanisme internal Rolling Update, perhitungan parameter transisi, sinkronisasi routing jaringan, serta panduan memecahkan masalah saat proses update mengalami hambatan (stuck).


Cara Kerja Internal Deployment Controller #

Saat kita memperbarui spesifikasi Pod (seperti mengganti versi tag image kontainer) pada objek Deployment, Kubernetes tidak langsung menimpa Pod yang sedang berjalan. Deployment Controller bertindak sebagai manager tingkat tinggi yang mengoordinasikan pembuatan dan penghapusan Pod melalui objek ReplicaSet.

Berikut adalah tahapan transisi state yang dikelola oleh Deployment Controller:

  1. Pembuatan ReplicaSet Baru: Deployment Controller membuat ReplicaSet kedua (kita sebut v2) di samping ReplicaSet aktif saat ini (v1).
  2. Pemberian Skala Bertahap (Scaling Up v2): ReplicaSet v2 mulai membuat Pod baru dengan versi kode v2.
  3. Verifikasi Kesehatan: Pod v2 harus melewati pemeriksaan Readiness Probe. Sebelum probe ini mengembalikan status sukses, Pod v2 dianggap belum siap melayani lalu lintas dan tidak akan dimasukkan ke dalam daftar endpoint jaringan.
  4. Pengurangan Skala Bertahap (Scaling Down v1): Setelah satu atau beberapa Pod v2 dikonfirmasi sehat, Deployment Controller menginstruksikan ReplicaSet v1 untuk mematikan sebagian Pod v1 secara perlahan.
  5. Pengulangan Siklus: Proses ini berulang hingga seluruh Pod v1 dihapus (skala nol) dan ReplicaSet v2 memegang seluruh jumlah replika yang diinginkan secara penuh.
sequenceDiagram
    participant DC as Deployment Controller
    participant RS1 as ReplicaSet v1 (Old)
    participant RS2 as ReplicaSet v2 (New)
    participant Pod2 as Pod v2 (Candidate)
    participant SVC as EndpointSlice / Service
    
    DC->>RS2: Perintahkan Scale Up (+1 Pod v2)
    RS2->>Pod2: Buat Pod v2
    Note over Pod2: Kontainer Inisialisasi & Jalankan Probe
    Pod2-->>DC: Readiness Probe Berhasil (200 OK)
    DC->>SVC: Tambahkan IP Pod v2 ke Routing List
    DC->>RS1: Perintahkan Scale Down (-1 Pod v1)
    Note over RS1: Hapus 1 Pod v1 secara graceful
    Note over DC: Ulangi siklus hingga v1 = 0

Perhitungan Parameter: maxSurge dan maxUnavailable #

Kecepatan, penggunaan resource tambahan, dan tingkat toleransi kegagalan selama proses Rolling Update dikendalikan secara ketat oleh dua parameter di bawah properti rollingUpdate:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0

Kita dapat menentukan nilai kedua parameter ini menggunakan angka absolut (misalnya 1 atau 2) atau persentase dari total replika yang diinginkan (misalnya 25%).

1. maxSurge #

Menentukan jumlah maksimum Pod tambahan yang boleh dibuat di atas kapasitas replika yang diinginkan selama proses update berlangsung.

  • Rumus: $\text{Jumlah Pod Maksimal} = \text{Desired Replicas} + \text{maxSurge}$
  • Implikasi: Nilai maxSurge yang tinggi mempercepat proses deployment karena Kubernetes dapat membuat banyak Pod baru secara paralel. Namun, hal ini membutuhkan kapasitas sisa CPU/Memory (headroom) yang cukup pada node cluster.

2. maxUnavailable #

Menentukan jumlah maksimum Pod yang boleh tidak tersedia (dalam kondisi mati atau belum siap) selama proses update berlangsung.

  • Rumus: $\text{Jumlah Pod Minimal Tersedia} = \text{Desired Replicas} - \text{maxUnavailable}$
  • Implikasi: Jika kita menetapkan maxUnavailable: 0, Kubernetes menjamin bahwa kapasitas layanan kita tidak akan pernah berkurang di bawah 100% dari total replika yang kita minta selama masa transisi.

Pola Konfigurasi yang Direkomendasikan di Produksi #

Pola A: Zero-Downtime Ketat (Sangat Direkomendasikan) #

  • Konfigurasi: maxSurge: 1 (atau 25%), maxUnavailable: 0
  • Karakteristik: Menjamin kapasitas penuh layanan 100% terjaga setiap saat. Pod lama tidak akan pernah dimatikan sebelum Pod baru dikonfirmasi sehat oleh readiness probe.
  • Trade-off: Deployment berjalan lebih lambat karena harus menunggu Pod baru siap satu per satu sebelum melanjutkan siklus berikutnya.

Pola B: Hemat Resource (Cluster Terbatas) #

  • Konfigurasi: maxSurge: 0, maxUnavailable: 1 (atau 25%)
  • Karakteristik: Tidak membutuhkan resource ekstra sama sekali di cluster. Kubernetes langsung mematikan satu Pod lama untuk memberikan ruang slot penjadwalan bagi Pod baru.
  • Trade-off: Selama transisi, kapasitas layanan kita berkurang satu replika. Tidak cocok untuk aplikasi yang jalurnya sudah mendekati batas utilisasi 100% di bawah beban puncak.

Pola C: Update Cepat Skala Besar #

  • Konfigurasi: maxSurge: 50%, maxUnavailable: 0
  • Karakteristik: Sangat cepat. Setengah dari jumlah Pod baru dibuat secara instan di awal proses.
  • Trade-off: Memerlukan headroom kapasitas node yang sangat besar di cluster.

Peran Vital Probes dalam Jaringan Kubernetes #

Mekanisme Rolling Update yang aman sangat bergantung pada keakuratan Readiness Probe. Ketika Pod v2 baru saja dibuat, Kubelet di node worker akan memicu startup kontainer. Jika kontainer sudah masuk status Running, hal ini hanya menandakan bahwa proses utama (seperti node server.js atau java -jar) telah dijalankan. Proses tersebut biasanya membutuhkan waktu beberapa detik untuk memuat pustaka, membuka koneksi ke database, atau memanaskan cache (warm start).

Jika kita tidak mendefinisikan readinessProbe:

  1. Kubernetes langsung menganggap Pod v2 tersebut sehat (Ready).
  2. Service Controller segera memasukkan IP Pod v2 ke daftar perutean trafik.
  3. Kube-proxy dan Ingress Controller mulai mengirimkan trafik pengguna nyata ke Pod v2 yang belum siap sepenuhnya. Pengguna akan menerima error HTTP 502 Bad Gateway atau 503 Service Unavailable.
  4. Bersamaan dengan itu, Kubernetes langsung mematikan Pod v1 karena menganggap tugas transisi telah selesai.
flowchart TD
    subgraph NoProbe["TANPA PROBE"]
        Run1["Pod v2 Run"] --> Ready1["Langsung Ditandai Ready"] --> Traffic1["Trafik Masuk"] --> Crash1["CRASH/ERROR 502"]
        Crash1 --> Kill1["Dimatikan Instan"] --> PodV1_1["Pod v1 Lama"]
    end

    subgraph WithProbe["DENGAN PROBE"]
        Run2["Pod v2 Run"] --> Wait2["Tunggu Probe Sukses / 200 OK"] --> Ready2["Ditandai Ready"] --> Traffic2["Trafik Masuk (Lancar)"]
        Traffic2 --> Kill2["Dimatikan Secara Bertahap"] --> PodV1_2["Pod v1 Lama"]
    end

Pentingnya minReadySeconds #

Selain probe, properti minReadySeconds sangat penting untuk ditambahkan. Properti ini memerintahkan Kubernetes untuk menunggu selama N detik setelah Pod baru ditandai Ready sebelum melanjutkan ke langkah berikutnya.

Jika aplikasi kita memiliki bug memori tersembunyi yang menyebabkannya crash 5 detik setelah menerima trafik nyata, minReadySeconds: 30 akan menyelamatkan kita. Kubernetes akan menghentikan proses update karena Pod baru mati sebelum batas waktu stabilisasi terlewati, sehingga Pod v1 yang tersisa tetap aman menjalankan layanan.


Siklus Graceful Shutdown (Pematian yang Rapi) #

Saat Deployment Controller memutuskan untuk mematikan Pod lama (v1), proses pemutusan koneksi harus dikelola secara halus agar request pengguna yang sedang berjalan di dalam kontainer tidak terputus di tengah jalan.

Alur Pematian Pod di Kubernetes: #

  1. Pod Diubah ke Status Terminating: Kubernetes menghapus IP Pod dari daftar Endpoint Service. Ingress dan kube-proxy mulai memperbarui tabel routing mereka agar tidak mengirimkan koneksi baru ke Pod tersebut.
  2. Eksekusi preStop Hook: Jika kita mengonfigurasi preStop lifecycle hook pada kontainer, Kubernetes akan menjalankan perintah tersebut terlebih dahulu.
  3. Signal SIGTERM Dikirim: Kubernetes mengirimkan signal SIGTERM ke proses utama (PID 1) di dalam kontainer. Aplikasi harus menangkap signal ini, berhenti menerima koneksi baru, menyelesaikan antrean request yang sedang berjalan, dan menutup koneksi database secara rapi.
  4. Batas Waktu Toleransi (Grace Period): Kubernetes menunggu hingga batas waktu terminationGracePeriodSeconds (default 30 detik) terlewati.
  5. Signal SIGKILL Dikirim: Jika aplikasi belum mati setelah batas waktu terlewati, kernel host akan mengirimkan SIGKILL untuk mematikan proses secara paksa.
flowchart TD
    Start["Deployment Controller memicu Scale Down"] --> Step1["Status Pod diubah ke 'Terminating'"]
    Step1 --> Step2["IP Pod dihapus dari Service EndpointSlice"]
    
    subgraph Parallel ["Berjalan secara Paralel"]
        Step3["Kube-proxy & Ingress update routing table (1-5 detik)"]
        Step4["Kubelet memicu 'preStop' hook pada kontainer"]
    end
    
    Step2 --> Step3
    Step2 --> Step4
    
    Step3 --> Step5["Kubelet mengirimkan SIGTERM ke PID 1"]
    Step4 --> Step5
    
    Step5 --> Step6{"Apakah proses mati<br>sebelum Grace Period berakhir?"}
    
    Step6 -- "Ya" --> EndSafe["Pod Terhapus Bersih"]
    Step6 -- "Tidak" --> Step7["Kubelet mengirimkan SIGKILL secara paksa"]
    
    Step7 --> EndForced["Pod Dihapus Paksa"]
    
    style EndSafe stroke:#388e3c,stroke-width:2px
    style EndForced stroke:#d32f2f,stroke-width:2px

[!IMPORTANT] Mengapa preStop hook dengan sleep sering dibutuhkan? Proses sinkronisasi tabel perutean di seluruh node worker oleh kube-proxy membutuhkan waktu beberapa detik (propagation delay). Jika aplikasi langsung merespons SIGTERM dengan menutup socket koneksi seketika, koneksi pengguna yang baru dikirimkan selama jeda propagasi tersebut akan gagal. Menerapkan preStop hook dengan perintah sleep 5 memberikan waktu bagi tabel routing jaringan untuk terupdate sepenuhnya sebelum aplikasi mulai mematikan layanannya.


Anti-Pattern vs Solusi Terbaik #

Mari kita pelajari beberapa kesalahan konfigurasi Rolling Update di lingkungan produksi beserta perbaikan terbaiknya.

Anti-Pattern 1: Mengatur maxUnavailable Sebesar 100% pada Cluster Produksi #

Menetapkan parameter maxUnavailable yang terlalu besar (atau sama dengan jumlah replika) untuk mempercepat rilis. Tindakan ini merusak fungsi dasar zero-downtime karena Kubernetes diizinkan untuk mematikan seluruh Pod lama sekaligus sebelum Pod baru siap.

# ✗ ANTI-PATTERN: Mengizinkan seluruh Pod mati bersamaan saat update
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 100% # ← KRITIS: Seluruh Pod v1 dimatikan seketika, downtime total terjadi

Solusi Terbaik #

Gunakan batasan ketat dengan nilai maxUnavailable: 0 agar kapasitas minimal layanan tetap terjaga penuh.

# ✓ SOLUSI: Konfigurasi aman menjamin ketersediaan Pod
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0 # ← Aman, Pod lama hanya dimatikan setelah Pod baru siap menerima trafik

Anti-Pattern 2: Tidak Mengonfigurasi preStop Hook untuk Aplikasi Web HTTP Traffic Tinggi #

Saat rolling update berjalan, pengguna terkadang menerima error HTTP 502 Bad Gateway secara berkala (flapping). Hal ini terjadi karena kontainer langsung memproses pematian begitu menerima SIGTERM, padahal Ingress Controller masih mengarahkan request sisa ke Pod tersebut akibat keterlambatan sinkronisasi jaringan internal.

Solusi Terbaik #

Tambahkan preStop lifecycle hook yang melakukan penundaan (delay) singkat sebelum Kubelet mengirimkan SIGTERM ke aplikasi.

# ✓ SOLUSI: Menambahkan preStop hook untuk mitigasi propagation delay
spec:
  containers:
  - name: web-app
    image: company/web-app:v2.1.0
    lifecycle:
      preStop:
        exec:
          # Memberikan waktu bagi kube-proxy / Ingress untuk menghapus IP Pod dari routing
          command: ["/bin/sh", "-c", "sleep 10"]
    terminationGracePeriodSeconds: 45 # Berikan waktu yang cukup (lebih lama dari sleep preStop)

Manifes Deployment Produksi Terbaik #

Berikut adalah contoh manifes Deployment siap produksi yang menerapkan strategi Rolling Update yang aman, lengkap dengan kontrol ketersediaan, probe kesehatan, dan shutdown teratur:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: billing-service
  namespace: finance
  labels:
    app: billing-service
spec:
  replicas: 4
  # Menentukan toleransi waktu tunggu stabilitas Pod baru
  minReadySeconds: 15
  # Batas waktu timeout keseluruhan deployment (300 detik)
  progressDeadlineSeconds: 300
  # Jumlah riwayat ReplicaSet yang disimpan untuk rollback instan
  revisionHistoryLimit: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1             # Maksimal 1 Pod ekstra di atas 4 replika (total 5)
      maxUnavailable: 0       # Selalu pastikan minimal ada 4 Pod siap menerima trafik
  selector:
    matchLabels:
      app: billing-service
  template:
    metadata:
      labels:
        app: billing-service
    spec:
      terminationGracePeriodSeconds: 60 # Sesuai dengan durasi cleanup internal aplikasi
      containers:
      - name: billing-app
        image: company/billing-app:v2.1.0
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "1000m"
            memory: "512Mi"
        lifecycle:
          preStop:
            exec:
              # Jeda agar trafik tidak dialihkan ke Pod yang akan mati ini
              command: ["/bin/sh", "-c", "sleep 15"]
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          failureThreshold: 3
        livenessProbe:
          httpGet:
            path: /health/live
            port: 8080
          initialDelaySeconds: 20
          periodSeconds: 10
          failureThreshold: 3

Panduan Perintah CLI untuk Manajemen Rollout #

Kita dapat mengontrol dan memantau jalannya proses Rolling Update secara interaktif menggunakan CLI kubectl:

1. Memantau Progress Deployment #

Untuk melihat status terkini secara real-time dari pembaruan Pod:

kubectl rollout status deployment/billing-service -n finance
# Output sukses: deployment "billing-service" successfully rolled out

2. Memeriksa Riwayat Revisi #

Setiap kali perubahan spec Pod disimpan, Kubernetes mencatatnya sebagai revisi baru:

kubectl rollout history deployment/billing-service -n finance

Untuk melihat detail spesifikasi dari revisi tertentu:

kubectl rollout history deployment/billing-service --revision=2 -n finance

3. Menghentikan Sementara dan Melanjutkan Rilis (Pause/Resume) #

Sangat berguna jika kita ingin melakukan modifikasi bertahap atau melakukan investigasi cepat di tengah-tengah proses rollout:

# Hentikan sementara rollout yang sedang berjalan
kubectl rollout pause deployment/billing-service -n finance

# Lanjutkan kembali rollout setelah investigasi selesai
kubectl rollout resume deployment/billing-service -n finance

4. Membatalkan Rilis (Rollback) #

Jika terdeteksi kegagalan sistem pada versi baru, kita dapat langsung membatalkannya dan kembali ke versi sebelumnya secara instan:

# Rollback ke revisi tepat sebelum revisi saat ini
kubectl rollout undo deployment/billing-service -n finance

# Rollback ke revisi spesifik berdasarkan nomor riwayat
kubectl rollout undo deployment/billing-service --to-revision=2 -n finance

Diagnosis Masalah Umum (Troubleshooting) #

Jika proses Rolling Update kita berhenti di tengah jalan (stuck), jalankan langkah diagnosis sistematis berikut:

  1. Periksa Status Deployment:

    kubectl describe deployment billing-service -n finance
    

    Perhatikan bagian Conditions. Jika terdapat pesan ProgressDeadlineExceeded, artinya batas waktu progressDeadlineSeconds telah terlewati karena ada kontainer yang gagal startup.

  2. Identifikasi ReplicaSet yang Terlibat:

    kubectl get replicaset -n finance -l app=billing-service
    

    Bandingkan kolom DESIRED, CURRENT, dan READY. Jika ReplicaSet baru memiliki jumlah DESIRED yang tinggi tetapi READY bernilai nol, masalah utama ada pada Pod versi baru.

  3. Temukan Pod yang Bermasalah:

    kubectl get pods -n finance -l app=billing-service
    

    Cari Pod dengan status selain Running atau yang memiliki nilai kolom READY 0/1.

  4. Investigasi Log dan Detail Pod:

    # Periksa logs kontainer untuk mencari error stacktrace aplikasi
    kubectl logs <pod-name-baru> -n finance
    
    # Periksa events sistem untuk mencari kegagalan probe, kekurangan resource, atau pull image
    kubectl describe pod <pod-name-baru> -n finance
    

Akar Masalah Paling Sering Terjadi: #

  • ImagePullBackOff: Tag image salah ditulis, atau node worker tidak memiliki kredensial otorisasi untuk mengakses repositori registry kontainer privat.
  • CrashLoopBackOff: Aplikasi mengalami crash sesaat setelah inisialisasi awal karena hilangnya variabel lingkungan (ConfigMap/Secret) yang dibutuhkan.
  • Kekurangan Resource Node (Pending status): Cluster tidak memiliki node worker yang memiliki kapasitas kosong CPU/Memory untuk menampung Pod tambahan (maxSurge) baru.

Ringkasan #

  • Pahami maxSurge & maxUnavailable — Gunakan kombinasi maxSurge: 25% dan maxUnavailable: 0 sebagai konfigurasi standar produksi untuk menjamin ketersediaan kapasitas penuh 100% selama rilis.
  • Readiness Probe wajib dipasang — Tanpa probe, Kubernetes akan mengarahkan trafik pengguna ke kontainer yang belum selesai melakukan inisialisasi awal.
  • Gunakan minReadySeconds sebagai buffer — Jeda waktu tunggu tambahan setelah Pod siap membantu mendeteksi kegagalan startup tersembunyi sebelum seluruh Pod lama dimatikan.
  • Gunakan preStop hook untuk graceful shutdown — Perintah sleep singkat di preStop hook memberikan waktu bagi jaringan kube-proxy untuk menyinkronkan tabel routing sebelum kontainer mati.
  • Pertahankan batas sejarah revisi — Atur properti revisionHistoryLimit untuk membatasi jumlah ReplicaSet usang di cluster tetapi tetap menyisakan cadangan untuk rollback cepat.
  • Pantau status secara berkala — Manfaatkan perintah kubectl rollout status pada pipeline CI/CD kita untuk memvalidasi keberhasilan deployment sebelum menandai rilis selesai.

← Sebelumnya: Deployment Strategy Overview   Berikutnya: Blue/Green Deployment →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact