← 返回 Day 169 正文

Day 169 · 练习与清单

Day 169 — Fabric 批量执行 · 完成清单与练习

先读 README.md(第 2、3 节),再跑 code/ 下 4 个脚本,最后做练习。 每道题都要能回答"为什么" —— 尤其"为什么读用并发、写用串行"。


✅ 今日完成清单

一、环境与依赖

二、跑通示例代码(先预测输出,再运行验证)

三、动手改造("读懂了"的唯一证明)

四、概念自检(不看 README)


📝 基础练习题(1–3)

练习 1:把 Result 翻译成一行运维摘要

写一个 summary(r),对任意 Result 返回一行结构化摘要:

ok    | web-01 | exit=0 | 0.42s | up 3 days, load 0.4
FAIL  | web-02 | exit=1 | 0.51s | nginx: inactive (dead)
KILLED| web-03 | exit=137 | 0.09s | (无输出,疑似 OOM)
ABORT | web-04 | exit=None | 5.00s | (超时/自动应答失败)

要求:

  1. 用 r.exited 区分 ok / FAIL / KILLED(≥128 或 <0 算 KILLED)/ ABORT(None)
  2. 摘要优先取 stdout 第一行,为空时取 stderr 第一行,都空则 (无输出)
  3. 写 4 条 --self-test 断言,覆盖四种状态
  4. 说明:为什么把 KILLED 与 FAIL 分开?(提示:处置动作不同)

练习 2:安全的目标解析(禁止猜用户)

写 parse_target(text) 支持:

deploy@web-01:2222   → (deploy, web-01, 2222)
deploy@web-01        → (deploy, web-01, 22)
web-01               → ValueError("必须显式给出用户")

要求:

  1. 端口范围校验 1~65535
  2. IPv6 字面量 deploy@[::1]:2222 也要支持
  3. 为什么"必须显式给出用户"?用一句话说清与 sudo/cron 的关系
💡 提示 脚本在 CI 或 cron 里跑时,`$USER` 可能是 `root`,也可能为空。 "不猜"能避免你把变更发到错误的账号上 —— 这类事故在共享跳板机上很常见。

练习 3:分片并发的最小实现

不借助 Fabric 的 Group,用 ThreadPoolExecutor + Connection 自己实现 run_chunked(hosts, cmd, batch=8):

  1. 每批最多 batch 台同时执行
  2. 返回 {host: Result | Exception}
  3. 任何一台失败都不影响其他机器
  4. 打印每批耗时与累计进度 [3/13]

并说明:自己实现相比 ThreadingGroup 多得到了什么能力? (提示:限流粒度、每台独立超时、失败重试的粒度。)


🚀 进阶挑战题(4–5)

练习 4:给部署工具加"金丝雀 + 自动决策"

在 04 上扩展:

  1. --canary 1:先只发 1 台,等它健康检查通过再发剩余机器
  2. 金丝雀失败 → 直接中止,不碰剩余主机,退出码 2
  3. --soak 30:金丝雀起来后持续观察 30 秒(每 5 秒探一次 /healthz), 期间任一次失败 → 回滚金丝雀并中止
  4. 报告里明确标注哪台是金丝雀

思考并写下:为什么"金丝雀 + 浸泡观察"比"一次性全量"更值得写进脚本? 提示从"故障半径"与"回滚成本"两个角度回答。

💡 提示 金丝雀的价值不在"少发几台",而在**把故障暴露在影响半径最小的时候**。 浸泡观察解决的是"启动成功但 30 秒后崩"这类慢故障 —— 而健康检查只看当下。

练习 5:把"部署"与"巡检"合成一条流水线

写一个 fab 任务(@task),串起三件事:

fab -f ops.py release --rev=20260927 --hosts=h1,h2,h3
      ① 部署前巡检:磁盘 < 85%、服务当前是 active、目标版本未部署过(幂等)
      ② 逐步滚动部署(复用 04 的逻辑)
      ③ 部署后巡检:健康检查 + 版本核对(cat current/VERSION)
      → 任一步骤失败:打印"停在哪一步/哪一台" + 回滚建议 + 退出码 1

要求:

  1. 用 @task + Context,命令行能传 --rev 与 --hosts
  2. "未部署过"用版本标记文件判断(幂等)
  3. 每一步的结论都写进 deploy-audit.jsonl
  4. 说明:为什么"部署前巡检"能省掉很多回滚?(提示:把可预见的失败前置)
💡 提示 `@task` 函数的第一个参数是 `Context`(习惯叫 `c`),**不是** `Connection`: 需要远端连接时要自己 `Connection(host, ...)`。 `@task(hosts=[...])` 或 `--hosts` 由你自己解析 —— Fabric 只负责把参数传进来。