Cluster Maintenance

Cluster Maintenance #

Mengelola kluster Kubernetes di lingkungan produksi membutuhkan perhatian ekstra terhadap aspek keandalan dan ketersediaan layanan. Infrastruktur yang berjalan tidak bersifat statis; kita harus secara rutin melakukan pemeliharaan (maintenance) seperti pembaruan sistem operasi node, peningkatan (upgrade) versi Kubernetes, serta pencadangan (backup) berkala pada database etcd. Melakukan tugas-tugas pemeliharaan ini secara manual pada puluhan atau ratusan node tidak hanya melelahkan, tetapi juga meningkatkan risiko kesalahan konfigurasi manusia (human error) yang dapat akibat downtime. Dengan memanfaatkan Ansible, kita dapat menyusun alur kerja pemeliharaan yang terorkestrasi, konsisten, terdokumentasi, dan dapat diulang dengan aman tanpa mengorbankan stabilitas aplikasi yang sedang berjalan.


Manajemen Siklus Hidup Node: Cordon dan Drain #

Sebelum kita menyentuh server fisik atau mesin virtual yang bertindak sebagai worker node untuk melakukan pemeliharaan seperti pembaruan kernel atau penggantian perangkat keras, kita harus memastikan bahwa tidak ada beban kerja (workload) yang terganggu. Proses pemindahan beban kerja ini di dalam Kubernetes dikenal dengan istilah cordon (menandai node agar tidak menerima Pod baru) dan drain (mengeluarkan Pod yang sedang berjalan secara ramah ke node lain).

Jika kita langsung mematikan atau men-reboot server tanpa melakukan proses ini, aplikasi yang berjalan di dalamnya akan mengalami pemutusan koneksi secara mendadak. Pod yang mati membutuhkan waktu untuk dideteksi oleh control plane sebelum akhirnya dijadwalkan ulang di node lain. Selama jeda waktu tersebut, pengguna akan mengalami gangguan layanan.

Mengapa Cordon dan Drain Harus Berurutan? #

Proses pemeliharaan node harus mengikuti urutan logis yang ketat. Pertama, kita menandai node sebagai unschedulable (cordon). Hal ini mencegah scheduler Kubernetes menempatkan Pod baru di node tersebut saat proses pemindahan Pod lama sedang berlangsung. Kedua, kita melakukan pengosongan node (drain) yang akan mengevakuasi semua Pod aktif ke node worker lain yang masih sehat.

Berikut adalah visualisasi alur kerja manajemen pemeliharaan node yang aman:

flowchart TD
    A["Mulai Maintenance Node"] --> B["Cordon Node (spec.unschedulable)"]
    B --> C["Drain Node (Eviction)"]
    C --> D{"Apakah ada DaemonSet / emptyDir?"}
    D -- "Ya" --> E["Gunakan --ignore-daemonsets & --delete-emptydir-data"]
    D -- "Tidak" --> F["Evakuasi Pod Selesai"]
    E --> F
    F --> G["Lakukan Update OS / Package (Kubeadm, Kubelet)"]
    G --> H["Reboot Node (Jika Dibutuhkan)"]
    H --> I["Uncordon Node"]
    I --> J["Verifikasi Node Ready"]
    J --> K["Selesai"]

Anti-Pattern vs Solusi Otomasi Cordon-Drain #

Mari kita pelajari contoh kesalahan umum dalam pemeliharaan node dan bagaimana solusi yang tepat menggunakan modul Ansible.

# ANTI-PATTERN: Melakukan pemeliharaan node langsung dengan reboot atau tanpa drain terpadu
- name: Reboot langsung worker node
  hosts: worker_nodes
  become: true
  tasks:
    - name: Restart server langsung
      reboot:
        reboot_timeout: 300
      # JANGAN lakukan ini! Pod akan mati mendadak, traffic akan drop, 
      # dan scheduler memerlukan waktu hingga 5 menit (pod-eviction-timeout) 
      # untuk menjadwalkan ulang Pod ke node lain.

