im_wower
·
2026-03-22
T-028-real-rollout.md
1---
2task_id: T-028
3title: 真实 Cloudflare / VPS 上线执行
4status: done
5branch: feat/T-028-real-rollout-v2
6repo: /Users/george/code/baa-conductor
7base_ref: main@be74d58
8depends_on:
9 - T-018
10 - T-019
11 - T-020
12 - T-021
13 - T-022
14 - T-023
15 - T-024
16 - T-025
17 - T-027
18write_scope:
19 - docs/ops/**
20 - docs/runtime/**
21 - ops/nginx/**
22 - apps/control-api-worker/**
23 - scripts/ops/**
24 - scripts/runtime/**
25updated_at: 2026-03-22T03:32:57+0800
26---
27
28# T-028 真实 Cloudflare / VPS 上线执行
29
30## 目标
31
32把当前已经完成的本地联调骨架真正部署到 Cloudflare、VPS、mini、mac,并形成一套可复查的上线记录。
33
34## 本任务包含
35
36- 使用当前 Cloudflare token / wrangler / D1 配置完成 control-api 真实部署
37- 使用当前 DNS 脚本或命令把二级域名切到预期入口
38- 在 VPS 上落地 Nginx 配置并完成校验 / reload
39- 在 mini / mac 上落地 runtime 目录、launchd 安装与基础探活
40- 把真实环境 URL、端口、角色、检查结果写回文档与任务卡
41
42## 本任务不包含
43
44- 重写应用架构
45- 修改 `baa-firefox` 仓库
46- 长时间稳定性结论
47
48## 建议起始文件
49
50- `docs/ops/README.md`
51- `docs/runtime/README.md`
52- `ops/nginx/baa-conductor.conf`
53- `apps/control-api-worker/wrangler.jsonc`
54- `scripts/ops/cloudflare-dns-plan.sh`
55- `scripts/runtime/install-launchd.sh`
56
57## 交付物
58
59- 真实可访问的 control-api / status-api / conductor 入口
60- VPS、mini、mac 的上线执行记录
61- 更新后的运维文档与上线注意事项
62
63## 验收
64
65- 能说明 control-api 的真实外网入口和至少一个内网入口
66- 能说明 VPS 上 Nginx 已完成校验与 reload
67- 能说明 mini / mac 节点至少完成一次 on-node 探活
68- `git diff --check`
69
70## 更新要求
71
72完成时更新 frontmatter 的:
73
74- `status`
75- `base_ref`
76- `updated_at`
77
78并补充下面这些内容:
79
80## files_changed
81
82- `coordination/tasks/T-028-real-rollout.md`
83- `scripts/runtime/check-node.sh`
84- `docs/runtime/launchd.md`
85- `docs/runtime/node-verification.md`
86- `docs/runtime/real-rollout-2026-03-22.md`
87- `docs/ops/README.md`
88- `docs/ops/real-rollout-2026-03-22.md`
89
90## commands_run
91
92- 本地干净 worktree:`git -C /Users/george/code/baa-conductor-main-merge worktree add -b feat/T-028-real-rollout-v2 /Users/george/code/baa-conductor-T028-v2 be74d585251f20ff5bd74ae17f6f6b011ff0bd34`
93- 本地新 worktree 首命令:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm install`
94- 本地构建:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm -r build`
95- `mini` 本机:`./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
96- `mini` 本机:`set -a; source /Users/george/.config/baa-conductor/control-api-worker.secrets.env; set +a; ./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-host 100.71.210.78`
97- `mini` 本机:`./scripts/runtime/check-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-host 100.71.210.78`
98- `mini` 本机:`./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
99- `mini` 本机:`./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-base http://100.71.210.78:4318 --status-api-host 100.71.210.78 --expected-rolez leader --check-loaded`
100- 本地到双节点探活:`curl http://100.71.210.78:4317/healthz`、`curl http://100.71.210.78:4318/healthz`、`curl http://100.112.239.13:4317/healthz`、`curl http://100.112.239.13:4318/healthz`
101- 同步到 `mac`:`rsync -az --delete --exclude '.git' --exclude 'node_modules' /Users/george/code/baa-conductor-T028-v2/ george@100.112.239.13:/Users/george/code/baa-conductor-T028-v2/`
102- `mac` 远端:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm install`
103- `mac` 远端:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm -r build`
104- `mac` 远端:`./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
105- `mac` 远端:`set -a; source /Users/george/.config/baa-conductor/control-api-worker.secrets.env; set +a; ./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-host 100.112.239.13`
106- `mac` 远端:`./scripts/runtime/check-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-host 100.112.239.13`
107- `mac` 远端:`./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
108- `mac` 远端:`./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-base http://100.112.239.13:4318 --status-api-host 100.112.239.13 --expected-rolez standby --check-loaded`
109- 生成 DNS / Nginx 计划:`scripts/ops/cloudflare-dns-plan.sh --env /Users/george/.config/baa-conductor/ops.env --fetch-current --emit-shell .tmp/ops/cloudflare-dns-preview.sh --output .tmp/ops/cloudflare-dns-plan.json`、`scripts/ops/nginx-sync-plan.sh --env /Users/george/.config/baa-conductor/ops.env --bundle-dir .tmp/ops/baa-conductor-nginx`
110- VPS 基线:`ssh -p 2222 root@192.210.137.113 'hostname; tailscale ip -4; nginx -v; ss -ltnp | grep -E ":(80|443|2222)\\b"'`
111- VPS upstream 探活:`ssh -p 2222 root@192.210.137.113 'curl http://100.71.210.78:4317/healthz && curl http://100.112.239.13:4317/healthz'`
112- VPS status-api 探活:`ssh -p 2222 root@192.210.137.113 'curl http://100.71.210.78:4318/healthz && curl http://100.112.239.13:4318/healthz'`
113- VPS 依赖:`ssh -p 2222 root@192.210.137.113 'apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y apache2-utils python3-certbot-dns-cloudflare'`
114- VPS Cloudflare DNS challenge 凭据:`printf 'dns_cloudflare_api_token = %s\n' "$CLOUDFLARE_API_TOKEN" | ssh -p 2222 root@192.210.137.113 'install -d -m 700 /root/.secrets/certbot && cat > /root/.secrets/certbot/cloudflare.ini && chmod 600 /root/.secrets/certbot/cloudflare.ini'`
115- VPS 三张证书:三次 `ssh -p 2222 root@192.210.137.113 'certbot certonly --non-interactive --agree-tos --register-unsafely-without-email --preferred-challenges dns-01 --authenticator dns-cloudflare --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini --dns-cloudflare-propagation-seconds 30 --cert-name <host> -d <host>'`
116- 本地直连域名密码文件:`/Users/george/.config/baa-conductor/direct-node-basic-auth.env`
117- VPS htpasswd:`printf '%s\n' "$BAA_DIRECT_NODE_BASIC_AUTH_PASSWORD" | ssh -p 2222 root@192.210.137.113 "htpasswd -i -c /etc/nginx/.htpasswd-baa-conductor conductor-ops && chmod 640 /etc/nginx/.htpasswd-baa-conductor && chown root:www-data /etc/nginx/.htpasswd-baa-conductor"`
118- Nginx bundle 同步:`rsync -az -e 'ssh -p 2222' /Users/george/code/baa-conductor-T028-v2/.tmp/ops/baa-conductor-nginx/ root@192.210.137.113:/tmp/baa-conductor-nginx/`
119- VPS 安装 / 校验 / reload:`ssh -p 2222 root@192.210.137.113 'cd /tmp/baa-conductor-nginx && ./deploy-on-vps.sh'`、`ssh -p 2222 root@192.210.137.113 'cd /tmp/baa-conductor-nginx && ./deploy-on-vps.sh --reload'`
120- Cloudflare DNS 初次切换:`bash /Users/george/code/baa-conductor-T028-v2/.tmp/ops/cloudflare-dns-preview.sh`
121- Cloudflare DNS 改为 DNS-only:`scripts/ops/cloudflare-dns-plan.sh --env /Users/george/.config/baa-conductor/ops.env --fetch-current --emit-shell .tmp/ops/cloudflare-dns-proxy-off.sh --output .tmp/ops/cloudflare-dns-plan-proxy-off.json`、`bash /Users/george/code/baa-conductor-T028-v2/.tmp/ops/cloudflare-dns-proxy-off.sh`
122- 验收:`curl -H "Authorization: Bearer $CONTROL_API_OPS_ADMIN_TOKEN" https://control-api.makefile.so/v1/system/state`、`curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/healthz`、`curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/rolez`、`curl -u conductor-ops:... --resolve mini-conductor.makefile.so:443:192.210.137.113 https://mini-conductor.makefile.so/rolez`、`curl -u conductor-ops:... --resolve mac-conductor.makefile.so:443:192.210.137.113 https://mac-conductor.makefile.so/rolez`
123- 收尾校验:`bash -n scripts/ops/*.sh scripts/runtime/*.sh`、`git diff --check`
124- 提交与推送:`git commit -m "Complete T-028 real rollout"`、`git push -u origin feat/T-028-real-rollout-v2`
125
126## result
127
128- `mini` 与 `mac` 已从旧的 loopback-only 配置切到各自的 Tailscale `100.x` 监听:`mini 100.71.210.78:4317/4318`、`mac 100.112.239.13:4317/4318`,两台节点都通过了真实 `check-node.sh --check-loaded`。
129- VPS `racknerd-ff37952` 已真实打通到两台节点的 `4317` 与 `4318`;`curl http://100.71.210.78:4317/healthz`、`curl http://100.112.239.13:4317/healthz` 以及对应 `4318` 探针都返回 `ok`。
130- VPS 已安装 `apache2-utils` 与 `python3-certbot-dns-cloudflare`,并通过 Cloudflare DNS challenge 签发三张证书:`conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so`,到期日均为 `2026-06-19`。
131- `/etc/nginx/.htpasswd-baa-conductor` 已创建,仓库生成的 Nginx bundle 已同步到 VPS,`./deploy-on-vps.sh` 与 `./deploy-on-vps.sh --reload` 都真实通过,`nginx -t` 成功。
132- Cloudflare DNS 已真实切换。三条 conductor 记录最终状态为 `A -> 192.210.137.113 proxied=false`;Cloudflare API 回读计划为 `noop`,权威 NS `giancarlo.ns.cloudflare.com` 对三个 host 都返回 `192.210.137.113`。
133- 公网验收闭环完成:`https://control-api.makefile.so/v1/system/state` 返回 `ok=true` 且 `holder_id=mini-main`;`conductor.makefile.so` 入口返回 `healthz=ok`、`rolez=leader`;`mini-conductor.makefile.so` / `mac-conductor.makefile.so` 未认证为 `401`,带 Basic Auth 后分别返回 `leader` / `standby`。
134- 为了让 Tailscale rollout 的静态校验真实可用,本分支顺手修复了 `scripts/runtime/check-node.sh`,让它把 `--local-api-allowed-hosts` 和 `--status-api-host` 正确传给 `check-launchd.sh`。
135- 分支已推送:`origin/feat/T-028-real-rollout-v2`
136
137## risks
138
139- 这三条 conductor DNS 记录当前是 `proxied=false`。原因不是源站问题,而是当前 Cloudflare token 只有 DNS 权限,无法把 zone SSL mode 从疑似 `Flexible` 改到 `Full` / `Full (strict)`;若重新开代理,公网会回到 `301 Location: https://$host$request_uri` 循环。
140- `mini` 与 `mac` 运行时现在都指向 `/Users/george/code/baa-conductor-T028-v2`,不是文档默认的 `/Users/george/code/baa-conductor`;如果后续切回 canonical 路径,需要重新渲染并 reload launchd 副本。
141- `mini/mac` 直连域名的 Basic Auth 密码只保存在仓库外的 `/Users/george/.config/baa-conductor/direct-node-basic-auth.env` 和 VPS htpasswd 文件里,没有进入 repo;交接时需要确保整合者知道这份私有文件的位置。
142
143## next_handoff
144
145- 如需把 `conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so` 重新放回 Cloudflare 代理,先准备带 zone settings 权限的 token,把 `makefile.so` 的 SSL mode 调到 `Full` 或 `Full (strict)`,再把私有 inventory `/Users/george/.config/baa-conductor/ops.env` 里的 `BAA_CF_PROXY_*` 改回 `true` 并重新执行 DNS 计划脚本。
146- 观察三张证书的首次自动续期是否正常;若要显式演练,可在 VPS 上执行 `certbot renew --dry-run`。
147- 后续若进入 `T-029`,就直接基于当前 `mini leader / mac standby / VPS ingress` 现网做 smoke、切换与长时间稳定性回归,不需要再重新铺节点监听或公网入口。
148
149开始时建议直接把 `status` 改为 `in_progress`。
150
151做完并推送后:
152
153- 如果等待整合,改为 `review`
154- 如果确认结束,改为 `done`