diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index 61120c1..029debb 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -213,12 +213,12 @@ jobs: --exclude='monitoring/alertmanager/' \ --exclude='._*' \ ./ ollama@192.168.0.188:/home/ollama/momo-pro/ || true - # H2: --force-recreate 確保容器強制重建(避免靜默更新失敗) + # H2: 先 build 成功再短暫切換,避免 no-cache build 時間全變成 502 ssh -i ~/.ssh/id_deploy ollama@192.168.0.188 \ "cd /home/ollama/momo-pro && \ - docker stop momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null; \ - docker rm momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null; \ docker compose build --no-cache momo-app && \ + (docker stop momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null || true) && \ + (docker rm momo-pro-system momo-scheduler momo-telegram-bot 2>/dev/null || true) && \ docker compose up -d --no-deps --force-recreate momo-app scheduler telegram-bot && \ echo '✅ Image 重建完成(三容器)'" diff --git a/AGENTS.md b/AGENTS.md index 2b3b0e5..02f4fe9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # EwoooC (MOMO Pro System) — Codex 專案工作規則 -> 版本: V13.2 +> 版本: V13.3 > 目標: 把專案知識整理成 Codex 可低成本讀取、可持續維護、可安全落地的單一工作入口。 ## 1. 入口原則 @@ -125,6 +125,7 @@ - 跨專案資源邊界以 ADR-011 為準。 - 部署、容器、SSH 類操作先看 `docs/adr/ADR-011-cross-project-resource-isolation.md`。 - `gunicorn.conf.py` 必須透過 `docker-compose.yml` bind mount 進 `momo-app`;除救急外,不以 `docker cp` 當常態部署方式。 +- CD rebuild 應先完成 image build,再短暫 recreate 三應用容器;禁止把 no-cache build 時間變成長時間 502。 ## 8. 常用入口 diff --git a/CONSTITUTION.md b/CONSTITUTION.md index 8bc52bb..eeea824 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.13 (CD Gunicorn 掛載強化版) +> **當前版本**: V10.14 (CD Rebuild 切換強化版) > **最後更新**: 2026-04-30 --- diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index ac6d77b..0c9100e 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -22,6 +22,7 @@ - CD Sync reload 修復:rsync 後明確 `docker compose restart momo-app scheduler telegram-bot`,避免檔案已同步但 process 仍跑舊版。 - CD Gunicorn 掛載強化:`momo-app` 明確掛載 `./gunicorn.conf.py:/app/gunicorn.conf.py:ro`,避免重啟後吃到 image 內舊版設定。 - Metrics schema drift 降噪:`realtime_sales_monthly` 總筆數改用 raw `COUNT(*)`,避免 ORM 欄位 drift 造成 Prometheus scrape warning。 + - CD Rebuild 切換強化:rebuild 模式改為先 `docker compose build --no-cache momo-app` 成功,再 stop/rm/recreate 三應用容器,避免長時間 502。 【下次待辦】 - 觀察 Prometheus scrape 後 `momo_ai_*` 是否在事件發生後產生時間序列。 diff --git a/app.py b/app.py index c783a2a..757d752 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-04-30 V10.13: CD Gunicorn bind mount + metrics schema drift hardening -SYSTEM_VERSION = "V10.13" +# 🚩 2026-04-30 V10.14: CD rebuild cutover hardening +SYSTEM_VERSION = "V10.14" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/config.py b/config.py index ac278a1..3a92b2a 100644 --- a/config.py +++ b/config.py @@ -253,7 +253,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.13" +SYSTEM_VERSION = "V10.14" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index f7e5f3c..3dadc68 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -2,7 +2,7 @@ > **最後更新**: 2026-04-30 (台北時間) > **狀態**: 🟢 四 AI Agent 自動化閉環已落地 — EventRouter / AutoHeal / OpenClaw Memory / ElephantAlpha bridge / Prometheus metrics / Smoke Dashboard / Smoke Trend Management / Telegram Summary / Grafana provisioning / Prometheus scrape / CD Gunicorn 掛載具測試覆蓋 -> **適用版本**: V10.13 CD Gunicorn 掛載強化版 +> **適用版本**: V10.14 CD Rebuild 切換強化版 --- @@ -71,6 +71,7 @@ SQL漏斗(~300筆) - Active monitoring stack 使用 `monitoring/prometheus.yml` 的 `momo-app` job scrape `momo-pro-system:80/metrics`;Prometheus container 需加入 `momo-network`。 - `/metrics` 對 `realtime_sales_monthly` 只用 raw `SELECT COUNT(*)` 取得總筆數,避免 ORM schema drift 讓 Prometheus scrape 產生 warning。 - `momo-app` 必須 bind mount `./gunicorn.conf.py:/app/gunicorn.conf.py:ro`,讓 CD sync/rebuild 後的 Gunicorn runtime 設定與 repo 保持一致。 +- CD rebuild 模式必須先 build image 成功,再短暫 stop/rm/recreate 三應用容器,避免 no-cache build 造成長時間 502。 --- diff --git a/docs/guides/devops_handbook.md b/docs/guides/devops_handbook.md index 574542f..4fb2c48 100644 --- a/docs/guides/devops_handbook.md +++ b/docs/guides/devops_handbook.md @@ -86,3 +86,8 @@ ### 6. `/metrics` 持續出現 realtime_sales_monthly 欄位不存在 warning - **原因**: 線上表 schema 可能比 ORM 窄,ORM count 會包子查詢並引用不存在欄位。 - **修復**: metrics 只需要總筆數時使用 `SELECT COUNT(*) FROM realtime_sales_monthly`,不要透過 `session.query(RealtimeSalesMonthly).count()`。 + +### 7. Rebuild 模式造成長時間 502 +- **原因**: CD 若先 `docker stop/rm` 再 `docker compose build --no-cache`,build 耗時會全部變成服務中斷時間。 +- **修復**: rebuild 模式必須先 `docker compose build --no-cache momo-app` 成功,才短暫 stop/rm/recreate `momo-app scheduler telegram-bot`。 +- **救急**: 若 build 已經在跑且三應用容器已被移除,可精準執行 `docker compose up -d --no-deps momo-app scheduler telegram-bot` 先恢復服務;禁止使用 `--remove-orphans`。 diff --git a/docs/memory/ai_automation_closure_20260429.md b/docs/memory/ai_automation_closure_20260429.md index 37b40a8..a221cb3 100644 --- a/docs/memory/ai_automation_closure_20260429.md +++ b/docs/memory/ai_automation_closure_20260429.md @@ -22,6 +22,7 @@ - 2026-04-30 CD Sync 模式曾只 rsync + `docker compose up -d`,導致 host 檔案已是新版但 gunicorn process 仍跑舊版;已補 `docker compose restart momo-app scheduler telegram-bot`。 - 2026-04-30 `gunicorn.conf.py` 不是 app container bind mount,曾導致手動 restart 後回吃 image 內舊設定;`momo-app` 已補 `./gunicorn.conf.py:/app/gunicorn.conf.py:ro`。 - 2026-04-30 `/metrics` 對 `realtime_sales_monthly` 改用 raw `SELECT COUNT(*)`,避免 ORM 欄位與線上表 schema drift 時每次 Prometheus scrape 都產生 warning。 +- 2026-04-30 CD Rebuild 模式曾先停三應用容器再 no-cache build,造成 build 時間全變成 502;已改為 build 成功後才短暫 stop/rm/recreate。 ## 已落地範圍 @@ -56,6 +57,7 @@ - 2026-04-30 CD health check hardening:新增 `tests/test_cd_health_check.py`。 - 2026-04-30 CD Gunicorn mount hardening:新增 `tests/test_docker_compose_runtime_mounts.py`。 - 2026-04-30 Metrics schema drift 降噪:`tests/test_ai_automation_metrics.py` 覆蓋 raw sales count query。 +- 2026-04-30 CD rebuild cutover hardening:`tests/test_cd_health_check.py` 覆蓋 build-before-stop 順序。 - 2026-04-29 L2 安全記憶批次:`24 passed`。 - collect-only:`48 tests collected`。 - `git diff --check` 已通過。 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 2618975..89ecccc 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -35,6 +35,7 @@ - **CD Sync reload 修復**: rsync 後明確 restart 三容器,避免 bind-mounted Python 檔案更新但 gunicorn/scheduler/bot process 未 reload。 - **CD Gunicorn 掛載強化**: `momo-app` 補 `./gunicorn.conf.py:/app/gunicorn.conf.py:ro`,避免容器 restart 後回吃 image 內舊 gunicorn 設定。 - **Metrics schema drift 降噪**: `/metrics` 的 `realtime_sales_monthly` 總筆數改用 raw `COUNT(*)`,避免 ORM 欄位 drift 造成 Prometheus scrape warning。 +- **CD Rebuild 切換強化**: rebuild 模式改成先 build 成功、再短暫 stop/rm/recreate 三應用容器,避免 no-cache build 長時間 502。 ### 2026-04-28~29:Phase 3e 重構大戰 + daily_sales cache 隱形 bug 根除 - **app.py 縮減 -10.8%**: 7,386 → 6,590 行,11 commits 全綠零 502。 diff --git a/tests/test_cd_health_check.py b/tests/test_cd_health_check.py index 8f0b83f..05665fc 100644 --- a/tests/test_cd_health_check.py +++ b/tests/test_cd_health_check.py @@ -27,3 +27,13 @@ def test_cd_sync_mode_restarts_after_rsync_to_reload_python_processes(): assert "docker compose up -d --no-deps momo-app scheduler telegram-bot" in workflow assert "docker compose restart momo-app scheduler telegram-bot" in workflow + + +def test_cd_rebuild_builds_image_before_stopping_running_containers(): + workflow = CD_WORKFLOW.read_text(encoding="utf-8") + + build_index = workflow.index("docker compose build --no-cache momo-app") + stop_index = workflow.index("docker stop momo-pro-system") + up_index = workflow.index("docker compose up -d --no-deps --force-recreate momo-app scheduler telegram-bot") + + assert build_index < stop_index < up_index