# BENAR: Menggunakan koordinasi terpadu untuk cordon dan drain secara anggun
- name: Cordon dan drain node secara anggun sebelum maintenance
  hosts: localhost
  vars:
    target_node: "k8s-worker-01"
  tasks:
    - name: Tandai node sebagai unschedulable (Cordon)
      kubernetes.core.k8s:
        kubeconfig: "~/.kube/config"
        state: present
        definition:
          apiVersion: v1
          kind: Node
          metadata:
            name: "{{ target_node }}"
          spec:
            unschedulable: true

    - name: Evakuasi beban kerja dari node (Drain)
      command: >
        kubectl drain {{ target_node }}
        --ignore-daemonsets
        --delete-emptydir-data
        --grace-period=60
        --timeout=300s
        --kubeconfig ~/.kube/config        
      register: drain_result
      changed_when: true

Dalam contoh solusi di atas, kita menggunakan modul kubernetes.core.k8s untuk memanipulasi spesifikasi node secara langsung guna mengaktifkan status unschedulable. Setelah itu, kita menggunakan perintah kubectl drain yang dibungkus dalam modul command karena modul native k8s belum menyediakan pembungkus khusus untuk perintah drain yang seandel CLI bawaan. Parameter --ignore-daemonsets wajib kita sertakan karena Pod yang dikelola oleh DaemonSet berjalan di setiap node dan tidak dapat dipindahkan. Parameter --delete-emptydir-data memastikan Pod yang menggunakan penyimpanan lokal sementara (emptyDir) tetap dapat dievakuasi meskipun datanya akan terhapus.

Mengatasi Hambatan Drain (DaemonSets, Local Storage, dan Grace Period) #

Saat kita menjalankan proses drain, terkadang kita menemui kegagalan akibat adanya Pod yang tidak dikelola oleh Controller (seperti Deployment, StatefulSet, atau ReplicaSet), atau adanya Pod yang menggunakan penyimpanan lokal. Kita harus menangani kasus ini dengan parameter tambahan.

Jika terdapat Pod mandiri (standalone Pod) yang dibuat langsung tanpa controller, Kubernetes secara default akan menolak proses drain untuk menghindari kehilangan data secara permanen. Jika kita yakin Pod tersebut aman untuk dihapus, kita bisa menambahkan parameter --force pada perintah drain kita. Selain itu, parameter --grace-period=60 memberikan waktu bagi aplikasi di dalam Pod untuk menyelesaikan sisa proses atau koneksi aktif (graceful shutdown) sebelum sistem mengirimkan sinyal SIGKILL.

Setelah tugas pemeliharaan selesai, kita harus mengembalikan node agar dapat menerima Pod kembali. Proses ini dinamakan uncordon. Berikut adalah playbook Ansible untuk mengembalikan fungsionalitas penjadwalan node:

- name: Kembalikan node ke status aktif (Uncordon)
  hosts: localhost
  vars:
    target_node: "k8s-worker-01"
  tasks:
    - name: Jadwalkan kembali Pod ke node (Uncordon)
      kubernetes.core.k8s:
        kubeconfig: "~/.kube/config"
        state: present
        definition:
          apiVersion: v1
          kind: Node
          metadata:
            name: "{{ target_node }}"
          spec:
            unschedulable: false

    - name: Tunggu status node menjadi Ready
      kubernetes.core.k8s_info:
        kubeconfig: "~/.kube/config"
        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: 10

Dengan alur ini, kita menjamin bahwa transisi node sebelum dan sesudah pemeliharaan terpantau secara penuh oleh sistem monitoring Ansible kita.


Strategi Upgrade Komponen Kubernetes (Kubeadm, Kubelet, dan Kubectl) #

Proses peningkatan versi (upgrade) kluster Kubernetes adalah tugas rutin yang wajib dijalankan untuk memastikan kita mendapatkan pembaruan keamanan, perbaikan bug, dan fitur-fitur terbaru. Kubernetes merekomendasikan agar kita melakukan upgrade maksimal satu versi minor dalam satu waktu (misalnya dari versi 1.28 ke 1.29, bukan melompat langsung dari 1.28 ke 1.30).

Proses upgrade harus dimulai dari control plane terlebih dahulu sebelum dilanjutkan ke worker node. Komponen pertama yang di-upgrade adalah kubeadm, kemudian konfigurasi control plane, diikuti oleh kubelet dan kubectl pada node control plane, baru setelah itu kita memperbarui worker node satu per satu.

Prosedur Upgrade Control Plane secara Terorkestrasi #

