Setiap perubahan template Pod pada Deployment menghasilkan satu revisi yang dapat dipanggil kembali. Kemampuan ini membuat Deployment dapat dikembalikan ke keadaan sebelumnya dengan satu perintah, tetapi hanya apabila riwayatnya terpelihara dan penyebab setiap perubahan tercatat. Topik ini membahas cara revisi disimpan, perintah kubectl rollout history dan kubectl rollout undo, anotasi kubernetes.io/change-cause, serta batasan revisionHistoryLimit.
Revisi sebagai ReplicaSet lama#
Deployment tidak menyimpan riwayat dalam berkas atau basis data tersendiri. Setiap kali bidang template Pod berubah, controller membuat ReplicaSet baru untuk template tersebut dan memindahkan replika ke sana, sedangkan ReplicaSet lama dipertahankan dengan spec.replicas bernilai nol. ReplicaSet yang sudah tidak menjalankan Pod berfungsi sebagai arsip, karena template-nya tetap lengkap tersimpan di API server.
Penomoran revisi bertambah satu setiap kali template berubah, bukan setiap kali Pod berganti. Nomor urut tersebut berasal dari anotasi deployment.kubernetes.io/revision yang ditempel controller pada setiap ReplicaSet-nya, sehingga nomor revisi tampil konsisten pada seluruh perintah yang membacanya. Penyesuaian jumlah replika, misalnya melalui kubectl scale, tidak membuat revisi baru karena perubahan itu tidak menyentuh template Pod. Sebaliknya, perubahan image, variabel lingkungan, atau probe selalu menghasilkan revisi, sekecil apa pun perbedaannya.
Daftar revisi dapat dilihat dengan kubectl rollout history:
# tidak-dijalankan
# daftar revisi Deployment beserta change-cause-nya
kubectl rollout history deployment catatandeployment.apps/catatan
REVISION CHANGE-CAUSE
1 deploy awal
2 naikkan versi image ke nginx 1.27
3 naikkan versi image ke nginx 1.28Kolom CHANGE-CAUSE berisi isi anotasi kubernetes.io/change-cause pada saat revisi dibuat. Tanpa anotasi, kolom tersebut hanya menampilkan <none>, sehingga daftar revisi kehilangan nilai dokumentasinya. Rincian satu revisi, termasuk template Pod lengkapnya, dapat dilihat dengan menambahkan opsi --revision pada perintah yang sama. Opsi ini berguna untuk memastikan isi revisi sebelum nomornya dipilih pada perintah pemulihan.
Anotasi kubernetes.io/change-cause#
Anotasi kubernetes.io/change-cause adalah tempat resmi mencatat alasan perubahan pada satu revisi. Isinya teks bebas yang dibaca perintah kubectl rollout history, sehingga riwayat berubah dari daftar nomor menjadi log perubahan yang dapat dibaca manusia. Anotasi yang sudah ada sejak objek dibuat juga ikut tersalin, sehingga revisi pertama pun dapat membawa catatan penyebabnya. Nilai yang tercatat pada satu revisi adalah nilai anotasi yang berlaku saat revisi tersebut dibuat, sehingga anotasi perlu ditulis sebelum template disentuh.
Opsi --record yang dulu mengisi anotasi ini secara otomatis sudah ditinggalkan oleh kubectl, sehingga pencatatan dilakukan secara eksplisit dengan kubectl annotate:
# tidak-dijalankan
# catat alasan perubahan sebelum template disentuh
kubectl annotate deployment catatan kubernetes.io/change-cause="naikkan versi image ke nginx 1.27"
# ubah image, perubahan ini menciptakan revisi baru
kubectl set image deployment catatan catatan=nginx:1.27deployment.apps/catatan annotated
deployment.apps/catatan image updatedMutu riwayat ditentukan oleh mutu catatannya. Pesan yang hanya berisi “naikkan versi” tidak membantu apabila terulang lima kali dalam sebulan, sedangkan pesan yang menyebut nomor versi image dan alasan perubahannya dapat langsung dipertimbangkan saat insiden. Kebiasaan yang sehat adalah memperlakukan change-cause seperti pesan commit pada repository git, yaitu singkat, spesifik, dan ditulis pada saat perubahan terjadi. Secara teknis, anotasi ini tersimpan pada ReplicaSet yang mewakili revisi, karena seluruh anotasi Deployment disalin ke ReplicaSet baru saat revisi dibuat. Konsekuensinya, nilai yang tampil pada satu revisi tidak berubah meskipun anotasi pada Deployment diperbarui di kemudian hari.
Undo dan pemangkasan riwayat#
Perintah kubectl rollout undo mengembalikan template Pod ke revisi sebelumnya, dengan atau tanpa opsi --to-revision untuk memilih revisi tertentu. Yang terjadi bukan pemutaran ulang sejarah, melainkan penyalinan template revisi lama menjadi revisi baru, sehingga nomor revisi terus bertambah dan tidak ada riwayat yang tertimpa. Pod lama dihentikan dan Pod baru dibuat dari template lama, sehingga proses pemulihan melewati strategi perputaran yang sama seperti pembaruan biasa. Perintah kubectl rollout pause dan kubectl rollout resume melengkapi kendali ini dengan menghentikan dan melanjutkan perputaran di tengah jalan.
# tidak-dijalankan
# kembalikan template pod ke revisi sebelumnya
kubectl rollout undo deployment catatan
# hentikan perputaran di tengah jalan, misalnya untuk pemeriksaan
kubectl rollout pause deployment catatan
# lanjutkan perputaran yang dijeda
kubectl rollout resume deployment catatandeployment.apps/catatan rolled back
deployment.apps/catatan paused
deployment.apps/catatan resumedJumlah revisi yang dipertahankan dikendalikan oleh revisionHistoryLimit pada spec Deployment. Nilai default-nya sepuluh, dan revisi yang melewati batas dihapus bersama ReplicaSet arsipnya. Sementara perputaran dijeda, perubahan template hanya tersimpan dan tidak memicu perputaran, sehingga beberapa perbaikan dapat ditumpuk dan diterapkan sekaligus ketika perputaran dilanjutkan.
Simulasi berikut meniru penomoran revisi, pemangkasan sesuai revisionHistoryLimit, dan pemulihan ke revisi tertentu:
RIWAYAT = [ # daftar revisi: nomor, image, dan change-cause
{"no": 1, "image": "nginx:1.26", "alasan": "deploy awal"},
{"no": 2, "image": "nginx:1.27", "alasan": "naikkan versi image"},
{"no": 3, "image": "nginx:1.27", "alasan": "tambah variabel lingkungan"},
{"no": 4, "image": "nginx:1.28", "alasan": "naikkan versi image"},
{"no": 5, "image": "nginx:1.29", "alasan": "naikkan versi image"},
]
BATAS = 3 # revisionHistoryLimit
NOMOR = 6 # nomor revisi berikutnya
def cetak(label: str) -> None:
print(label)
for r in RIWAYAT:
print(f" revisi {r['no']}: {r['image']} ({r['alasan']})")
print()
def simpan(image: str, alasan: str) -> None:
global NOMOR
RIWAYAT.append({"no": NOMOR, "image": image, "alasan": alasan})
NOMOR += 1
while len(RIWAYAT) > BATAS: # buang revisi terlama sampai sisa BATAS
RIWAYAT.pop(0)
def undo(no: int) -> None:
sumber = next(r for r in RIWAYAT if r["no"] == no)
simpan(sumber["image"], f"undo ke revisi {no}")
cetak("Riwayat awal:")
simpan("nginx:1.30", "naikkan versi image")
cetak(f"Setelah revisi {NOMOR - 1} dibuat (revisionHistoryLimit = {BATAS}):")
undo(4)
cetak("Setelah undo ke revisi 4:")Riwayat awal:
revisi 1: nginx:1.26 (deploy awal)
revisi 2: nginx:1.27 (naikkan versi image)
revisi 3: nginx:1.27 (tambah variabel lingkungan)
revisi 4: nginx:1.28 (naikkan versi image)
revisi 5: nginx:1.29 (naikkan versi image)
Setelah revisi 6 dibuat (revisionHistoryLimit = 3):
revisi 4: nginx:1.28 (naikkan versi image)
revisi 5: nginx:1.29 (naikkan versi image)
revisi 6: nginx:1.30 (naikkan versi image)
Setelah undo ke revisi 4:
revisi 5: nginx:1.29 (naikkan versi image)
revisi 6: nginx:1.30 (naikkan versi image)
revisi 7: nginx:1.28 (undo ke revisi 4)Keluaran memperlihatkan revisi satu sampai tiga dibuang begitu revisi enam dibuat, karena batasnya tiga. Pemulihan ke revisi empat tidak menghidupkan kembali revisi empat, melainkan menyalinnya sebagai revisi tujuh dengan image nginx 1.28. Revisi terlama tetap dibuang, sehingga isi riwayat selalu bergerak maju meskipun keadaannya mundur.
Kesalahan umum#
- Mengandalkan opsi
--recorduntuk mengisi change-cause membuat revisi baru tampil tanpa alasan, karena opsi tersebut sudah ditinggalkan oleh kubectl. Perbaikannya, tulis anotasikubernetes.io/change-causesecara eksplisit sebelum mengubah template. - Memperbarui anotasi setelah revisi dibuat tidak mengubah kolom CHANGE-CAUSE revisi tersebut, karena catatan telah tersalin ke ReplicaSet arsipnya. Perbaikannya, tulis anotasi lebih dahulu, lalu ubah template pada baris perintah berikutnya.
- Menurunkan
revisionHistoryLimitterlalu agresif membuat template yang masih dibutuhkan sudah terhapus saat pemulihan. Perbaikannya, tetapkan batas yang seimbang, misalnya lima sampai sepuluh, dan perlakukan riwayat sebagai cadangan pemulihan.
Ringkasan#
- Setiap perubahan template Pod menghasilkan revisi baru berupa ReplicaSet lama yang jumlah replikanya nol.
- Anotasi
kubernetes.io/change-causemencatat alasan perubahan dan tampil pada kolom CHANGE-CAUSEkubectl rollout history. kubectl rollout undomenyalin template revisi lama menjadi revisi baru, dengan atau tanpa opsi--to-revision.revisionHistoryLimitmembatasi jumlah revisi yang dipertahankan, dengan nilai default sepuluh.
Referensi#
- Deployments — Kubernetes — Dokumentasi resmi tentang revisi Deployment, rollback, dan riwayat rollout.
- kubectl Cheat Sheet — Kubernetes — Ringkasan perintah kubectl yang digunakan sehari-hari, termasuk rollout dan annotate.