適用版本:v1.2.1+ 狀態:experimental,適用 dev / STG;production 部署等 DB 化(v1)後再評估 相關文件:
spec.md(行為規格)、design.md(設計決策)
┌─────────────────────────────────────────────────┐
│ BE host(STG 主機) │
│ │
│ ┌──────────────┐ spawn ┌──────────────┐│
│ │ BE process │─────────────▶│ Docker daemon││
│ │ (Flask) │ │ ││
│ │ │ │ ┌──────────┐ ││
│ │ ANTHROPIC_ │ │ │ classifier│││
│ │ API_KEY env │ │ │ container│ ││
│ └──────┬───────┘ │ │ (-rm) │ ││
│ │ │ └──────────┘ ││
│ │ shell out └──────────────┘│
│ │ libreoffice --convert │
│ ▼ │
│ ┌──────────────┐ │
│ │ LibreOffice │ (預覽 docx→pdf 用) │
│ └──────────────┘ │
└─────────────────────────────────────────────────┘
│
│ HTTPS
▼
┌──────────────────┐ ┌────────────────────┐
│ Google Drive API │ │ Anthropic Claude API│
└──────────────────┘ └────────────────────┘
重點:
subprocess.run(["docker", "run", "--rm", ...]) 拉起 → 拿結果 → container 自動清掉docker ps # 不該噴 permission denied / cannot connect
docker info # 應正常列出 daemon 資訊如果 BE 跑的 user 沒權限:
sudo usermod -aG docker <BE-user>
# 重新登入該 user(group 變更需要新 session)which libreoffice || which soffice
# 應該回 /usr/bin/libreoffice 或類似
libreoffice --version # 確認版本如果未安裝:
sudo apt install -y libreoffice --no-install-recommendsBE process 必須能讀到:
ANTHROPIC_API_KEY=sk-ant-... # Anthropic API key,classifier 用放法(擇一):
systemd service 用 EnvironmentFile=/etc/guidant/.envenvironment=ANTHROPIC_API_KEY=...禁止 commit .env / API key 任何含憑證的檔到 git(per CLAUDE.md)。
Container 跑時會在 BE host /tmp/cm-jobs/<job_uid>/ 放 catalog.json + _state.json + _report-original.json(每 job 約 1-2 MB)。
df -h /tmp # 至少預留 1GB,長期堆積要加定期清理(下方 §6)進 Google Cloud Console → 該 OAuth Client 的 Authorized redirect URIs,加上 STG 域名的 callback,例如:
https://stg.your-domain.com/api/1.0/integrations/google-drive/oauth-callback
否則 STG 連 Drive 時會被 Google 擋掉。
# 1. SSH 進 STG
ssh <user>@stg-host
# 2. Pull 最新 code(切到目標 tag / branch)
cd /path/to/compliance-manager-be
git fetch
git checkout v1.2.1 # 或對應 branch
# 3. 套件更新(若有改 pyproject.toml)
poetry update
# 4. 跑 DB migration(本期 v1.2.1 需要)
PGPASSWORD='<cmmgr-pwd>' psql \
-h <stg-db-host> -p <port> -U cmmgr -d <db-name> \
-f scripts/sql/2026-05-28-add-evidences-drive-folder-scope.sql
# 5. Build classifier image(就地 build,不推 registry)
cd scripts/evidence/classify/docker
docker build -t cmmc-classifier:latest .
# 約 1-3 分鐘
cd -
# 6. 重啟 BE
lsof -ti :8000 | xargs -r kill -9
nohup python main_socketio.py > log/app.log 2>&1 &
# 或用既有 systemd / supervisord 機制
# 7. 確認 BE 起來
sleep 3
curl -fsS http://localhost:8000/swagger-ui/ > /dev/null && echo OK
tail -20 log/app.log # 確認沒 stack tracev0.4 init folders 流程才會建 Evidences/ subfolder。v0.3 之前 init 的專案沒有,要重跑 init 補上。
由 PM 在 UI 操作:
POST /api/1.0/integrations/google-drive/projects/<project_uid>/init-folders)Evidences/(以及對應的 EVIDENCES drive_folder_mappings 紀錄)不重跑的話 → 該專案的「自動分類證據」按鈕不會顯示(drive_ready=false,因為 _derive_evidence_folder_id 找不到 Evidences 資料夾)。
# 1. Classifier image 可正常起
docker run --rm cmmc-classifier:latest --help
# 預期:列出 service-classify 子命令的 help 訊息
# 2. LibreOffice headless 可用(預覽 docx)
echo "test" > /tmp/test.txt
libreoffice --headless --convert-to pdf --outdir /tmp /tmp/test.txt
ls /tmp/test.pdf && rm -f /tmp/test.txt /tmp/test.pdf
# 3. BE API 健康
curl -fsS http://localhost:8000/swagger-ui/ > /dev/null && echo "BE up"
# 4. UI 流程
# - 登入 STG → 進已 init 的專案總覽
# - 確認「自動分類證據」按鈕出現
# - 用小測試集(< 10 檔)跑一次完整流程:觸發 → 等完成 → 進審閱頁 → 預覽 docx → 編輯 → 儲存--rm 旗標讓 container 跑完自動刪,但偶發 daemon crash 可能留 dead container:
docker container prune -f # 清掉所有 stopped containers/tmp/cm-jobs/ 清理每跑一次分類會留 catalog + state + report,長期堆積。建議排程清理:
# 例:每週清 7 天前的 job dir
find /tmp/cm-jobs -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +放 cron 或 systemd timer。
關鍵 log pattern(log/app.log):
[classifier] job=<uid> container done — 容器成功跑完[classifier] job=<uid> completed; run_folder=<id> classified=N — 整個 job 收尾完成[classifier] job=<uid> container failed / unexpected failure — 失敗grep 查最近狀態:
grep "\[classifier\]" log/app.log | tail -20目前無全機資源上限。多 user 同時觸發不同 project 會同時起多個 container:
docker ps --filter "ancestor=cmmc-classifier:latest"每個 container 跑時用 ~500MB RAM + 1 vCPU。若 STG 規格小,建議目前限 demo 用戶,觀察行為再考慮加 queue。
若 v1.2.1 上 STG 出問題:
# 1. 切回上一版 tag
git checkout v1.2.0
poetry update
# 2. DB migration 是 ADD ONLY(只擴 CHECK constraint 允許值),無需 rollback
# 但若要乾淨 rollback CHECK:
PGPASSWORD='...' psql -h ... -U cmmgr -d ... -c "
ALTER TABLE compliance.drive_folder_mappings
DROP CONSTRAINT IF EXISTS chk_drive_folder_mappings_scope;
ALTER TABLE compliance.drive_folder_mappings
ADD CONSTRAINT chk_drive_folder_mappings_scope
CHECK (scope_type IN ('ROOT','PROJECT','AP','CONTROL_GROUP','CONTROL','AO','TASK','ARCHIVE'));
"
# (若已有 EVIDENCES scope row,DROP CHECK 不會擋,但加回去會;需先 DELETE row)
DELETE FROM compliance.drive_folder_mappings WHERE scope_type = 'EVIDENCES';
# 3. 重啟 BE
lsof -ti :8000 | xargs -r kill -9 && nohup python main_socketio.py > log/app.log 2>&1 &
# 4. 既有 Drive 上的 Evidences 資料夾不會自動刪,人工處理(或保留)目前 STG 是 premise(BE 跑在 VM),docker 在本機。若改用 STAGING_AWS_BILLOWS 之類:
| 部分 | premise STG | AWS STG |
|---|---|---|
| Classifier 跑哪 | host docker daemon | 改用 ECS Fargate Task / k8s Job / AWS Batch |
| BE → 觸發 | subprocess docker run |
改用 boto3 ecs.run_task 或 k8s API |
| Image 來源 | local build | 推到 ECR,Task 從 ECR pull |
| 環境變數注入 | host env | Task definition env / Secrets Manager |
這是非平凡的改動,正式遷移時要規劃 infra/evidence_classification/classifier_container_runner.py 抽象化(目前只有 subprocess 一條路徑)。先列在 follow-up。
| 問題 | 症狀 | 解 |
|---|---|---|
| BE user 沒 docker group 權限 | trigger 後 BE log 噴 permission denied... docker.sock |
usermod -aG docker <user>,重新登入 |
ANTHROPIC_API_KEY 沒讀到 |
container 跑出來 _state.json 為空 / container exit code != 0 |
檢查 BE process env,確認 systemd / supervisord 注入 |
| LibreOffice 沒裝 | 預覽 docx 顯示「不支援內嵌預覽」 fallback | 裝 libreoffice 或 soffice |
| Drive OAuth redirect URI 未加 | 連結 Drive 跳轉時被 Google 擋 redirect_uri_mismatch | Cloud Console 加 STG callback URL |
cmmc-classifier:latest image 缺 |
trigger 後 BE log:Unable to find image 'cmmc-classifier:latest' |
跑 build 步驟 |
| DB migration 沒跑 | trigger 後 chk_drive_folder_mappings_scope violation(寫 EVIDENCES row 時) |
跑 2026-05-28-add-evidences-drive-folder-scope.sql |
| 既有專案沒重跑 init | 「自動分類證據」按鈕不顯示 | PM 在 UI 重跑「初始化專案資料夾」 |
| 並發太多 container 撞 RAM | container 起不來 / OOM kill | 限 demo user 範圍,v1 加 queue |