Rolling Update #
Penerapan pembaruan aplikasi di lingkungan produksi modern menuntut ketiadaan waktu henti (zero-downtime). Di masa lalu, merilis versi baru seringkali membutuhkan pemeliharaan terjadwal di tengah malam yang memaksa kita mematikan aplikasi untuk sementara waktu. Kubernetes menawarkan solusi elegan melalui mekanisme pembaruan bertahap (rolling update) secara bawaan. Namun, pembaruan yang andal tidak hanya sekadar mengganti gambar kontainer (container image), melainkan melibatkan pemeriksaan kesehatan yang ketat, pengelolaan lalu lintas jaringan, dan kemampuan mendeteksi kegagalan secara real-time. Melalui integrasi dengan Ansible, kita dapat mengotomatiskan seluruh alur kerja deployment ini, menguji kesehatan aplikasi pasca-update, dan melakukan pemulihan instan jika terjadi anomali.
Strategi Zero-Downtime Rolling Update #
Mekanisme rolling update bekerja dengan cara mengganti instans Pod lama dengan instans Pod baru secara bertahap. Kubernetes memastikan bahwa selama proses transisi ini berlangsung, aplikasi tetap memiliki kapasitas yang cukup untuk melayani permintaan lalu lintas pengguna yang masuk.
Dua parameter utama yang mengontrol perilaku rolling update pada objek Deployment adalah maxSurge dan maxUnavailable.
maxSurgemenentukan jumlah maksimal Pod baru yang dapat dibuat di atas jumlah replika yang diinginkan selama proses update. Nilai ini bisa berupa persentase (misalnya25%) atau angka bulat (misalnya1).maxUnavailablemenentukan jumlah maksimal Pod yang boleh tidak tersedia atau mati selama proses pembaruan berlangsung.
Berikut adalah alur proses rolling update yang dikoordinasikan secara ketat antara pemeriksaan kesehatan, pengujian smoke test, dan rollback otomatis:
flowchart TD
A["Mulai Deployment Baru"] --> B["Catat Image Versi Lama (Rollback Fact)"]
B --> C["Terapkan Manifest Deployment Baru (Update Image)"]
C --> D["Monitor Rollout (k8s_rollout_status)"]
D --> E{"Apakah Rollout Sukses?"}
E -- "Tidak (Timeout)" --> H["Jalankan Rollback (rollout undo)"]
E -- "Ya" --> F["Jalankan Smoke Test (Akses Endpoint /health)"]
F --> G{"Apakah HTTP Status 200?"}
G -- "Ya" --> I["Deployment Sukses & Selesai"]
G -- "Tidak" --> H
H --> J["Kembalikan ke Versi Lama & Laporkan Error"]
Mengatur Batas Toleransi Rollout (maxSurge & maxUnavailable) #
Pilihan konfigurasi kedua parameter ini sangat bergantung pada ketersediaan sumber daya kluster dan karakteristik aplikasi kita.
Jika kita menetapkan maxUnavailable: 0 dan maxSurge: 25%, kita menjamin bahwa kapasitas kluster tidak akan pernah turun di bawah 100% dari jumlah replika yang diinginkan. Kubernetes akan membuat Pod baru terlebih dahulu sebelum mematikan Pod lama. Ini adalah konfigurasi ideal untuk aplikasi web publik yang sensitif terhadap penurunan performa akibat berkurangnya instans aktif. Sebaliknya, jika kita memiliki keterbatasan kapasitas komputasi (CPU/RAM) pada worker node, kita bisa mengatur maxUnavailable: 25% dan maxSurge: 0 agar Kubernetes mematikan beberapa instans lama terlebih dahulu sebelum mengalokasikan Pod baru.
Berikut adalah contoh konfigurasi manifest deployment Kubernetes yang aman untuk rolling update:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Buat maksimal 1 Pod tambahan di atas kapasitas normal
maxUnavailable: 0 # Jangan biarkan ada Pod lama yang mati sebelum Pod baru Ready
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-server
image: myregistry.local/web-app:v2.1.0
Dengan konfigurasi maxUnavailable: 0, kita memastikan ketersediaan layanan tetap terjaga penuh sepanjang masa transisi deployment.
Penerapan Liveness dan Readiness Probes #
Kesalahan paling umum dalam menerapkan rolling update adalah mengabaikan konfigurasi pemeriksaan kesehatan kontainer (health probes). Tanpa adanya probe, Kubernetes hanya mengandalkan status proses utama di dalam kontainer. Jika proses tersebut berjalan, Kubernetes menganggap kontainer sudah sehat dan langsung mengarahkan lalu lintas data pengguna ke sana, meskipun aplikasi di dalamnya masih dalam proses inisialisasi koneksi database atau membaca file konfigurasi yang besar. Akibatnya, pengguna akan menerima pesan error 502 (Bad Gateway) atau 503 (Service Unavailable) selama beberapa detik.
Jenis-Jenis Probes di Kubernetes #
Untuk mencegah skenario di atas, kita harus mengonfigurasi tiga jenis probe secara disiplin:
- Startup Probe: Digunakan untuk aplikasi yang membutuhkan waktu inisialisasi awal yang lama. Pemeriksaan lainnya (liveness & readiness) akan ditangguhkan hingga startup probe berhasil melewati ambang batas.
- Readiness Probe: Digunakan untuk menentukan apakah kontainer siap menerima lalu lintas jaringan. Jika readiness probe gagal, alamat IP Pod akan dihapus dari daftar endpoints Service Kubernetes sehingga tidak ada lalu lintas yang diarahkan ke sana.
- Liveness Probe: Digunakan untuk memantau kesehatan internal aplikasi selama masa berjalan. Jika liveness probe mengalami kegagalan beruntun, Kubernetes akan mematikan kontainer dan melakukan restart secara otomatis.
Anti-Pattern vs Solusi Konfigurasi Probes #
Mari kita bandingkan konfigurasi kontainer tanpa perlindungan probe dengan konfigurasi yang menerapkan praktik terbaik.
# ANTI-PATTERN: Kontainer didefinisikan tanpa mekanisme health check
spec:
containers:
- name: backend-api
image: myregistry.local/api:v1.0.0
# JANGAN lakukan ini! Kubernetes langsung menganggap kontainer
# siap begitu container engine mendeteksi entrypoint berjalan.
# BENAR: Menerapkan startup, readiness, dan liveness probes secara lengkap
spec:
containers:
- name: backend-api
image: myregistry.local/api:v1.0.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # Berikan waktu inisialisasi hingga 5 menit (30 * 10s)
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
Melalui konfigurasi di atas, proses rolling update menjadi sangat aman. Kubernetes tidak akan pernah mematikan Pod versi lama sebelum Pod versi baru berhasil melewati pemeriksaan readinessProbe dengan sukses.
Orkestrasi Rolling Update dan Pemantauan Rollout dengan Ansible #
Sekarang kita akan merancang sebuah playbook Ansible lengkap yang bertugas memperbarui gambar aplikasi, memantau proses penggelaran (rollout) hingga selesai secara real-time, menjalankan pengujian pasca-deployment (smoke test), dan melakukan pembatalan otomatis (rollback) jika terdeteksi adanya kegagalan pada aplikasi baru.
# playbooks/deploy-app.yml
---
- name: Orkestrasi Zero-Downtime Deployment
hosts: localhost
connection: local
vars:
app_name: "web-app"
app_namespace: "production"
target_version: "{{ version | mandatory }}" # Wajib dilewatkan melalui CLI: -e version=v2.2.0
app_image: "myregistry.local/web-app"
kubeconfig_path: "~/.kube/config"
app_url: "https://webapp.production.local/healthz"
tasks:
- name: Dapatkan metadata Deployment saat ini
kubernetes.core.k8s_info:
kubeconfig: "{{ kubeconfig_path }}"
kind: Deployment
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
register: current_deploy_info
- name: Verifikasi apakah Deployment ada dan simpan versi saat ini
set_fact:
rollback_image: "{{ current_deploy_info.resources[0].spec.template.spec.containers[0].image }}"
when: current_deploy_info.resources | length > 0
- name: Tampilkan informasi versi transisi
debug:
msg:
- "Image lama terdeteksi: {{ rollback_image | default('Tidak ada (Fresh install)') }}"
- "Menuju target image: {{ app_image }}:{{ target_version }}"
- name: Perbarui gambar aplikasi pada Deployment
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig_path }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
spec:
template:
metadata:
annotations:
# Memaksa pembuatan Pod baru meskipun tag image sama (misal: latest)
deployment-triggered-by: "Ansible {{ ansible_date_time.iso8601 }}"
spec:
containers:
- name: web-server
image: "{{ app_image }}:{{ target_version }}"
- name: Tunggu status rollout selesai (Timeout: 5 menit)
kubernetes.core.k8s_rollout_status:
kubeconfig: "{{ kubeconfig_path }}"
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
kind: Deployment
timeout: 300
register: rollout_result
ignore_errors: true
- name: Evaluasi hasil rollout
block:
- name: Gagalkan eksekusi jika status rollout gagal atau timeout
fail:
msg: "Rollout di Kubernetes mengalami kegagalan atau timeout!"
when: rollout_result.failed or (rollout_result.status is not defined)
- name: Jalankan Smoke Test pasca-rollout (Kirim HTTP Request)
uri:
url: "{{ app_url }}"
method: GET
status_code: 200
validate_certs: false
timeout: 10
register: smoke_test_result
retries: 6
delay: 10
# Kita mencoba mengakses endpoint selama 6 kali dengan jeda 10 detik
# untuk memberikan toleransi loading startup aplikasi backend.
rescue:
- name: Peringatan kritis - Memulai pembatalan otomatis (Rollback)
debug:
msg: "Terjadi kesalahan selama rollout atau smoke test gagal! Mengembalikan versi..."
- name: Batalkan pembaruan dan kembalikan ke versi sebelumnya (Rollback)
command: >
kubectl rollout undo deployment/{{ app_name }}
-n {{ app_namespace }}
--kubeconfig {{ kubeconfig_path }}
register: rollback_execution
changed_when: true
- name: Tunggu status rollback selesai
kubernetes.core.k8s_rollout_status:
kubeconfig: "{{ kubeconfig_path }}"
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
kind: Deployment
timeout: 180
- name: Hentikan jalannya playbook dengan pesan kesalahan
fail:
msg: >
Deployment ke versi {{ target_version }} gagal!
Sistem telah di-rollback secara otomatis ke versi {{ rollback_image }}.
Pada playbook di atas, kita merancang struktur penanganan kesalahan menggunakan blok block dan rescue. Jika langkah-langkah di dalam blok utama (seperti menunggu status rollout atau verifikasi smoke test melalui modul uri) mengalami kegagalan, Ansible akan langsung melompat ke bagian rescue. Di dalam rescue, kita mengeksekusi perintah pembatalan kubectl rollout undo untuk memulihkan kluster ke status sebelum playbook dijalankan. Strategi ini sangat krusial untuk mencegah kluster kita berada dalam status setengah rusak (broken state).
Canary Deployments dan Manajemen Lalu Lintas #
Meskipun rolling update bawaan Kubernetes sangat aman, kadang kala kita ingin menguji fitur baru secara terbatas pada sebagian kecil pengguna nyata sebelum menggulirkannya ke seluruh kluster. Teknik ini dinamakan Canary Deployment (mengambil nama burung kenari yang digunakan penambang untuk mendeteksi gas beracun).
Konsep Canary Deployment di Kubernetes #
Canary deployment dapat diimplementasikan di Kubernetes secara sederhana tanpa menggunakan service mesh (seperti Istio atau Linkerd) dengan cara membuat dua objek Deployment yang berbeda namun berbagi label pemilih (label selector) yang sama pada Service yang menghubungkannya.
Misalnya, kita memiliki Service utama bernama app-service yang mengarahkan lalu lintas data ke Pod dengan label app: web-app.
Kita membuat:
- Deployment Utama (Stable): Menjalankan versi
v2.1.0dengan replika sebanyak 9 Pod. - Deployment Canary: Menjalankan versi
v2.2.0dengan replika sebanyak 1 Pod.
Karena Service menyebarkan lalu lintas data secara merata ke semua Pod yang cocok dengan label pemilih, maka secara otomatis sekitar 10% (1 dari 10 Pod) dari total permintaan lalu lintas pengguna akan masuk ke versi Canary baru, sementara 90% sisanya tetap diarahkan ke versi stable yang aman.
Orkestrasi Canary dengan Ansible #
Mari kita buat playbook Ansible yang mengotomatiskan pembuatan Deployment Canary, melakukan verifikasi anomali log/error selama durasi tertentu, dan meningkatkan versi Stable jika pengujian Canary berhasil.
# playbooks/canary-deploy.yml
---
- name: Manajemen Alur Kerja Canary Deployment
hosts: localhost
connection: local
vars:
app_name_stable: "web-app-stable"
app_name_canary: "web-app-canary"
app_namespace: "production"
stable_version: "v2.1.0"
canary_version: "v2.2.0"
kubeconfig_path: "~/.kube/config"
tasks:
- name: Buat Deployment Canary (Kapasitas kecil: 1 Replika)
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig_path }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name_canary }}"
namespace: "{{ app_namespace }}"
spec:
replicas: 1
selector:
matchLabels:
app: web-app # Berbagi label pemilih yang sama dengan stable
track: canary
template:
metadata:
labels:
app: web-app
track: canary
spec:
containers:
- name: web-server
image: "myregistry.local/web-app:{{ canary_version }}"
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
- name: Tunggu Deployment Canary siap melayani traffic
kubernetes.core.k8s_rollout_status:
kubeconfig: "{{ kubeconfig_path }}"
name: "{{ app_name_canary }}"
namespace: "{{ app_namespace }}"
kind: Deployment
timeout: 180
- name: Lakukan observasi lalu lintas data (Tahap Evaluasi)
pause:
minutes: 2
# Selama 2 menit ini, kita membiarkan pengguna menggunakan aplikasi.
# Di dunia nyata, langkah ini bisa diintegrasikan dengan query Prometheus
# untuk memeriksa error rate HTTP 5xx pada pod canary.
- name: Ambil metrik kegagalan dari logging pod canary (Simulasi)
command: >
kubectl logs -n {{ app_namespace }} -l track=canary --tail=100
--kubeconfig {{ kubeconfig_path }}
register: canary_logs
changed_when: false
- name: Evaluasi kestabilan versi Canary
block:
- name: Periksa apakah ada pola error kritis di log
fail:
msg: "Terdeteksi error kritis pada logs canary!"
when: "'ERROR' in canary_logs.stdout or 'FATAL' in canary_logs.stdout"
- name: Promosikan versi Canary menjadi Stable
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig_path }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name_stable }}"
namespace: "{{ app_namespace }}"
spec:
template:
spec:
containers:
- name: web-server
image: "myregistry.local/web-app:{{ canary_version }}"
- name: Tunggu pembaruan versi stable selesai
kubernetes.core.k8s_rollout_status:
kubeconfig: "{{ kubeconfig_path }}"
name: "{{ app_name_stable }}"
namespace: "{{ app_namespace }}"
kind: Deployment
timeout: 300
- name: Hapus Deployment Canary setelah promosi selesai
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig_path }}"
state: absent
kind: Deployment
name: "{{ app_name_canary }}"
namespace: "{{ app_namespace }}"
rescue:
- name: Hapus Deployment Canary karena terdeteksi tidak stabil
kubernetes.core.k8s:
kubeconfig: "{{ kubeconfig_path }}"
state: absent
kind: Deployment
name: "{{ app_name_canary }}"
namespace: "{{ app_namespace }}"
- name: Hentikan playbook dan laporkan kegagalan pengujian Canary
fail:
msg: "Pengujian versi Canary gagal karena terdeteksi error pada log. Deployment Canary telah ditarik."
Orkestrasi Canary di atas memberikan tingkat perlindungan tambahan yang sangat kuat untuk sistem produksi kita. Kita tidak perlu menebak-nebak apakah versi baru kita memiliki bug tersembunyi yang hanya muncul di bawah lalu lintas data dunia nyata.
Perbandingan Strategi Deployment #
Sebagai referensi dalam menentukan desain pipeline aplikasi kita, mari kita rangkum perbandingan beberapa strategi deployment yang umum kita gunakan beserta kelebihan dan kekurangannya:
| Strategi Deployment | Kebutuhan Tambahan Resource | Dampak pada Downtime | Kompleksitas Manajemen | Keunggulan Utama | Kekurangan Utama |
|---|---|---|---|---|---|
| Recreate | 0% (Tidak membutuhkan tambahan resource) | Ada downtime (selama masa penggantian Pod) | Sangat Rendah | Sangat sederhana, tidak ada potensi konflik data versi ganda. | Pengguna mengalami downtime beberapa menit. |
| Rolling Update | Rendah-Sedang (Bergantung pada nilai maxSurge) | Zero-Downtime | Rendah-Sedang | Bawaan Kubernetes, penanganan otomatis, aman. | Aplikasi harus mampu menangani kompatibilitas mundur schema database. |
| Canary | Rendah (Hanya butuh minimal 1 Pod baru tambahan) | Zero-Downtime | Sedang-Tinggi | Membatasi radius kerusakan jika ada bug, bisa validasi user experience nyata. | Memerlukan sistem monitoring yang presisi untuk deteksi error rate. |
| Blue-Green | 100% (Membutuhkan kluster/infra paralel berukuran penuh) | Zero-Downtime | Tinggi | Pengalihan lalu lintas instan via DNS/Load Balancer, rollback instan. | Sangat boros resource, biaya infrastruktur berlipat ganda. |
Ringkasan #
- Konfigurasi
maxUnavailable: 0— Merupakan opsi terbaik untuk menjamin ketersediaan layanan zero-downtime, karena Kubernetes tidak akan mematikan Pod lama sebelum Pod baru Ready.- Wajibkan Pemeriksaan Health Probes — Terapkan startup, readiness, dan liveness probes pada setiap manifest kontainer agar Kubernetes dapat memantau kesiapan internal aplikasi secara akurat.
- Manfaatkan
k8s_rollout_status— Selalu pantau status kemajuan rollout deployment menggunakan modul Ansible ini agar playbook tidak melanjutkan ke langkah berikutnya saat proses penggelaran masih berjalan.- Implementasi Rollback Otomatis — Gunakan blok penanganan error (
block/rescue) untuk memicu rollback instan viakubectl rollout undojika terjadi kegagalan smoke test pasca-deployment.- Evaluasi Canary untuk Rilis Kritis — Gunakan strategi Canary dengan memisahkan label track, jalankan observasi log selama durasi tertentu sebelum mempromosikannya ke versi stable.
- Kompatibilitas Mundur Skema Database — Saat menjalankan deployment bertahap, pastikan aplikasi kita selalu mendukung kompatibilitas mundur karena Pod versi lama dan baru akan berjalan berdampingan selama masa transisi rollout.
← Sebelumnya: Cluster Maintenance Berikutnya: CI/CD Integration →