Mari kita buat playbook Ansible yang aman untuk memproses peningkatan komponen pada control plane. Playbook ini akan memperbarui repositori paket, melakukan instalasi kubeadm versi baru, menerapkan konfigurasi upgrade Kubernetes, dan memperbarui kubelet.

# playbooks/upgrade-control-plane.yml
---
- name: Peningkatan Versi Kubernetes Control Plane
  hosts: control_plane
  become: true
  serial: 1
  vars:
    target_version: "1.29.2"
    debian_version: "1.29.2-1.1" # Sesuaikan dengan format penamaan paket distribusi
  tasks:
    - name: Perbarui cache repositori paket apt
      apt:
        update_cache: true

    - name: Hapus proteksi hold pada kubeadm
      dpkg_selections:
        name: kubeadm
        selection: install

    - name: Install kubeadm versi target
      apt:
        name: "kubeadm={{ debian_version }}"
        state: present
        update_cache: true

    - name: Pasang kembali proteksi hold pada kubeadm
      dpkg_selections:
        name: kubeadm
        selection: hold

    - name: Jalankan simulasi verifikasi upgrade (Upgrade Plan)
      command: "kubeadm upgrade plan v{{ target_version }}"
      register: upgrade_plan
      changed_when: false

    - name: Tampilkan hasil simulasi upgrade
      debug:
        var: upgrade_plan.stdout_lines

    - name: Terapkan upgrade control plane
      command: "kubeadm upgrade apply v{{ target_version }} --yes"
      register: upgrade_apply_result
      changed_when: "'Upgrade complete!' in upgrade_apply_result.stdout"

    - name: Hapus proteksi hold pada kubelet dan kubectl
      dpkg_selections:
        name: "{{ item }}"
        selection: install
      loop:
        - kubelet
        - kubectl

    - name: Upgrade kubelet dan kubectl
      apt:
        name:
          - "kubelet={{ debian_version }}"
          - "kubectl={{ debian_version }}"
        state: present

    - name: Terapkan kembali proteksi hold pada kubelet dan kubectl
      dpkg_selections:
        name: "{{ item }}"
        selection: hold
      loop:
        - kubelet
        - kubectl

    - name: Muat ulang daemon systemd
      systemd:
        daemon_reload: true

    - name: Restart layanan kubelet
      systemd:
        name: kubelet
        state: restarted
        enabled: true

Playbook di atas memanfaatkan perintah dpkg_selections untuk melepas sementara status hold pada paket APT. Status hold ini sangat penting di lingkungan produksi agar paket-paket sistem Kubernetes tidak ter-upgrade secara tidak sengaja saat administrator menjalankan perintah apt-get upgrade harian. Setelah kita memasang versi yang sesuai, kita wajib mengembalikan status hold tersebut.

Orkestrasi Rolling Upgrade Node Worker #

Setelah control plane berhasil ditingkatkan, kita dapat meningkatkan versi paket pada seluruh worker node. Kita tidak boleh memperbarui semua worker node sekaligus karena akan menyebabkan kehabisan kapasitas kluster dan downtime aplikasi. Kita harus mengorkestrasinya secara bergantian (rolling upgrade) menggunakan parameter serial: 1 pada level play.

Berikut adalah playbook untuk melakukan rolling upgrade pada seluruh worker node:

