CT124 故障復原報告與 CLAUDE.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-06-21 21:54:13 +08:00
commit 6a7aaba059
2 changed files with 179 additions and 0 deletions

29
CLAUDE.md Normal file
View File

@@ -0,0 +1,29 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
This is **not a software project** — there is no code, build, or test. It is an operations/infrastructure
notes directory for a self-hosted homelab running **Docker Compose stacks inside Proxmox LXC containers**.
Files are post-incident / runbook documents written in Traditional Chinese.
Current contents: `CT124_故障復原_20260529.md` — a recovery report for a full Docker/Tailscale outage on host
CT124 (192.168.42.124, Ubuntu 24.04 LXC) after a hard power-off.
## Working in here
- Documents are the deliverable. "Editing" means writing/revising Markdown, not changing code.
- Filename convention: `<host>_<topic>_<YYYYMMDD>.md` (e.g. `CT124_故障復原_20260529.md`).
- Shell snippets in the docs run on the LXC host as root; they are records of what was done, not scripts to
execute here. Don't run them against any machine unless explicitly asked.
- Cross-references point to docs **not in this directory** (e.g. `PVECluster_Architecture.md`'s "停電復電 SOP").
If a referenced doc is needed, ask — don't assume it exists locally.
## Key non-obvious fact (CT124)
The outage's original symptom (Tailscale/Headscale offline) was a downstream effect, not the root cause. The
dependency chain is: **docker daemon → Nginx Proxy Manager container (reverse-proxies :443) → Headscale**.
External `headscale.lotimmy.com:443` hairpins back to CT124's own WAN IP. When Docker dies, NPM dies, port 443
stops listening, and the Tailscale client logs out. When debugging Tailscale disconnects on this host, verify
this chain first before suspecting Tailscale itself.

View File

@@ -0,0 +1,150 @@
# 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.db`bbolt損毀dockerd 還原網路 sandbox 時 panic`assertion 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.db`kernel 殘留 21 個孤兒 `br-*` bridge仍佔著 `172.1831.x``192.168.0/48/.../128` 子網Docker 判定位址池全被佔用 |
| 5 | prometheus crash-loop | 資料目錄 `/opt/ai-proxy-hub/prometheus_data` 屬 root但容器以 `nobody`(65534) 執行 → `permission denied`pre-existing非本次新問題 |
| 6 | orthanc-pacs crash-loop | SQLite 索引 `/opt/ohif-docker/orthanc-db/index` 損毀(`SQLITE_CORRUPT`code 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
```bash
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 即可連線)
```bash
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.db`kernel 殘留的 `br-*` 介面不屬於任何現存 docker 網路,卻佔住子網。只刪「非當前網路」的 bridge
```bash
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
```bash
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 權限
```bash
chown -R 65534:65534 /opt/ai-proxy-hub/prometheus_data
docker restart prometheus
```
### 6. 復原 Orthanc 損毀的 SQLite 索引(非破壞性)
實體影像(`00``ff` content-addressed 目錄)完好,只有索引檔損毀。`PRAGMA integrity_check` 顯示為孤立 page + 重複 page 引用:
```bash
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 排程,作為索引復原失敗時的最後保險。