Jump to content

Insiden OKD SNO 2026-11-02: CRI-O macet setelah unclean shutdown

From Haidhi Wiki


Catatan insiden pada cluster dev OKD Single Node (SNO) di Proxmox. Lihat juga Install OKD Single Node (SNO) di Proxmox.

Ringkasan

Item Keterangan
Tanggal Senin, 2 November 2026
Durasi pemulihan ±09:30 – 10:42 WIB (±1 jam 10 menit)
Dampak API OKD, console, dan semua aplikasi di cluster tidak tersedia
Cluster okd-sno (192.168.90.30), OKD 5.0.0-okd-scos.3
Data hilang Tidak ada. etcd utuh; hanya image/container lokal yang di-wipe dan ditarik ulang
Status Pulih. Semua cluster operator Available=True, Progressing=False, Degraded=False

Singkatnya: VM OKD mati tidak rapi, lalu saat boot DNS bastion sedang mati. Setelah DNS pulih, CRI-O terjebak deadlock dengan plugin jaringan Multus (CRI-O menunggu multus-shim, multus-shim menunggu daemon Multus yang justru butuh CRI-O untuk jalan). Watchdog systemd terus membunuh CRI-O, sehingga etcd dan API server tidak pernah naik. Lingkaran diputus dengan menonaktifkan sementara config CNI Multus.

Gejala awal

Di console VM OKD (Proxmox), log berulang:

nodeip-configuration: Error: unable to copy from source docker://quay.io/okd/scos-content@sha256:...
... dial tcp: lookup quay.io on 192.168.90.29:53: read udp 192.168.90.30:...->192.168.90.29:53: read: connection refused

connection refused pada UDP 53 berarti host bastion hidup tetapi tidak ada yang mendengarkan di port 53 → dnsmasq di bastion mati.

Kronologi (WIB)