# playbooks/upgrade-worker-nodes.yml
---
- name: Rolling Upgrade Paket Worker Nodes
  hosts: worker_nodes
  become: true
  serial: 1  # Memproses node satu per satu untuk mencegah downtime
  vars:
    target_version: "1.29.2"
    debian_version: "1.29.2-1.1"
    kubeconfig_path: "/home/ansible/.kube/config"

  tasks:
    - name: Drain worker node dari kontrol lokal (Control Node)
      delegate_to: localhost
      become: false
      command: >
        kubectl drain {{ inventory_hostname }}
        --ignore-daemonsets
        --delete-emptydir-data
        --grace-period=60
        --timeout=300s
        --kubeconfig {{ kubeconfig_path }}        
      changed_when: true

    - name: Hapus proteksi hold pada komponen node
      dpkg_selections:
        name: "{{ item }}"
        selection: install
      loop:
        - kubeadm
        - kubelet
        - kubectl

    - name: Upgrade kubeadm di worker node
      apt:
        name: "kubeadm={{ debian_version }}"
        state: present
        update_cache: true

    - name: Jalankan konfigurasi upgrade worker node
      command: kubeadm upgrade node
      register: node_upgrade_result
      changed_when: "'Successfully upgraded' in node_upgrade_result.stdout"

    - name: Upgrade kubelet dan kubectl di worker node
      apt:
        name:
          - "kubelet={{ debian_version }}"
          - "kubectl={{ debian_version }}"
        state: present

    - name: Kembalikan status hold pada komponen node
      dpkg_selections:
        name: "{{ item }}"
        selection: hold
      loop:
        - kubeadm
        - kubelet
        - kubectl

    - name: Muat ulang konfigurasi systemd
      systemd:
        daemon_reload: true

    - name: Restart layanan kubelet
      systemd:
        name: kubelet
        state: restarted

    - name: Kembalikan node agar dapat dijadwalkan kembali (Uncordon)
      delegate_to: localhost
      become: false
      command: >
        kubectl uncordon {{ inventory_hostname }}
        --kubeconfig {{ kubeconfig_path }}        
      changed_when: true

    - name: Verifikasi kestabilan node
      delegate_to: localhost
      become: false
      kubernetes.core.k8s_info:
        kubeconfig: "{{ kubeconfig_path }}"
        kind: Node
        name: "{{ inventory_hostname }}"
      register: node_status
      until: >
        node_status.resources[0].status.conditions 
        | selectattr('type', 'equalto', 'Ready') 
        | map(attribute='status') 
        | first == 'True'        
      retries: 15
      delay: 10

Dalam playbook ini, teknik delegate_to: localhost digunakan untuk menjalankan perintah administratif kubectl langsung dari control node (mesin tempat Ansible dijalankan) yang memiliki otorisasi file kubeconfig. Hal ini menghindarkan kita dari keharusan menyalin file sensitif kubeconfig ke seluruh worker node. Langkah ini meningkatkan postur keamanan infrastruktur kita secara signifikan.


Backup dan Pemulihan etcd secara Terdistribusi #

Komponen etcd adalah pilar utama dari kluster Kubernetes yang bertindak sebagai penyimpanan data key-value yang konsisten dan terdistribusi. Seluruh definisi objek Kubernetes, status kluster, konfigurasi jaringan, dan rahasia aplikasi (Secrets) disimpan di dalam etcd. Jika terjadi kegagalan fatal pada data etcd tanpa adanya backup, kluster kita tidak akan bisa diselamatkan. Oleh karena itu, menyusun sistem pencadangan etcd otomatis adalah kewajiban mutlak bagi tim operasi.

Cara Kerja Snapshot etcd #

Pencadangan etcd dilakukan dengan membuat snapshot point-in-time dari data store menggunakan perintah etcdctl. Karena etcd dalam kluster produksi biasanya dikonfigurasi menggunakan TLS untuk enkripsi lalu lintas data, proses backup memerlukan akses ke sertifikat enkripsi kluster seperti Certificate Authority (CA) root, sertifikat server, dan private key-nya.

Berikut adalah visualisasi alur pencadangan etcd terdistribusi menggunakan kontroler otomatis:

flowchart TD
    A["Trigger Backup Scheduler"] --> B["Dapatkan Credential Cert/Key etcd"]
    B --> C["Jalankan etcdctl snapshot save"]
    C --> D{"Verifikasi Integritas?"}
    D -- "Ya (Valid)" --> E["Salin Snapshot ke Control Node (Fetch)"]
    D -- "Tidak (Gagal)" --> F["Kirim Notifikasi Alert"]
    E --> G["Terapkan Retention Policy (Hapus Backup >7 Hari)"]
    G --> H["Selesai"]

Playbook Ansible untuk Backup etcd secara Otomatis #

Mari kita buat playbook Ansible yang melakukan eksekusi backup etcd secara berkala pada node control plane, memverifikasi integritas file cadangan tersebut, mengunduhnya ke tempat penyimpanan terpusat, dan menerapkan kebijakan retensi penyimpanan.

