Cluster Maintenance

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 drain dengan --ignore-daemonsets dan --delete-emptydir-data untuk 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 plan sebelum upgrade — tampilkan apa yang akan diubah dan potensi masalah yang terdeteksi.

← Sebelumnya: Helm   Berikutnya: Rolling Update →

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