Cluster Maintenance #
Cluster Kubernetes yang sudah berjalan bukan berarti pekerjaan selesai. Node perlu di-update, komponen perlu di-upgrade, etcd perlu di-backup, dan kadang ada node yang harus diganti atau diperbaiki. Semua operasi maintenance ini memiliki prosedur yang harus diikuti dengan hati-hati — salah langkah bisa menyebabkan downtime. Mengotomasi prosedur ini dengan Ansible memastikan setiap operasi dijalankan dengan urutan yang benar setiap kali.
Drain Node Sebelum Maintenance #
Sebelum melakukan maintenance pada sebuah node (update OS, replace hardware), node harus di-drain terlebih dahulu — semua Pod dipindahkan ke node lain dan node tidak menerima Pod baru:
# playbooks/drain-node.yml
---
- name: Drain node sebelum maintenance
hosts: localhost
vars:
target_node: "{{ node | mandatory }}" # Harus dipass via -e node=k8s-worker-02
tasks:
- name: Cordon node (tandai sebagai tidak schedulable)
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Node
metadata:
name: "{{ target_node }}"
spec:
unschedulable: true
- name: Drain node (evict semua Pod)
command: >
kubectl drain {{ target_node }}
--ignore-daemonsets
--delete-emptydir-data
--grace-period=60
--timeout=300s
--kubeconfig {{ k8s_kubeconfig }}
register: drain_result
- name: Verifikasi node sudah ter-drain
command: >
kubectl get node {{ target_node }}
-o jsonpath='{.spec.unschedulable}'
--kubeconfig {{ k8s_kubeconfig }}
register: node_status
changed_when: false
- name: Konfirmasi node siap untuk maintenance
debug:
msg: "Node {{ target_node }} sudah di-drain. Lakukan maintenance sekarang."
# Jalankan drain untuk node tertentu
ansible-playbook playbooks/drain-node.yml -e node=k8s-worker-02
Maintenance di Node #
Setelah node di-drain, lakukan maintenance yang diperlukan:
# playbooks/maintain-node.yml
---
- name: Lakukan maintenance di node yang sudah di-drain
hosts: "{{ target_node }}"
become: true
tasks:
- name: Update semua package OS
apt:
upgrade: dist
update_cache: true
autoremove: true
- name: Cek apakah reboot diperlukan
stat:
path: /var/run/reboot-required
register: reboot_required
- name: Reboot node jika diperlukan
reboot:
reboot_timeout: 300
post_reboot_delay: 30
when: reboot_required.stat.exists
- name: Tunggu node kembali online
wait_for_connection:
timeout: 300
delay: 10
Uncordon Node Setelah Maintenance #
Setelah maintenance selesai, kembalikan node ke pool yang schedulable:
- name: Uncordon node setelah maintenance selesai
hosts: localhost
tasks:
- name: Uncordon node
command: >
kubectl uncordon {{ target_node }}
--kubeconfig {{ k8s_kubeconfig }}
- name: Tunggu node kembali Ready
kubernetes.core.k8s_info:
kubeconfig: "{{ k8s_kubeconfig }}"
kind: Node
name: "{{ target_node }}"
register: node_info
until: >
node_info.resources[0].status.conditions
| selectattr('type', 'equalto', 'Ready')
| map(attribute='status')
| first == 'True'
retries: 20
delay: 15
Backup etcd #
etcd menyimpan seluruh state cluster. Backup etcd secara rutin adalah wajib:
# playbooks/backup-etcd.yml
---
- name: Backup etcd cluster state
hosts: control_plane[0]
become: true
vars:
backup_dir: /opt/etcd-backups
backup_file: "etcd-backup-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}.db"
tasks:
- name: Buat direktori backup
file:
path: "{{ backup_dir }}"
state: directory
mode: '0700'
- name: Backup etcd snapshot
command: >
etcdctl snapshot save {{ backup_dir }}/{{ backup_file }}
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
environment:
ETCDCTL_API: "3"
register: etcd_backup
- name: Verifikasi integritas backup
command: >
etcdctl snapshot status {{ backup_dir }}/{{ backup_file }}
--write-out=table
environment:
ETCDCTL_API: "3"
register: backup_status
changed_when: false
- name: Tampilkan status backup
debug:
var: backup_status.stdout_lines
- name: Salin backup ke control node lokal
fetch:
src: "{{ backup_dir }}/{{ backup_file }}"
dest: "backups/etcd/{{ inventory_hostname }}/{{ backup_file }}"
flat: true
- name: Hapus backup lama (lebih dari 7 hari)
find:
paths: "{{ backup_dir }}"
age: "7d"
patterns: "etcd-backup-*.db"
register: old_backups
- name: Hapus file backup lama
file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_backups.files }}"
Rolling Upgrade Komponen Kubernetes #
Upgrade Kubernetes harus dilakukan satu versi minor sekaligus (misalnya 1.27 → 1.28 → 1.29):
# playbooks/upgrade-k8s.yml
---
- name: Upgrade Kubernetes — control plane
hosts: control_plane[0]
become: true
tasks:
- name: Upgrade kubeadm ke versi target
apt:
name: "kubeadm={{ k8s_target_version }}-*"
state: present
allow_downgrades: false
- name: Verifikasi upgrade plan
command: "kubeadm upgrade plan v{{ k8s_target_version }}"
register: upgrade_plan
changed_when: false
- name: Tampilkan upgrade plan
debug:
var: upgrade_plan.stdout_lines
- name: Jalankan upgrade control plane
command: "kubeadm upgrade apply v{{ k8s_target_version }} --yes"
register: upgrade_result
- name: Upgrade kubelet dan kubectl
apt:
name:
- "kubelet={{ k8s_target_version }}-*"
- "kubectl={{ k8s_target_version }}-*"
state: present
- name: Reload kubelet
systemd:
name: kubelet
state: restarted
daemon_reload: true
Ringkasan #
- Selalu cordon node sebelum maintenance — ini mencegah Pod baru terjadwal ke node yang akan di-maintenance.
kubectl draindengan--ignore-daemonsetsdan--delete-emptydir-datauntuk memindahkan semua Pod ke node lain.- Uncordon setelah maintenance selesai dan verifikasi node kembali dalam kondisi
Ready.- Backup etcd secara rutin — ini adalah satu-satunya cara recover cluster jika terjadi kegagalan fatal. Backup harus di-copy ke lokasi eksternal, bukan hanya di node itu sendiri.
- Upgrade Kubernetes harus dilakukan satu versi minor sekaligus — skip versi tidak didukung oleh kubeadm.
- Selalu jalankan
kubeadm upgrade plansebelum upgrade — tampilkan apa yang akan diubah dan potensi masalah yang terdeteksi.