# playbooks/backup-etcd.yml
---
- name: Backup Database etcd Kubernetes
  hosts: control_plane[0] # Cukup jalankan di salah satu node control plane
  become: true
  vars:
    etcd_backup_dir: "/var/backups/etcd"
    etcd_certs_dir: "/etc/kubernetes/pki/etcd"
    local_backup_dest: "/opt/backup-center/k8s-etcd"
  tasks:
    - name: Buat direktori penyimpanan backup lokal di target host
      file:
        path: "{{ etcd_backup_dir }}"
        state: directory
        mode: '0700'
        owner: root
        group: root

    - name: Dapatkan cap waktu untuk nama file unik
      set_fact:
        backup_timestamp: "{{ lookup('pipe', 'date +%Y%m%d-%H%M%S') }}"

    - name: Jalankan snapshot etcdctl
      command: >
        etcdctl snapshot save {{ etcd_backup_dir }}/etcd-snap-{{ backup_timestamp }}.db
        --endpoints=https://127.0.0.1:2379
        --cacert={{ etcd_certs_dir }}/ca.crt
        --cert={{ etcd_certs_dir }}/server.crt
        --key={{ etcd_certs_dir }}/server.key        
      environment:
        ETCDCTL_API: "3"
      register: etcd_backup_result

    - name: Verifikasi integritas file snapshot yang baru dibuat
      command: >
        etcdctl snapshot status {{ etcd_backup_dir }}/etcd-snap-{{ backup_timestamp }}.db
        --write-out=table        
      environment:
        ETCDCTL_API: "3"
      register: etcd_status_result
      changed_when: false

    - name: Pastikan file backup valid sebelum diproses lebih lanjut
      assert:
        that:
          - "'total size' in etcd_status_result.stdout"
        fail_msg: "File snapshot etcd mengalami kerusakan atau tidak valid!"

    - name: Buat direktori backup di mesin Ansible Control
      delegate_to: localhost
      become: false
      file:
        path: "{{ local_backup_dest }}"
        state: directory
        mode: '0750'

    - name: Ambil file backup ke penyimpanan terpusat (Ansible Control Node)
      fetch:
        src: "{{ etcd_backup_dir }}/etcd-snap-{{ backup_timestamp }}.db"
        dest: "{{ local_backup_dest }}/etcd-snap-{{ inventory_hostname }}-{{ backup_timestamp }}.db"
        flat: true

    - name: Cari cadangan etcd yang lebih lama dari 7 hari di target host
      find:
        paths: "{{ etcd_backup_dir }}"
        age: "7d"
        patterns: "etcd-snap-*.db"
      register: old_backups

    - name: Hapus cadangan etcd yang sudah kedaluwarsa di target host
      file:
        path: "{{ item.path }}"
        state: absent
      loop: "{{ old_backups.files }}"

Prosedur Pemulihan (Restore) etcd dalam Kondisi Darurat #

Ketika kluster kita mengalami kerusakan data total, etcd harus dipulihkan sebelum API server Kubernetes dapat berjalan kembali. Proses pemulihan ini harus dijalankan dengan sangat hati-hati karena etcd harus dihentikan sepenuhnya di semua node control plane terlebih dahulu untuk mencegah konflik sinkronisasi status.

Langkah-langkah pemulihan etcd adalah sebagai berikut:

  1. Hentikan semua manifest static pod kube-apiserver dan etcd dengan memindahkan file definisinya dari direktori manifest /etc/kubernetes/manifests agar systemd/kubelet mematikan container tersebut.
  2. Hapus direktori data etcd lama yang rusak di /var/lib/etcd.
  3. Jalankan perintah etcdctl snapshot restore dengan parameter nama node, alamat IP peer, dan daftar inisialisasi kluster etcd yang sesuai.
  4. Pindahkan kembali manifest static pod ke direktori semula agar kubelet menyalakan kembali API server dan etcd.

Berikut adalah contoh skrip pemulihan etcd yang diotomatiskan dengan Ansible:

