Files
42_124/CT124_故障復原_20260529.md
Timmy 6a7aaba059 CT124 故障復原報告與 CLAUDE.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:54:13 +08:00

7.2 KiB
Raw Permalink Blame History

CT124 故障復原報告 — Docker / Tailscale 全面當機

日期2026-05-29 | 主機CT124192.168.42.124Ubuntu 24.04 LXC 誘因:非正常關機(硬斷電) | 結果38/38 容器恢復,零資料遺失


一、摘要

CT124 在一次非正常關機reboot 後 uptime 僅 6 分鐘Docker daemon 陷入 crash-loop連帶導致這台上約 30 個 compose stack 全數停擺,其中包含對外提供 Headscale 反向代理的 Nginx Proxy ManagerNPM。最初的症狀是 TailscaleHeadscale client連不上 control server,往下追才發現是整條相依鏈崩潰。

最終全部修復,且 Orthanc PACS 的醫療影像 SQLite 索引損毀也以非破壞性方式完整復原(零資料遺失)。

項目 修復後狀態
容器 38 / 38 running0 not-running
docker.service active
Tailscale 100.64.0.28,已登入 Headscale
Orthanc PACS 11 patients / 18 studies / 7616 instances / 3.2GB,資料完整

二、根因與相依鏈

這是一次 reboot 引發的連鎖故障,共六層:

# 問題 根因
1 Docker daemon crash-loopexit 2 libnetwork 的 local-kv.dbbbolt損毀dockerd 還原網路 sandbox 時 panicassertion failed: write: circular dependency occurred
2 Tailscale 連不上 Headscale(原始症狀) 對外 443 由 CT124 上的 NPM 容器反代到 HeadscaleNPM 隨 docker 崩潰掛掉443 無人監聽(connection refused
3 約 30 個 compose stack 全停 reboot + docker 崩潰,非 host-network 的容器全部 Exited只有 host-network 的 tailscale/netbird + jellyfin 活著)
4 後半 stack 重建時 all predefined address pools have been fully subnetted 移除 local-kv.dbkernel 殘留 21 個孤兒 br-* bridge仍佔著 172.1831.x192.168.0/48/.../128 子網Docker 判定位址池全被佔用
5 prometheus crash-loop 資料目錄 /opt/ai-proxy-hub/prometheus_data 屬 root但容器以 nobody(65534) 執行 → permission deniedpre-existing非本次新問題
6 orthanc-pacs crash-loop SQLite 索引 /opt/ohif-docker/orthanc-db/index 損毀(SQLITE_CORRUPTcode 11硬斷電寫入中斷所致

關鍵非顯性拓撲

外部 → headscale.lotimmy.com:443
        │  (DNS → 125.229.110.50 = CT124 自己的 WAN IPhairpin 繞回內網)
        ▼
   CT124Nginx Proxy Manager 容器 (443 反代)
        │
        ▼
   Headscale 本體(內網監聽 :8080/health 回 {"status":"pass"}
        ▲
        │  /opt/tailscale 容器 (network_mode: host) 作為 client 連回

相依鏈:docker daemon → NPM 容器 → Headscale。NPM 一掛Tailscale 就 logged out。排查 Tailscale 斷線時務必先確認這條鏈,別誤判成 Tailscale 本身問題。


三、修復步驟(依序)

1. 修復 Docker daemon移除損毀的網路 DB

systemctl stop docker.socket docker.service
pkill -9 docker-proxy; pkill -9 dockerd
mv /var/lib/docker/network/files/local-kv.db \
   /var/lib/docker/network/files/local-kv.db.bak.$(date +%s)
systemctl reset-failed docker.service
systemctl start docker.service

host-network 的 tailscale/netbird/jellyfin 會自動回來;自訂網路的容器仍會 Exited見步驟 3、4

2. 重建 NPM恢復對外 443 → Tailscale 即可連線)

cd /opt/nginx-proxy-manager
docker compose down          # 清掉殘留 container 對已死網路 ID 的參照
docker rm -f nginxproxymanager
docker compose up -d
# 驗證curl -m10 https://headscale.lotimmy.com/health  → HTTP 200 {"status":"pass"}
docker restart tailscale     # 讓 client 重新登入
docker exec tailscale tailscale status   # 應取得 100.64.0.x

3. 清除孤兒 bridge釋放 Docker 位址池)