Waktu Kejadian
±09:30 Node gagal resolve quay.io; DNS bastion (192.168.90.29) menolak koneksi.
±09:48 Setelah DNS bastion pulih, node menyelesaikan boot: OVS, crio.service, kubelet.service started.
±10:00 API (https://192.168.90.30:6443/readyz) tidak menjawab. Di node, crictl timeout: DeadlineExceeded ke crio.sock. chrony-wait.service failed (jam tetap benar).
10:14 crio.service berstatus activating (start); log: Checking storage directory ... because of unclean shutdown; ada proses anak multus-shim. Disk /var hanya 11%.
10:16 crio wipe -f dijalankan; storage container dibersihkan.
10:18–10:20 CRI-O tetap menggantung di multus-shim; systemd: Failed with result 'watchdog', core dump (SIGABRT), restart, berulang.
10:26 pkill -f multus-shim → CRI-O active. Cache /var/lib/cni/results dipindahkan.
±10:29 Setelah kubelet dinyalakan, CRI-O kembali dibunuh watchdog karena multus-shim menggantung lagi.
10:30 Dikonfirmasi lewat journal: watchdog + stack trace multus-shim.
±10:32 00-multus.conf dipindahkan sementara ke backup. CRI-O start bersih, kubelet dinyalakan.
10:33 kube-apiserver Running.
10:34 etcd (semua container) Running, data lama terbaca.
10:35 API UP. oc dengan token user gagal (provide credentials); dipakai kubeconfig installer. Node Ready, tidak ada CSR pending.
10:42 Semua cluster operator sehat, pod Multus Running. Ditemukan Deployment myapp di test-app hilang (masalah terpisah, lihat bawah).

Analisis penyebab

Rantai kejadian

  1. VM OKD mati tidak rapi (dugaan: host Proxmox restart atau VM dihentikan paksa). CRI-O tidak sempat menulis /var/lib/crio/clean.shutdown.
  2. dnsmasq di bastion tidak berjalan saat node boot. Dugaan kuat: dnsmasq dikonfigurasi listen-address=192.168.90.29 + bind-interfaces, dan start sebelum interface mendapat IP saat boot, sehingga gagal bind dan tidak dicoba ulang. (Log dnsmasq saat kejadian tidak sempat dicek.)
  3. Setelah DNS pulih, CRI-O deadlock dengan Multus:
    • Saat start setelah unclean shutdown, CRI-O memanggil plugin CNI multus-shim untuk sandbox/jaringan pod lama.
    • multus-shim hanyalah klien; ia menunggu daemon Multus, yang berjalan sebagai pod.
    • Daemon Multus tidak bisa jalan karena CRI-O belum siap → saling tunggu.
    • Watchdog systemd membunuh CRI-O (status=6/ABRT), CRI-O di-restart, dan karena tidak pernah shutdown bersih, kondisi "unclean shutdown" terus berulang.
  4. Tanpa CRI-O, kubelet tidak bisa menjalankan static pod etcd dan kube-apiserver → seluruh cluster mati.

Kenapa crio wipe saja tidak cukup

crio wipe menghapus storage image/container, tetapi CRI-O tetap memanggil multus-shim saat start (config CNI 00-multus.conf masih ada), dan kubelet memicu pembuatan sandbox pod baru yang juga lewat Multus. Selama daemon Multus belum hidup, pemanggilan itu tetap menggantung.

Langkah pemulihan (runbook)

Semua langkah di node dijalankan lewat ssh [email protected] dari bastion.

1. Pastikan DNS bastion hidup

# di bastion
systemctl status dnsmasq --no-pager | head
sudo ss -lntup | grep ':53'
dig +short quay.io @192.168.90.29
dig +short api.okd.lab.local @192.168.90.29

2. Bedakan masalah DNS vs API

# di bastion
getent hosts api.okd.lab.local
curl -sk --max-time 5 https://192.168.90.30:6443/readyz; echo

3. Diagnosis CRI-O di node

systemctl status crio --no-pager | head -20
sudo journalctl -u crio -b --no-pager | grep -iE "watchdog|unclean|multus|dumped" | tail -20
ps -ef | grep [m]ultus-shim
df -h /var
timedatectl

Pola masalah: activating (start) + proses anak multus-shim + Failed with result 'watchdog'.

4. (Opsional) Wipe storage CRI-O

Hanya bila log menunjukkan kerusakan storage. Tidak menghapus etcd (/var/lib/etcd).

sudo systemctl stop kubelet
sudo systemctl stop crio
sudo crio wipe -f

5. Putus deadlock Multus

sudo systemctl stop kubelet
sudo systemctl kill -s SIGKILL crio
sudo pkill -9 -f multus-shim

# backup lalu nonaktifkan sementara config CNI Multus
sudo mkdir -p /root/cni-backup
sudo cp -a /etc/kubernetes/cni/net.d/. /root/cni-backup/
sudo mv /etc/kubernetes/cni/net.d/00-multus.conf /root/cni-backup/00-multus.conf.disabled

# (opsional) pindahkan cache hasil CNI lama
sudo mv /var/lib/cni/results /var/lib/cni/results.bak-$(date +%s)

sudo systemctl start crio --no-block
sleep 20
systemctl is-active crio          # harus active
sudo crictl ps | head              # harus langsung menjawab
sudo systemctl start kubelet
watch -n 10 'sudo crictl ps | grep -E "etcd|kube-apiserver|ovnkube|multus"'

Catatan:

  • systemctl start crio tanpa --no-block akan ikut menggantung bila CRI-O macet; pakai sesi SSH kedua atau --no-block.
  • etcd dan kube-apiserver memakai jaringan host, jadi bisa jalan tanpa Multus.

6. Pastikan config Multus kembali

Setelah pod daemon multus Running, file config biasanya ditulis ulang. Cek:

ls -la /etc/kubernetes/cni/net.d/
# bila 00-multus.conf belum ada setelah beberapa menit:
sudo cp /root/cni-backup/00-multus.conf.disabled /etc/kubernetes/cni/net.d/00-multus.conf

7. Verifikasi cluster

Pakai kubeconfig installer (berbasis sertifikat), karena token user (htpasswd/OAuth) belum bisa divalidasi sebelum pod OAuth pulih:

export KUBECONFIG=~/okd/cluster/auth/kubeconfig
until curl -sk https://api.okd.lab.local:6443/readyz | grep -q ok; do echo "API belum siap..."; sleep 15; done; echo "API UP"
oc get nodes
oc get csr | grep -i pending
oc get csr -o name | xargs oc adm certificate approve   # bila ada yang pending
oc get co | grep -vE "True\s+False\s+False"
oc get pods -A | grep -vE "Running|Completed"

Temuan terpisah: Deployment myapp hilang

Setelah cluster pulih, di namespace test-app hanya tersisa Service dan Route; Deployment dan ReplicaSet myapp tidak ada, sehingga router menampilkan Application is not available.

  • Bukan akibat insiden ini: etcd utuh (Service, Route, dan semua objek cluster lain masih ada). Kemungkinan terhapus oleh perintah oc delete di hari sebelumnya.
  • Cara menelusuri lewat audit log:
oc adm node-logs okd-sno --path=kube-apiserver/audit.log \
  | grep '"verb":"delete"' | grep '"namespace":"test-app"' | grep -i deployments | tail -5
  • Pemulihan: buat ulang Deployment; Service lama otomatis tersambung karena memilih label app=myapp:
oc project test-app
oc secrets link default marshall-reg --for=pull
oc create deployment myapp \
  --image=registry.marshall-teach.web.id/aamr/marshall-streamlit-summarization:latest \
  --port=8501
  • Pelajaran: simpan manifest Deployment di Git (k8s/deployment.yaml) agar bisa dipulihkan dengan oc apply.

Tindakan pencegahan

Tindakan Tujuan Status
dnsmasq: ganti bind-interfaces → bind-dynamic Tetap bind ke IP bastion walau IP muncul belakangan saat boot
Override systemd dnsmasq: After=network-online.target, Restart=on-failure dnsmasq menunggu jaringan dan pulih sendiri bila gagal
Proxmox start order: bastion order 1 (delay 60 dtk), OKD order 2 DNS selalu naik sebelum cluster
Selalu Shutdown, jangan Stop, untuk VM OKD Hindari unclean shutdown CRI-O
Snapshot VM OKD saat cluster sehat Titik pulih cepat
Manifest app disimpan di Git Deployment yang terhapus bisa dipulihkan dengan oc apply
Pertimbangkan UPS untuk host Proxmox Hindari mati listrik mendadak

Konfigurasi dnsmasq yang disarankan (/etc/dnsmasq.d/okd.conf di bastion):

listen-address=192.168.90.29
bind-dynamic
server=1.1.1.1
server=8.8.8.8
address=/api.okd.lab.local/192.168.90.30
address=/api-int.okd.lab.local/192.168.90.30
address=/apps.okd.lab.local/192.168.90.30
address=/registry.marshall-teach.web.id/192.168.90.100

Override systemd:

sudo mkdir -p /etc/systemd/system/dnsmasq.service.d
sudo tee /etc/systemd/system/dnsmasq.service.d/override.conf <<'EOF'
[Unit]
Wants=network-online.target
After=network-online.target

[Service]
Restart=on-failure
RestartSec=5
EOF
sudo systemctl daemon-reload
sudo systemctl restart dnsmasq

Pelajaran

  • Di SNO, satu komponen mati = seluruh cluster mati. CRI-O, DNS bastion, dan etcd semuanya single point of failure.
  • connection refused (port tertutup) berbeda dengan timeout/hang. Bedakan untuk mempersempit penyebab: DeadlineExceeded = CRI-O hidup tapi macet; connection refused pada socket = CRI-O mati/sedang restart.
  • Failed with result 'watchdog' + core dump bukan berarti CRI-O bug crash; artinya CRI-O terlalu lama tidak memberi tanda hidup ke systemd.
  • Tes konektivitas API dengan IP (curl -sk https://192.168.90.30:6443/readyz) untuk memisahkan masalah DNS dari masalah API.
  • Simpan kubeconfig installer (cluster/auth/kubeconfig) dengan aman: satu-satunya akses admin yang tidak bergantung pada OAuth.