# playbooks/restore-etcd.yml
---
- name: Pemulihan etcd Kluster Kubernetes dari Backup
  hosts: control_plane
  become: true
  vars:
    etcd_data_dir: "/var/lib/etcd"
    manifest_dir: "/etc/kubernetes/manifests"
    backup_file_path: "/tmp/etcd-snap-restore.db" # Pastikan file backup sudah disalin ke direktori ini
    etcd_certs_dir: "/etc/kubernetes/pki/etcd"
    cluster_token: "etcd-k8s-cluster"
  tasks:
    - name: Periksa ketersediaan file backup di host
      stat:
        path: "{{ backup_file_path }}"
      register: backup_file_status

    - name: Gagalkan jika file backup tidak ditemukan
      fail:
        msg: "File backup tidak ditemukan di {{ backup_file_path }}!"
      when: not backup_file_status.stat.exists

    - name: Nonaktifkan API Server dan etcd dengan memindahkan manifes static pod
      file:
        path: "{{ manifest_dir }}/{{ item }}"
        state: absent
      loop:
        - kube-apiserver.yaml
        - etcd.yaml
      # Langkah ini penting agar kubelet mematikan pod apiserver dan etcd saat ini.

    - name: Beri jeda agar container benar-benar berhenti
      pause:
        seconds: 15

    - name: Hapus direktori data etcd yang lama (Backup data lama terlebih dahulu jika perlu)
      file:
        path: "{{ etcd_data_dir }}"
        state: absent

    - name: Jalankan restorasi etcd snapshot lokal
      command: >
        etcdctl snapshot restore {{ backup_file_path }}
        --name={{ inventory_hostname }}
        --initial-cluster={{ inventory_hostname }}=https://{{ ansible_default_ipv4.address }}:2380
        --initial-cluster-token={{ cluster_token }}
        --initial-advertise-peer-urls=https://{{ ansible_default_ipv4.address }}:2380
        --data-dir={{ etcd_data_dir }}        
      environment:
        ETCDCTL_API: "3"

    - name: Pastikan hak kepemilikan direktori data etcd baru sesuai
      file:
        path: "{{ etcd_data_dir }}"
        state: directory
        owner: root
        group: root
        recurse: true

    - name: Aktifkan kembali API Server dan etcd dengan memulihkan manifes static pod
      # Kita bisa menyalinnya kembali dari direktori backup aman
      copy:
        src: "/etc/kubernetes/manifests-backup/{{ item }}"
        dest: "{{ manifest_dir }}/{{ item }}"
        remote_src: true
      loop:
        - etcd.yaml
        - kube-apiserver.yaml

Proses pemulihan ini harus disesuaikan dengan konfigurasi jaringan kluster masing-masing. Sangat penting bagi kita untuk menguji skrip pemulihan ini di kluster staging secara berkala agar saat terjadi kegagalan nyata di produksi, tim operasional dapat bertindak dengan tenang dan terarah.


Penanganan Node Bermasalah dan Pemulihan Otomatis #

Selain tugas pemeliharaan terjadwal, kluster Kubernetes seringkali menghadapi situasi darurat di mana node tiba-tiba berganti status menjadi NotReady atau mengalami tekanan sumber daya (resource pressure). Faktor penyebabnya bisa beragam, mulai dari kehabisan memori (OOM), kegagalan disk, hingga crash pada daemon container runtime seperti Docker atau Containerd.

Sebagai administrator, kita dapat memanfaatkan Ansible untuk melakukan deteksi dini (drift detection / health check) dan melakukan tindakan pemulihan mandiri (self-healing) secara otomatis tanpa menunggu tiket keluhan masuk ke meja bantuan TI.

Memahami Kondisi Status Node #

Kubernetes mengekspos beberapa kondisi kesehatan node. Kita dapat memantau kondisi ini secara terperinci menggunakan modul Ansible. Berikut adalah ringkasan indikator kondisi utama pada node dan tindakan remediasi yang bisa kita ambil melalui skrip Ansible:

Kondisi Node Deskripsi Masalah Tindakan Pemulihan dengan Ansible
Ready Node dalam kondisi sehat dan dapat menerima Pod. Tidak ada tindakan yang diperlukan.
DiskPressure Kapasitas penyimpanan lokal menipis (<10%). Jalankan pembersihan cache docker, hapus image yang tidak terpakai, dan bersihkan direktori log.
MemoryPressure Kapasitas RAM menipis, berisiko terjadi OOM. Identifikasi Pod non-essential dengan konsumsi RAM tinggi, pindahkan beban kerja secara rolling.
PIDPressure Terlalu banyak proses berjalan pada node. Periksa limitasi max PID di systemd, restart proses zombie, batasi thread container.
NetworkUnavailable Konfigurasi jaringan node terganggu. Restart daemon CNI (seperti Calico/Cilium), periksa tabel routing, muat ulang modul jaringan kernel.

Playbook Remediasi Otomatis untuk Node Bermasalah #