移除 local-kv.dbkernel 殘留的 br-* 介面不屬於任何現存 docker 網路,卻佔住子網。只刪「非當前網路」的 bridge

keep="docker0"
for id in $(docker network ls -q); do keep="$keep br-$id"; done
for br in $(ip -o link show | awk -F': ' '{print $2}' | sed 's/@.*//' | grep '^br-'); do
  echo "$keep" | grep -qw "$br" || { ip link set "$br" down; ip link delete "$br"; }
done

本次刪除 21 個孤兒 bridge。

4. 重建所有停掉的 compose stack

for d in /opt/<each-stack>; do
  (cd "$d" && docker compose down >/dev/null 2>&1; docker compose up -d)
done

注意:偵測 compose 檔不要用 ls a.yml b.yml(多重 glob 只要一個沒中就回非零會誤判);直接 cd 進目錄讓 docker compose 自動辨識 docker-compose.yml / compose.yaml

5. 修 prometheus 權限

chown -R 65534:65534 /opt/ai-proxy-hub/prometheus_data
docker restart prometheus

6. 復原 Orthanc 損毀的 SQLite 索引(非破壞性)

實體影像(00ff content-addressed 目錄)完好,只有索引檔損毀。PRAGMA integrity_check 顯示為孤立 page + 重複 page 引用:

cd /opt/ohif-docker/orthanc-db
docker stop orthanc-pacs
cp -a index index.corrupt.bak.$(date +%s)        # 先備份
apt-get install -y sqlite3                         # 需 3.45+Ubuntu 24.04 內建)
sqlite3 index ".dump" > dump.sql                   # .recover 在此環境 dbpage vtab 失效,改用 .dump
# .dump 因損毀 page 結尾會是 "ROLLBACK; -- due to errors",但資料其實都已 dump 出來:
sed -i 's/^ROLLBACK; -- due to errors/COMMIT;/' dump.sql
sqlite3 index.rebuilt < dump.sql
sqlite3 index.rebuilt "PRAGMA integrity_check;"    # → ok
# 比對列數確認零遺失後換上:
mv index index.corrupt.old; mv index.rebuilt index; chown root:root index
docker start orthanc-pacs

復原前後列數比對(完全吻合):

原損毀檔 重建後
Resources 7775 7775
Metadata 83382 83382
AttachedFiles實體影像 7746 7746
MainDicomTags 50834 50834
Changes 8124 8124
DicomIdentifiers 7897 7897
GlobalProperties 4其中 1 列為損毀讀不出) 3乾淨DatabaseSchemaVersion=6

索引檔從 23.5MB 縮為 11.6MB(損毀膨脹被清除)。


四、留下的備份(確認運作正常後可刪)

/var/lib/docker/network/files/local-kv.db.bak.<ts>     損毀的 docker 網路 DB
/opt/ohif-docker/orthanc-db/index.corrupt.bak.<ts>     損毀的 orthanc 索引
/opt/ohif-docker/orthanc-db/index.corrupt.old          同上(另一份)

五、預防建議

  1. 務必走正常 shutdown:本次 1docker 網路 DB與 6orthanc SQLite兩處損毀都源於硬斷電。LXC 裡的 Docker 對非正常關機特別脆弱。可參考 PVECluster_Architecture.md 的「停電復電 SOP」。
  2. UPS / 自動關機:若停電風險高,建議掛 UPS 並設定 NUT 在斷電時對 CT124 觸發 graceful shutdown。
  3. Orthanc 改用 PostgreSQL 後端選配SQLite 在 LXC + 突發斷電下較易損毀;改用 PostgreSQL plugin 可顯著降低索引損毀風險。
  4. PBS 備份涵蓋 CT124:確認 /opt 下各 stack 的資料卷有納入 Proxmox Backup Server 排程,作為索引復原失敗時的最後保險。