FR-184 交接現況(STATE)

這份是 living 檔:每棒就地 Edit,只描述「此刻」。歷史脈絡看同資料夾的 FR-184-LOG.md。

項目 值
最後更新 2026-11-08 17:40(首腦第 2 棒交出:T3 完成、1.30.1 線上更新成功、VM 升 4 核 16 GB、Terraform 導讀寫好)
branch BE feature/FR-172;infra feature/FR-172(clone 時帶入,不切);兩邊都已 push 到 origin(11-08)
母案卡 CM-3643 https://app.notion.com/p/FR-184-GCP-VM-Terraform-6-4-411346da4cd0810fa529df7b81d720d1
下一張要做的卡 下一棒=Terraform Q&A:決策者要能回答 PM 的問題,首腦陪讀 infra repo、答問題、需要時補導讀。之後才是 P1 → O1 → V1
卡片狀態 T1 CM-3644 驗收通過待確認;T2 CM-3645 工作坊上仍是 In progress(實際已由首腦做完並回寫,見 §2-8);F1 CM-3650、T4 CM-3651 取消(卡上已寫理由,等決策者在狀態頁按「這張不用做」);其餘 Not started
接手前必讀 §0 讀序

🧭 原始需求 / WHY

  • 決策者原話(11-07):「假設我今天的主系統想要部署到 GCP,你建議要怎麼做,最好是可以用成自動化部署。我要用 terraform 模式,這樣未來換平台也可以支援。」後續:「一套就好,stg 先不做,因為突然急著要上雲端」、「這是要自己營運的,然後是 POC 階段」。
  • 目標:GCP 台灣開一台 VM,Terraform 建環境、主產品既有 install.sh 裝系統(程式零改動),自己營運多租戶的雲端版給客戶試用。換雲只換 Terraform 的 module,安裝腳本不動。
  • 為什麼不是 K8s:費用 2~5 倍(實查)、程式要改八處拿掉單機假設、多三週。POC 用不到。決策者聽完「好貴」,定案 VM。
  • 本棒位置:雲端版已上線可用(1.30.1、e2-standard-4),決策者先給客戶試用。下一棒做 Q&A:決策者原話「你可能要教我看一下你的 terraform 是怎麼寫的,因為 PM 會問」。導讀已寫好(infra docs/terraform-walkthrough.md,含 PM 可能問的九題),下一棒以它為底陪決策者讀、答問題、答不出或文件沒寫到的就查程式碼補進導讀。

§0 接手讀序