Mari kita buat playbook Ansible defensif yang memantau status keaktifan layanan runtime kontainer (containerd) dan agen kubelet pada setiap node worker. Jika layanan tersebut mati, Ansible akan mendeteksi dan mencoba menghidupkannya kembali, lalu memverifikasi pemulihannya.

# playbooks/node-remediation.yml
---
- name: Deteksi dan Remediasi Otomatis Layanan Node Worker
  hosts: worker_nodes
  become: true
  tasks:
    - name: Cek status keaktifan runtime containerd
      ansible.builtin.systemd:
        name: containerd
      register: containerd_status

    - name: Cek status keaktifan agen kubelet
      ansible.builtin.systemd:
        name: kubelet
      register: kubelet_status

    - name: Lakukan tindakan pemulihan jika containerd mati
      ansible.builtin.systemd:
        name: containerd
        state: restarted
      when: containerd_status.status.ActiveState != 'active'
      register: containerd_remediation

    - name: Beri jeda 10 detik agar containerd menginisialisasi soket
      pause:
        seconds: 10
      when: containerd_remediation.changed

    - name: Lakukan tindakan pemulihan jika kubelet mati
      ansible.builtin.systemd:
        name: kubelet
        state: restarted
      when: kubelet_status.status.ActiveState != 'active'

    - name: Bersihkan cache gambar tidak terpakai jika DiskPressure terjadi
      block:
        - name: Dapatkan informasi disk space root
          ansible.builtin.setup:
            filter: "ansible_mounts"

        - name: Hitung persentase penggunaan disk root
          set_fact:
            root_disk: "{{ ansible_mounts | selectattr('mount', 'equalto', '/') | first }}"

        - name: Jalankan pembersihan docker/containerd cache jika penggunaan disk > 85%
          command: crictl rmi --prune
          when: "((root_disk.size_total - root_disk.size_available) / root_disk.size_total * 100) > 85"
          changed_when: true
          register: prune_result

        - name: Tampilkan log pembersihan disk
          debug:
            var: prune_result.stdout_lines
          when: prune_result.changed
      rescue:
        - name: Peringatan jika proses pembersihan disk gagal
          debug:
            msg: "Gagal mendeteksi disk space atau crictl rmi tidak tersedia pada node."

Playbook di atas merupakan contoh implementasi pemulihan mandiri (self-healing) sederhana. Kita dapat memperluas logika ini dengan mengintegrasikan sistem alert seperti Prometheus Alertmanager untuk memicu Ansible playbook secara otomatis menggunakan webhook ketika mendeteksi masalah pada kluster Kubernetes kita.


Ringkasan #

  • Orkestrasi Cordon & Drain secara Berurutan — Selalu lakukan cordon untuk menandai node agar tidak menerima Pod baru sebelum menjalankan perintah drain untuk memindahkan beban kerja secara ramah.
  • Konfigurasi Drain yang Kuat — Gunakan opsi --ignore-daemonsets untuk mengabaikan daemon sistem dan --delete-emptydir-data untuk menyetujui penghapusan data lokal sementara saat mengevakuasi Pod.
  • Peningkatan Bertahap Versi Minor — Jalankan proses upgrade Kubernetes secara berurutan satu versi minor sekaligus. Mulai upgrade dari node control plane baru dilanjutkan ke worker node.
  • Proteksi Paket dengan APT Hold — Kunci versi paket kubeadm, kubelet, dan kubectl menggunakan status hold agar tidak ter-upgrade secara tidak sengaja oleh pemeliharaan harian OS.
  • Pencadangan etcd dengan Enkripsi TLS — Lakukan backup etcd secara teratur menggunakan perintah etcdctl snapshot save dengan menyertakan sertifikat enkripsi TLS lengkap yang valid.
  • Penyimpanan Snapshot Terpusat — Manfaatkan modul fetch pada Ansible untuk menarik file snapshot etcd ke mesin Ansible control node yang aman untuk mempermudah manajemen pemulihan bencana.
  • Remediasi Otomatis Node Bermasalah — Buat playbook pemantauan mandiri untuk mendeteksi matinya runtime kontainer atau kehabisan ruang disk, lalu terapkan tindakan pemulihan instan seperti restart layanan dan pembersihan cache.

← Sebelumnya: Helm   Berikutnya: Rolling Update →

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