Insiden OKD SNO 2026-11-02: CRI-O macet setelah unclean shutdown
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
- VM OKD mati tidak rapi (dugaan: host Proxmox restart atau VM dihentikan paksa). CRI-O tidak sempat menulis
/var/lib/crio/clean.shutdown. - 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.) - Setelah DNS pulih, CRI-O deadlock dengan Multus:
- Saat start setelah unclean shutdown, CRI-O memanggil plugin CNI
multus-shimuntuk sandbox/jaringan pod lama. multus-shimhanyalah 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.
- Saat start setelah unclean shutdown, CRI-O memanggil plugin CNI
- Tanpa CRI-O, kubelet tidak bisa menjalankan static pod
etcddankube-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 criotanpa--no-blockakan 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 deletedi 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 denganoc 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 refusedpada 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.