0.1 🔒 先懂需求 gate

  1. Q&A 這棒先讀:infra ~/Projects/Billows/Audit-Manager/guidant-ai-infra/docs/terraform-walkthrough.md(導讀)全讀,再讀 modules/platform-gcp/main.tf、budget.tf、scripts/startup.sh、envs/gcp-prod/*.tf 實際程式——答問題以程式碼為準,導讀與程式不一致時改導讀
  2. 本檔 🧭 WHY、§2 已定裁示
  3. docs/features/FR-184-2611-gcp-vm-deployment/design.md 全讀(§3 決策、§5.2 裝機五步、§7 拆分表、§8 驗收、§10 風險)
  4. python3 /Users/chouraymond/Projects/Jedicogy/agent-workshop/scripts/notion_case.py get CM-3646(T3 卡,卡尾有三段首腦補充:1.30.0、AI 金鑰走 install.conf、序號裝完才貼)
  5. ~/Projects/Billows/Audit-Manager/guidant-ai-infra/docs/runbook.md(機器座標、防火牆驗法)

0.2 現況

  1. 本檔 §1、§3
  2. FR-184-LOG.md 最後一個 block

0.4 ✅ 冷接自檢

  1. 為什麼雲端版第一期跑 DEPLOYMENT_MODE=host 而不是 saas?
  2. env 與 module 為什麼分兩層?要加 STG、要換 AWS 各改哪裡?
  3. 哪三樣東西有 prevent_destroy/deletion_protection,為什麼是它們?
  4. 「VM 被入侵也清不掉備份」是哪一行設定做到的?
  5. 為什麼密碼值不經過 Terraform?Terraform 對 Secret Manager 做了什麼、沒做什麼?
  6. 換 VM 規格的完整步驟(含 apply 前後要做的兩件事)?
  7. 災難復原目前的缺口是什麼?為什麼快照救不回整台?

§1 當前狀態

GCP(專案 guidant-ai-cloud-514012,帳號 company.jedicotech@gmail.com,gcloud config 名 guidantai)

項目 狀態
VM guidant-ai-vm 跑著,asia-east1-b,e2-standard-4(4 核 16 GB)+8 GB swap,Ubuntu 24.04,vTPM(11-08 17:05 由 2 核 8 GB 升級,infra 5fe59da)
固定 IP 35.194.213.124
開機腳本 exit 0:swap、資料碟 /dev/sdb bind 到 /srv/guidant-ai//var/lib/docker//var/lib/containerd、docker 29.9.3+compose v5.7.0、Ops Agent active、/dev/tpm0
防火牆 直打 IP 的 80/443 逾時;5432、20202 被擋;22 只放 125.228.154.97(公司線,188 與決策者 Mac 同一個)
ssh ssh jedi@35.194.213.124(首腦 Mac 與 188 的公鑰都在)
系統 1.30.1(11-08 13:53 裝 1.30.0、16:45~16:57 更新中心線上升到 1.30.1),9 容器 healthy;Cloudflare Origin 憑證、UPDATE_SITE_URL、Turnstile 兩把 key 升級後都保留;安裝紀錄 /srv/guidant-ai/log/install-20261108-134535.log
Terraform state 在 gs://guidant-ai-cloud-514012-tfstate/gcp-prod/,24 個資源;本機 envs/gcp-prod/terraform.tfvars(不入版控)已填真值
預算告警 每月 6,500 TWD(約 200 美元;帳單帳戶是 TWD 計價,預算幣別必須相同),50/90/100%與預測超標寄信,只通知不停機;infra 724e9c0、modules/platform-gcp/budget.tf

Secret Manager(18 個,名稱都是 guidant-*)

secret 誰放的 用途
db-admin、db-app、redis、redis-api、redis-socketio、redis-worker 決策者 裝機自帶密碼(12 字元英數)
root-admin 決策者 GUIDANT_ROOT_ADMIN_PASSWORD,非互動安裝必填
anthropic 決策者 GUIDANT_AI_KEY_ANTHROPIC,install.conf 填,裝機時加密進 DB
smtp 決策者 Brevo SMTP key,備查;實際由決策者裝完在後台填
turnstile-secret 決策者 install.conf 的 GUIDANT_TURNSTILE_SECRET_KEY
origin-cert、origin-key 決策者 Cloudflare Origin 憑證與私鑰,已驗配對、SAN 只有 guidant-ai.jedicotech.com、到 2041

VM service account 只有前六個的讀取權(Terraform 綁的)。其餘首腦從 Mac 讀出再送上 VM。 11-08 裝機後:redis-api/redis-socketio/redis-worker 已用 installer 產的值加新版本(舊版本是決策者給的、裝機沒用到);另新建 6 個備查(label source=installer,VM 不可讀、不在 Terraform 管理):guidant-s3-access-key、guidant-s3-secret-key、guidant-classifier-token、guidant-bugsink-db、guidant-jwt-secret、guidant-app-encryption-key。guidant-db-app 決策者 11-08 換過一版(不可與 db-admin 相同)。

Cloudflare(jedicotech.com,權杖與 Zone ID 在 infra .env,權杖只有 DNS 編輯權)

  • A 紀錄 guidant-ai.jedicotech.com → 35.194.213.124,橘雲代理,已解析到 Cloudflare,連網址回 521(正常,系統未裝)
  • 全網域 SSL 是「完整」;guidant-ai.jedicotech.com 另有 Configuration Rule 設「嚴格」(決策者 11-08 設,運算式 (http.host eq "guidant-ai.jedicotech.com"))
  • Turnstile 小工具「Guidant AI Cloud For GCP」,site key 0x4AAAAAAF3nyyICSRHDlH3k(公開值)
  • Brevo:網域驗證通過、寄件人 noreply@jedicotech.com 驗證通過、四筆 DNS 已加

安裝包:188 /opt/guidant-ai-be/.build/bundle/guidant-ai-1.30.0.tar.gz,11-08 13:09 決策者重出(sha256 6b0f806f…),含 Turnstile 改動(開包核過 install.sh/compose/fe 的 nginx 與 entrypoint/Login.js)。這包有兩個問題:vulndb 檔是 600(457f2964a 已修出包腳本,包本身未重出)、沒帶 DEFAULT_UPDATE_SITE_URL(手跑出包沒設這個環境變數;CI 會帶)。VM 上已手動處理,家目錄的包已刪。

commits

repo commits
BE (已 push)db989fcc3 討論稿、5ac49c456 設計 v1.0、0209f3ea0 網址、ea6095af0 開卡、f7a336878 secret 改讀既有、5acafb2ce Turnstile 兩卡、bf880a7cf site key、f16174dcf 架構圖、1af8bb821 架構圖補 Agent、5a0f17ab0 交接、811a9ba44 memory、a108ab505 Turnstile 有填 key 就開(主產品)、本棒文件 commit
FE (已 push)f95a85b8 登入頁問後端決定 Turnstile、前門 CSP 依 site key 放行
infra (已 push)8194fb0 開機腳本補時區與 tpm2-tools、724e9c0 預算告警;(新建遠端分支)7ec4f3a 骨架、78e8255 專案 ID、6775501 工作坊登記、2227c0d 網址、224b744 對齊 8 GB/host、93e0287+31dc505 T1(小弟,已合回)、08c67b6 T2 runbook

§2 已定裁示(不要重新討論)

  1. VM 不是 K8s;一套 prod,不做 stg(11-07)
  2. 規格 e2-standard-2 8 GB(首腦建議 16,決策者裁 8,可改 machine_type 重 apply 升級)(11-07)
  3. DEPLOYMENT_MODE=host:saas 兩個既有缺口(common/authz/tenant_hq.py 總部判定讓郵件/LDAP/安全政策三頁消失、product_update_flow.py:193 只認 host 照)另開 FR(11-07)
  4. Turnstile 必開,是上線條件。做法:主產品改成「兩把都填真 key 才開」,裝機時 install.conf 填 GUIDANT_TURNSTILE_SITE_KEY/GUIDANT_TURNSTILE_SECRET_KEY(寫進 compose .env,升級保留)。F1、T4 取消(11-08)。admin 後台設定之後另做(11-08)
  5. DB/Redis 密碼決策者自帶;AI 與 SMTP 金鑰走後台或 install.conf,不進 .env(11-07)
  6. License 開通:裝完後決策者登入、在開通頁貼序號線上開通,不預先放序號(11-07 首腦講錯被糾正)
  7. 對外網址 guidant-ai.jedicotech.com;SSL 只對這個主機名稱設嚴格,不動全網域(11-08)
  8. 工作坊狀態決策者不動:T2 被工作坊誤派給小弟後卡住、工作坊暫停;決策者說「那邊狀態我不動,你知道就好」。首腦在 infra 主資料夾親手做完 T2、卡上已回寫。接手者也不要去動工作坊的狀態(11-08)
  9. Agent 資安問題(Cloudflare 憑證 90 天換、8443 健康檢查、8444 白名單預設空)交給另一個 session,本 arc 不處理(11-08)。該 session 已處理 Cloudflare 憑證那題:先在 BE 加「token 不拼指紋」開關(01fb1289e),決策者裁撤回(b0cc9b321,理由:設定檔開關沒人記得設、1.30.0 已出版不含開關),改由 Agent 端自己判斷(CM-3654):主系統憑證能被系統信任庫驗過(含 Cloudflare 公開憑證)就不比對指紋。所以雲端版 guidant.env 不用加任何指紋開關。CM-3654 的修正(evidence-agent 16ca2b5、bf262cf)已含在 Agent 1.7.0 正式版(11-08 已發),雲端版現在就能接 Agent;判準是「系統信任庫驗得過」不是寫死 Cloudflare,換其他 CDN/公開 CA 都不用改。CM-3654 卡在修正待驗證,待真機接一台 Agent 驗收
  10. 雲端版平常升級走更新中心線上更新、只收正式發版;P1 CI 直接部署仍做,定位救火用(11-08)。雲端版機器指紋不登記進更新站「內部主機清單」、guidant.env 不加 PRODUCT_UPDATE_ALLOW_SAME_VERSION

§3 下一步

🔴 下一個動作:Terraform Q&A(決策者發令再開始)。首腦陪決策者讀 infra repo,答 PM 會問的問題。

做法建議:

  1. 先用導讀 §2 檔案結構、§3 資源表帶一遍全貌(約 10 分鐘),決策者有問題隨時打斷
  2. 依決策者想看的部分開實際檔案,對著程式碼講;導讀 §7 是 PM 可能問的九題,可當練習
  3. 問到導讀沒寫、或答得不夠好的,查程式碼後補進導讀並 commit(infra repo,可直接 push)
  4. 需要的話產一頁給 PM 的簡報版(writing-technical-docs skill「系統說明」骨架,或 Artifact 一頁)

Q&A 之後才是:P1(救火用直接部署,批 3,可派小弟)→ O1(備份與災難復原,批 4,開工前先跟決策者定災難復原做法)→ V1(驗收,批 5)。

1.30.1 線上更新實績(11-08 16:45~16:57):八步全過(自我替換 → 預檢 → 收單 → 複製資料庫 123 MB/11 秒 → 複本套新版 → 健康檢查 → 換接 → 清理)。健康檢查在 Turnstile 開著時通過(a108ab505 的放寬生效)。升完 Turnstile、UPDATE_SITE_URL、Origin 憑證都在。

VM 升規格實績(11-08 17:00~17:06):guidantai stop → apply(約 2 分 45 秒)→ 開機腳本 exit 0、vTPM 解封成功 → guidantai start 9 容器 healthy。指紋不變、License 不用重開。前後對照:VM 內 API 80~130 ms → 17~24 ms;CPU pressure 28% → 4%;可用記憶體 1.7 GB → 9.4 GB。注意:停過服務再改規格,重開機後要手動 guidantai start(容器停下的狀態會保留)。

裝機時實際走過的步驟與坑(下次重建 VM 照這個):

  • 包從 188 直接 scp 到 VM(188 的 known_hosts 已收 VM host key);家目錄系統碟只有 30 GB,包+解開約 7 GB,解開後刪壓縮檔
  • install.conf 在 Mac 從 Secret Manager 組(600,單引號包值),scp 上 VM;裝完 shred -u
  • 先 --check-only;主機時區由開機腳本設 Asia/Taipei(infra 8194fb0)
  • Ubuntu 24.04 要 tpm2-tools:已由開機腳本預裝(infra 8194fb0);主產品 --check-only 也會照實報錯(b3b21a950)
  • 🔴 vulndb 檔 600 → 第⑥-3 步 Permission denied:chmod 644 後 sudo GUIDANT_REUSE_EXISTING_INSTALL=1 ./install.sh --config install.conf 重跑(沿用已產的密碼)
  • 裝完換 Origin 憑證:install -m 600 -o 101 -g 101 放 key、sudo guidantai restart guidant-fe
  • 🔴 手跑出包沒有 UPDATE_SITE_URL:用 sudo EDITOR=<追加一行的腳本> guidantai config edit guidant.env 補 UPDATE_SITE_URL=https://guidantai-update.jedicotech.com,再 sudo guidantai start
  • installer 自產的九組回寫 Secret Manager(§1)

之後順序:P1 管線 gcp-prod(救火用,可派小弟,批 3)→ O1 備份告警(批 4,開工前先跟決策者定災難復原做法)→ V1 驗收(批 5)。

⚠️ 這節是建議清單,不是執行授權。接手方盤點完即停,等決策者發令。

§4 待辦與風險

  • T3 卡 Model 欄空、工作坊恢復時會被誤派:決策者不動工作坊,接手者也不要動,但要提醒決策者
  • 災難復原路徑沒解:installer 把設定檔加密鑰匙密封進 vTPM;快照只拍資料碟,/etc/machine-id 在系統碟;install.sh 提示寫「換機器需走 --restore」但沒有這個選項(T1 小弟實查 install.sh:770~774)。O1 開工前要跟決策者定
  • 已發布的 1.30.0 給新客戶手動安裝會撞 vulndb 600:包內 19 個檔 600(映像 8、vulndb 1、storage-seed 10),只有 vulndb 是掛給容器 uid 1000 讀才會壞;解包帳號不是 uid 1000 時第⑥-3 步失敗(雲端 VM 的 jedi 是 1001 才撞到;188/189 的 jedi 剛好 1000)。線上更新由 systemd root 解包、保留包內 1000/1000,不受影響。修正 457f2964a+9086e8ad8(改接既有 dist_perms.sh 整包正規化+斷言,已 push),1.30.1 帶上。暫時解法:chmod 644 後 GUIDANT_REUSE_EXISTING_INSTALL=1 重跑
  • tpm2-tools 已修(1.30.1 帶上):主產品 b3b21a950 讓 --check-only 與正式安裝共用 OS 判斷,24.04 上檢查就會照實報錯;infra 8194fb0 開機腳本預裝 tpm2-tools 並設時區(已 apply、VM 上重跑驗過)。重建 VM 只剩 Origin 憑證與 188 known_hosts 要手動(runbook「重建 VM 後要手動補的」)
  • 郵件設定全站共用、「總部」可改:SMTP 只有一筆(tenant 1,key *),所有租戶共用。host 模式下安裝精靈建的 Billows 被 common/authz/tenant_hq.py 判為總部,其管理員能改全站寄信/LDAP/登入政策。Billows 留給自家用,外部試用客戶另開租戶;根治在 saas FR
  • SMTP 設定 has_secret: false 被存回 DB:secret 欄有值(加密、長度 204)、寄信正常,has_secret 是讀取時算的旗標卻被畫面原樣存回。不影響使用,另開小卡
  • 雲端租戶的儲存:每租戶一份 STORAGE_CONFIG,新租戶複製 root(SeaweedFS、同一個 bucket guidant-ai,檔名時間+uuid 不撞名,歸屬靠 DB 紀錄+RLS);租戶可改指向自己的 Agent(檔留在客戶內網,雲端只留參照)。compliance.remote_agents 有 RLS 但沒 FORCE、INSERT policy 無條件(註冊 token 綁租戶,實務上擋得住),交 Agent 資安那條線確認
  • Cloudflare 真實 IP:FE 83556a06(CM-3655,另一 session)已讓前門只信 Cloudflare IP 範圍送來的真實 IP,1.30.0 起帶上,未在雲端實測稽核紀錄
  • 外部延遲 0.65~1.25 秒主要是網路:Cloudflare 免費方案把台灣使用者分到 SJC(聖荷西),VM 內只要 17~24 ms。/assets/*(檔名帶雜湊)Cloudflare 已快取(預設 4 小時);theme-init.js、layout/ 主題檔名固定也被快取 4 小時,前端改主題時升級後最多 4 小時不生效。可改前門 nginx 分檔種設 Cache-Control(雜湊檔 1 年 immutable、固定檔名 no-cache),屬產品改動。決策者 11-08 裁:先不動,看客戶反應
  • 告警確認信:Google 寄到 raymond.jedicotech@gmail.com,決策者要點,未確認是否點了
  • License Center 的 Cloudflare Access 放行 35.194.213.124:決策者要做,未確認是否做了

§5 Pre-flight

gcloud config configurations list                       # guidantai 要 active
gcloud billing projects describe guidant-ai-cloud-514012 --format="value(billingEnabled)"   # True
gcloud secrets list --project=guidant-ai-cloud-514012 --format="value(name)" | wc -l       # 18
ssh -o ConnectTimeout=8 jedi@35.194.213.124 'free -g | sed -n 2,3p; df -h /srv/guidant-ai | tail -1; ls /srv/guidant-ai'
curl -s -o /dev/null -w "%{http_code}\n" -m 15 https://guidant-ai.jedicotech.com/          # 200
curl -s -m 15 https://guidant-ai.jedicotech.com/api/1.0/setup/status                      # turnstile.enabled true
ssh jedi@35.194.213.124 'sudo guidantai status | grep -c healthy'                         # 9
cd ~/Projects/Billows/Audit-Manager/guidant-ai-infra/envs/gcp-prod && terraform plan -detailed-exitcode >/dev/null; echo $?   # 0=無漂移

§6 環境座標

東西 在哪
infra repo ~/Projects/Billows/Audit-Manager/guidant-ai-infra(GitLab auditmanager/guidant-ai-infra);導讀 docs/terraform-walkthrough.md
Terraform 初始化 terraform init -backend-config="bucket=guidant-ai-cloud-514012-tfstate"
Cloudflare 權杖 infra .env CLOUDFLARE_API_TOKEN、CLOUDFLARE_ZONE_ID(600、不入版控)
安裝包 188 /opt/guidant-ai-be/.build/bundle/
installer 原始碼 BE scripts/installer/install.sh、install.conf.example
設計站 BE docs/features/FR-184-2611-gcp-vm-deployment/(index.html 雙擊可開)
架構圖 同上 diagrams/cloud-architecture.png,圖源 cloud_architecture.py(venv 要裝 diagrams 0.25.1)
工作坊 http://127.0.0.1:7777;工作坊 46 暫停中

§7 行為規範提醒

  • infra repo:runner 只做到 plan,apply/destroy 首腦或決策者做;plan 出現 destroy/replace 停下
  • 祕密不進 repo、不進對話、不進 Notion;從 Secret Manager 讀值只在記憶體或 600 暫存檔裡流動,用完刪
  • docker compose up -d 必帶 service 名
  • 首腦親手的卡開卡時直接開成「暫緩」,不靠 Model 留空擋派(memory feedback_brain_handson_cards_open_as_paused_not_empty_model)
  • 不切 branch;commit 可自行做;push 自 2026-10-31 起常態授權(推自己當下 branch、推前 fetch、不推 main、不 force)

§8 不在本期 scope

K8s;saas 模式;AWS module;多租戶自助註冊;高可用;Agent 資安(另一個 session);Cloudflare DNS 收進 Terraform。

§9 給 fresh session 的短 prompt

請接手 FR-184(主系統上 GCP),這一棒要陪我做 Terraform Q&A(PM 會問)。先讀 docs/features/FR-184-2611-gcp-vm-deployment/handoff/FR-184-STATE.md,照 §0 讀序讀完(含 infra repo 的 docs/terraform-walkthrough.md 與實際 .tf 檔)、答 §0.4 冷接自檢,再跑 §5 pre-flight。
讀取與盤點完成後不做任何事——不派工、不跑測試、不改檔、不 ssh 上 VM 動手、不 terraform apply,回報自檢答案與現況後等我下指令才開始作業。