FDE 模块 02/08 讲了 Docker、K8s、CI/CD、Prometheus 的"怎么做"。本页把它们串成工程方法论:可观测性三支柱(指标/日志/链路)与 OpenTelemetry 标准、GitOps、Terraform 状态深入、SLO/错误预算、混沌工程、事故复盘。学完你不再是"会敲 kubectl",而是能设计一条"可观测、可复用、可回滚"的可靠交付链路。
CI/CD 流水线:阶段、制品、门禁、GitOps
CI(持续集成)让每次提交都自动构建测试;CD(持续交付/部署)把产物自动推向环境。但真正的成熟标志是"可回滚"——任何一次发布都应该能一键退回上一版,而不是"上线即赌命"。这一章把流水线拆成可复用的零件。
一条流水线的典型阶段
把交付想成一条传送带:代码进左端,能放心跑在生产就是出右端。中间每一段做一件事、且失败就截断。
| 阶段 | 做什么 | 失败后果 |
|---|---|---|
| ① 构建 Build | 编译/打镜像 | 代码都编不过,立刻红 |
| ② 测试 Test | 单测+集成+静态 | 质量门禁拦截 |
| ③ 制品 Artifact | 出不可变版本 | 产物无法复现 |
| ④ 部署 Deploy | 推到环境 | 回滚到上一版本 |
| ⑤ 验证 Verify | 健康检查/冒烟 | 自动回滚 |
阶段之间用"门"隔开:任一段失败,后续段不执行,并通知提交者。这保证"坏变更走不到生产"。
把阶段写进 CI:可抄的配置
下面是一条覆盖"构建→测试→出镜像"的 GitHub Actions 流水线。注意它把测试失败设成硬门槛,且镜像用 commit SHA 打标签(不可变)。
论为什么用 commit SHA 而不是 latest
latest 是移动的靶子——你不知道生产跑的是哪次构建。每个制品用唯一版本(SHA/语义版本)标记,部署和回滚都能精确定位"这一份",也便于审计"哪个版本引入了 bug"。
制品与不可变版本:部署的是"同一份"
"不可变制品"指:构建一次,测试通过,之后无论部署到测试/预发/生产,用的都是同一个二进制/镜像,绝不在环境间重新编译。否则"测试环境好的"和"生产跑的"可能不是同一份,排查无从下手。
做法:构建时打唯一版本(如 1.4.2 或 commit SHA),"晋级"时只改引用、不重新构建。测试环境验过的那一份,原封不动推上生产。
SSH 上服务器改个配置"先救火"——下次部署这份改动就丢了,还和代码库不一致。配置要么进版本库(GitOps),要么进配置中心,永远别让生产状态脱离代码。
质量门禁嵌在流水线
门禁是流水线的"闸":覆盖率、安全扫描、复杂度任一不过,就不许进下一阶段。它把质量规则从"人自觉"变成"机器强制"。
蓝绿 / 金丝雀:把"全量故障"变成"可控试错"
论两种发布策略的本质
蓝绿:两套环境,切流量瞬间完成,回滚也只切回去(秒级)。金丝雀:先放 1% 流量验证,再逐步放大到 10%/50%/100%。两者目的都是把"全量故障"变成"可控的小范围试错",出问题影响面小、回退快。
GitOps:以 Git 为唯一事实源
传统做法是"人敲命令改集群";GitOps 把期望状态写进 Git,由控制器(ArgoCD/Flux)持续把集群拉向 Git 里的状态。好处:每次变更有 PR 记录、可审计、可一键回滚(回退 commit 即可)。
论GitOps vs 普通 CI/CD
CI/CD 管"怎么构建发布"(推 push),GitOps 管"集群该是什么样、并自动维持"(拉 pull + 持续对齐)。后者每次变更可审计、回退 commit 即回滚,且自动修正配置漂移,更抗"生产被手改坏"。
流水线反模式:这些坑别踩
① 半成品合并:直接往 main 推、不跑 PR 检查,靠"反正能跑"。② 门禁形同虚设:覆盖率设了但 --cov-fail-under 写 0,永远绿。③ 手工改生产:部署后 SSH 上去改配置,下次部署覆盖、还和 Git 不一致。④ 巨型单体流水线:改一行前端也要等后端全量测完,反馈慢到没人等。
论小步快跑才是王道
把大流水线拆成"快门禁(秒级)+ 慢验证(分钟级)"两层,主分支保护靠 PR 检查,长任务放合并后异步跑。目标是"提交后几分钟内给可信反馈"。
容器 Docker:分层、多阶段、体积与安全
FDE 模块 02 给了基础 Dockerfile。这一章讲清镜像为什么分层、多阶段构建怎么把镜像砍小、体积优化技巧、以及镜像安全(别用 root、别用 latest、要扫描)——这些直接决定部署速度与攻击面。
镜像分层原理:为什么顺序很重要
Docker 镜像由一层层(layer)叠成,每层是"上层的差异"。层是缓存单元:某层不变,构建就复用缓存;某层一变,它之后所有层都失效重跑。所以"变动少的放前面,变动多的放后面"。
论依赖放前面 = 构建快十倍
把"装依赖"(很少变)和"拷源码"(常变)分开:先 COPY package.json 装依赖并缓存,再 COPY 源码。改一行业务代码时,依赖层命中缓存,不用重装几百个包。
多阶段构建:构建环境和运行环境分离
编译需要一整套工具链(JDK、gcc),但运行只需要产物。多阶段构建用"胖阶段"编译、把产物拷进"瘦阶段"运行,最终镜像不含编译器,体积和漏洞都大减。
一个 FROM maven 直接 CMD java 的镜像可能 800MB+ 且带着 git/ssh 密钥。多阶段后常能砍到 1/10,启动更快、被攻破的面更小。
体积优化技巧清单
| 技巧 | 效果 |
|---|---|
| 多阶段构建 | 去掉编译器/构建依赖 |
| 用 slim/alpine 基础镜像 | 去文档与多余工具 |
合并 RUN + 清缓存 | 减少层内垃圾 |
.dockerignore | 别把 .git/node_modules 打进上下文 |
| 非 root 运行 | 降权限(见下节) |
镜像安全:别用 root、别用 latest、要扫描
镜像默认常以 root 跑、拉 latest 不可复现、还可能带已知 CVE。三条铁律:固定基础镜像版本、非 root 运行、CI 里扫漏洞。
FROM python:latest 意味着"下次构建可能悄悄换了大版本",今天绿明天红。永远钉版本(python:3.12.4-slim),并定期升级而非被动漂移。
容器运行时的隐藏陷阱
镜像对了,运行时还可能翻车:PID 1 不转发信号导致优雅停机失效、写进容器层的文件重启即丢、资源不设限被一个容器拖垮整台机。
论有状态别放容器里
容器文件系统是易逝的——重启/调度走,数据就没。数据库、缓存的持久化必须挂卷(volume)或走外部存储。把容器当"随时可换的无状态积木",状态交给专门的基础设施。
镜像供应链:签名与 SBOM
镜像不只是"能跑",还要"可信"。SBOM(软件物料清单)列清镜像里每一层装了什么依赖,签名(cosign)证明"这镜像确实出自我们的流水线、没被篡改"。供应链攻击(投毒依赖)的防线就在这。
论零信任也适用于镜像
别假设"内网拉的镜像就安全"。依赖可能已被投毒。签名 + SBOM + 定期重扫,让你在"某依赖曝出 CVE"时一秒查出"哪些镜像受影响、该先修谁"。
BuildKit 与缓存加速:别每次重装依赖
默认 docker build 在 CI 里每次都从头来,慢。开启 BuildKit + 挂载缓存(cache mount),让依赖下载跨构建复用,常能把"装依赖"从几分钟压到几秒。
论构建慢是反馈慢的源头
"改一行等十分钟构建"的团队,会本能地少提交、少测。把镜像构建压到秒级,CI 才敢高频跑,快速反馈闭环才转得起来。
编排 Kubernetes:Pod / Deployment / Service / Ingress
FDE 模块 02 给了最小 K8s 例子。这一章把核心对象讲透:Pod 是最小调度单位、Deployment 管副本与滚动更新、Service 做稳定寻址、Ingress 管外部流量,再加 HPA 自动扩缩、存储与探针。目标:能读懂一份真实清单。
核心对象:Pod 与 Deployment
Pod 是"一个或多个共享网络的容器"的最小运行单元;Deployment 在它之上管"我要几个副本、怎么滚动更新、挂了怎么拉起"。你几乎不直接操作 Pod,而是操作 Deployment。
Service 与 Ingress:流量怎么进来
Pod 的 IP 会随重启变,不能直接对外暴露。Service 给一组 Pod 一个稳定虚拟 IP 和 DNS 名(按标签选后端);Ingress 在集群边缘把外部 HTTP 路由到内部 Service。
论为什么需要 Service 这层
没有 Service,调用方得自己跟踪"现在哪几个 Pod 活着、IP 是多少"。Service 用标签选择器 + 端点自动同步,把"寻址"从应用里抽出来交给集群,应用只认一个稳定名字。
HPA:按指标自动扩缩
流量有峰谷,人工调副本数不现实。Horizontal Pod Autoscaler 按 CPU/内存或自定义指标自动增减副本,峰值来了自动扩容、闲时自动缩容省钱。
存储:PV / PVC / ConfigMap / Secret
容器易逝,状态要外置。PVC(持久卷声明)向集群要一块持久盘;ConfigMap 存非机密配置;Secret 存密码/令牌(虽叫 Secret,默认只 base64,需配合加密)。
K8s Secret 默认只是 base64 编码,谁有读权限就能解码。生产要开 etcd 静态加密或接外部密钥管理(Vault),且别把 Secret 写进镜像或 Git。
就绪与存活探针:让更新不中断
就绪探针(readiness):没准备好就不接流量(滚动更新时新 Pod 没热好不进流量)。存活探针(liveness):挂了就重启。两者分清,否则"启动慢"会被误杀、"假死"却还在接流量。
论探针是"零停机发布"的底座
滚动更新时,K8s 先起新 Pod,等它就绪探针过了才把旧 Pod 摘下——用户全程无感知。没配探针,新 Pod 一启动就被灌流量,可能返回 500。
常见坑:就绪探针只探"端口通了"而非"能服务",于是缓存还没热就被放流量。探针要探真实依赖(如能连上 DB),别只探端口。
常见 K8s 事故清单
① 没设资源 limit:一个 Pod 吃光节点,邻居全被挤走。② 用 latest:滚动更新拉到不同版本。③ 探针路径错:Pod 一直 not ready,副本永远 0。④ 把数据库跑在容器里没挂卷:Pod 一重建数据没了。
资源 Request / Limit:配错就崩或就浪费
Request 是"我至少要多少"(调度器据此排节点、保证预留);Limit 是"最多能用多少"(超了被 throttle 或 OOMKill)。两者都没设,节点会被一个 Pod 撑爆;设太宽,资源利用率低、成本高。
只写 limit 时,K8s 会把 request 默认成 0,调度器以为这 Pod 不占资源,可能把一堆都塞到同一节点,实际一起跑就抢 CPU、相互 throttle。生产务必 request/limit 成对配。
IaC:Terraform / Ansible、声明式与漂移
FDE 模块 02 给了 Terraform 示意。深入点:IaC 用代码描述基础设施,让环境可版本化、可重现。本章讲清声明式 vs 命令式、Terraform 的 plan/apply 工作流、状态文件管理、模块化复用、配置漂移,以及 Ansible 适合什么。
声明式 vs 命令式
命令式是"一步步告诉我怎么做"(跑脚本),声明式是"告诉我终态长啥样,工具自己想办法达到"。IaC 主流(Terraform/K8s)都是声明式。
| 命令式 | 声明式 | |
|---|---|---|
| 你写 | 操作步骤 | 期望状态 |
| 重跑 | 可能重复创建 | 幂等,反复应用同态 |
| 代表 | Shell/Ansible | Terraform/K8s |
Terraform 工作流:plan / apply
plan 先算"现实 vs 期望"的差异并预览变更(不动手);apply 才真正执行。永远先 plan 看清楚再 apply——这是 IaC 防手滑的核心纪律。
论为什么 apply 前必看 plan
plan 会告诉你"要新建 3 个资源、替换 1 个、删除 0 个"。看到"替换/删除"生产数据库这类字样,立刻停手——可能是一个参数写错会把库重建。IaC 让灾难在 plan 阶段就可见。
状态管理:远程存储 + 锁
Terraform 用状态文件(state)记录"现实长什么样",每次 plan 对比期望与现实。本地 state 多人协作会冲突、易丢。必须远程存(S3/GCS)+ 加锁(DynamoDB)。
state 是 Terraform 的"记忆"。本地 state 误删会导致它以为资源不存在、下次 apply 重建一遍(可能覆盖线上)。务必远程存 + 加锁 + 备份,且绝不上 Git(含密钥)。
模块与变量:基础设施也能 DRY
把"一个标准 Web 服务"抽成模块,不同环境只传不同变量(实例数、域名),避免上百行重复 .tf。
配置漂移:现实偷偷偏离了代码
漂移指"生产实际状态和 Git/Terraform 里写的不一样"——通常因为有人手动改了控制台。后果:下次 apply 可能"纠正"掉你手动加的东西,引发事故。
论怎么消灭漂移
① 禁止控制台手动改(除真正紧急的事故)。② 定时 terraform plan,有差异就报警。③ GitOps 自愈:集群被手改后,控制器自动拉回 Git 声明态(呼应 ch1)。漂移的本质是"生产脱离了代码",治法就是"一切变更走代码"。
实战:设一个每晚跑的 terraform plan 流水线,把 diff 发到值班群。一旦出现非预期的"will be updated/destroyed",说明有人手改了,立刻有人跟进。
Ansible 适合什么:配置管理
Terraform 管"资源存在不存在"(建 VM/网络),Ansible 管"VM 内部装了什么、怎么配"(命令式+幂等)。两者常配合:Terraform 拉起机器,Ansible 进去装软件。
模块版本化与 Registry:基础设施也能发版
Terraform 模块写好后,别让各团队复制粘贴。把它发布到私有 Module Registry 并打版本,谁要用就 source = "app.terraform.io/org/vpc/aws/1.2.0"——基础设施和代码一样有版本、可审查、可回退。
论基础设施也要 DRY 与治理
没版本化的模块会被到处 fork,安全基线(加密、标签、日志)改一处要改十个仓库。收口成受治理的模块,安全与规范"一次做对、处处复用",也方便统一升级。
监控与可观测:Prometheus / Grafana / OTel
FDE 模块 08 提过三支柱。本页讲清它们怎么配合:指标告诉你"系统不健康"(红色告警),链路告诉你"哪次请求慢在哪",日志告诉你"当时发生了什么细节"。再加 OpenTelemetry 统一标准与 SLO/错误预算。
三支柱重新梳理
| 支柱 | 回答 | 工具 |
|---|---|---|
| Metrics | "现在正常吗?" | Prometheus / Grafana |
| Logs | "出了什么事?" | Loki / ELK |
| Traces | "慢在哪个环节?" | Jaeger / Tempo |
论三者怎么接力排障
告警(指标)让你知道"出事了" → 链路定位"是哪次请求、卡在哪个服务" → 日志查明"那个时刻到底打印了什么"。单靠任一类都难以闭环,三者互补才是完整可观测。
Prometheus 数据模型与 PromQL
Prometheus 拉取(pull)指标存为时间序列(带标签的度量),用 PromQL 查询。核心概念:Counter(只增,如请求数)、Gauge(可升降,如内存)、Histogram(分桶,算延迟分位)。
Grafana 看板:把数字变成直觉
Prometheus 存数据,Grafana 画图。一个好看板按"概览→下钻"组织:顶部是 SLO/错误率/QPS 总览,点进去看单服务、单接口。核心原则:看板服务于"出问题一眼看到",不是堆满图表炫技。
论看板过多的反效果
二三十个面板、每个颜色花哨,真出事反而找不到重点。黄金信号(延迟/流量/错误/饱和度,即 RED/USE 法)四五个面板足矣,其余放"下钻"页。
链路追踪与 OpenTelemetry
一个用户请求跨五六个服务,想知"卡在哪",靠日志得手动拼 traceId。链路追踪自动把一次请求在各服务的耗时串成一条瀑布。OpenTelemetry(OTel)用一套厂商无关标准采集,后端可接 Jaeger/Prometheus/商业 APM 任意一种。
论为什么需要 OTel 标准
过去每家 SDK、格式都不一样,换工具就得改代码。OTel 统一采集层,后端可任意替换——避免被单一厂商锁定,也统一了团队口径。新项目直接上 OTel,别再绑死某家 APM。
SLI / SLO / 错误预算
SLI 是可测的指标(如"成功请求占比");SLO 是给它定的目标(如"99.9% 成功");错误预算 = 允许的失败额度(0.1%)。预算烧光就冻结发布先止血。
| 概念 | 是什么 | 例子 |
|---|---|---|
| SLI | 可测量的健康指标 | 请求成功率、P99 延迟 |
| SLO | 给 SLI 定的目标 | 成功率 ≥ 99.9% |
| 错误预算 | 允许失败的空间 | 每月可失败 43 分钟 |
论错误预算驱动决策
预算充足 → 放心发新功能;预算快烧光 → 冻结发布、先止血。它把"可靠性"和"迭代速度"用同一个数字调和,避免研发要快、运维要稳的无休止扯皮。
告警该不该响
告警太多、一半是噪音,团队就会"红了也不看",真事故反而漏掉。原则:告警只对"需要人现在行动"的事触发(如 SLO 即将破、磁盘将满);纯信息用看板/日志,别打扰人。每条告警都要配"收到后干嘛"。
结构化日志:让机器也能查
日志别再一行行自由文本。结构化日志(JSON)把字段显式标出,集中采集后能用查询语言按字段过滤("查所有 order_id=100 且 level=error"),还能和 traceId 关联串起全链路。
论日志要带上下文,别只打"出错了"
排障时最怕"error: null pointer"——在哪、哪个用户、什么参数全没有。每条关键日志带上 order_id / trace_id / 关键入参,事后才能从一条告警顺藤摸到根因。
SRE 实践:告警疲劳、混沌、复盘与 on-call
SRE(站点可靠性工程)是把软件工程方法用到运维上。这一章讲告警疲劳与告警设计、混沌工程(故意制造故障)、事故复盘 Postmortem(对事不对人)、on-call 健康、以及消灭 toil(重复体力活)——这才是"可靠"的软实力。
告警疲劳与告警设计
告警是"请人来救火"的信号。一旦噪音多,人就会脱敏,真火也忽略。好告警三要素:可行动(收到知道干啥)、精确(少误报)、有优先级(P0 立刻、P3 上班再看)。
论"页面"和"工单"要分开
需要半夜爬起来的是 Page(P0/P1);可以明天处理的丢进工单系统,别进 on-call 手机。混在一起 = 制造告警疲劳。一条经验法则:能等 8 小时的,就不该半夜叫人。
混沌工程:故意制造故障
系统不逼自己一把,永远不知道哪里脆。混沌工程在受控条件下注入故障(杀 Pod、断网络、加延迟),验证"系统是否真能自愈、降级、不雪崩"。它不是"制造事故",而是"在实验室里安全暴露弱点"。
混沌实验要有假设、监控、一键停止,且从小范围开始(先预发、先 1% 流量)。目的是"在可控条件下暴露弱点",不是制造事故。没有回滚预案就别跑。
起步建议:先在预发环境、对一个非核心服务做"杀 Pod"这类低风险实验,确认告警和自愈按预期工作,再逐步上生产、上网络分区等高风险项。
事故复盘 Postmortem:对事不对人
出事后写复盘,目的不是追责,而是"这次为什么会发生、怎么让同类不再发生"。黄金准则:无指责(blameless)。追问到根因(用"五个为什么"),产出可执行的改进项。
论复盘模板长啥样
① 时间线(发生了什么、谁在什么时刻做了啥);② 影响(损失多少、影响哪些用户);③ 根因(不是"某人手滑",而是"缺少门禁让它能被手滑");④ 行动项(带负责人和 deadline,且能被验证)。复盘的价值全在行动项是否真的落地。
on-call 与轮班健康
on-call 是"轮到我值班、出事叫我"。不健康的值班会 burnout:半夜频繁叫、白天还正常搬砖、没人替。健康做法:轮班、保障接班后的休息、控制 Page 量、工程化降低呼叫。
论谁写代码谁 on-call
让"写这服务的人"参与值班,才能形成反馈闭环:你写的告警噪音大,第一个被吵醒的是你自己,于是你会去修告警、修可靠性。把运维甩给另一拨人,开发就永远不在乎可观测性。
消灭 toil:把重复体力活自动化
SRE 把 toil(手工、重复、无长期价值的运维活) 视为要消灭的对象:手动重启、手工扩缩、复制粘贴部署。能自动化的自动化,让人去做"提升系统本身可靠性"的工程。
不是所有手工都值得自动化——一年只做一次、做一次五分钟的事,写自动化脚本可能花三天还不维护。优先自动化"高频 + 易错 + 半夜发生"的那类 toil。
SRE 的闭环:从事故到更好
把前面几章串起来:可观测(ch5)让你早发现问题 → 告警(ch6)叫对人 → 复盘找到根因 → 用 IaC/GitOps(ch1/4)把修复固化进代码防复发 → 用混沌(ch6)主动验证修复有效。这就是"可靠"不是运气,而是一条持续改进的引擎。
可靠性成熟度:你处在哪一级
团队可靠性能力是分阶段的。对照一下,知道下一步往哪发力。
| 级别 | 特征 |
|---|---|
| 1 救火 | 出事靠人 SSH 上去查,无监控 |
| 2 可观测 | 有指标/日志/链路,能定位 |
| 3 可自愈 | 探针 + HPA + 自动回滚,少人工 |
| 4 抗脆弱 | 混沌演练 + 错误预算 + SLO 驱动决策 |
论别想一步到 4 级
先有监控(2 级),再做自动恢复(3 级),最后才谈混沌与 SLO 治理(4 级)。每级都建立在下级之上,跳级只会造出"看起来很高级但一碰就碎"的假可靠。
DevOps/SRE 深化把"能部署"升级为"可靠交付":流水线分段(构建→测试→制品→部署→验证)且失败即截断,用不可变版本(SHA 而非 latest)+ 质量门禁,靠蓝绿/金丝雀让发布可回滚,靠 GitOps 拉式同步实现可审计、可回退 commit。容器要懂分层缓存、多阶段构建砍体积、固定版本+非 root+扫描保安全;K8s 核心是 Pod/Deployment/Service/Ingress、HPA 自动扩缩、PVC/ConfigMap/Secret 管状态、就绪/存活探针保零停机,警惕资源 limit 与 Secret 明文。IaC 用声明式、Terraform 先 plan 后 apply、state 远程加锁防漂移,Ansible 管配置;可观测靠指标/日志/链路三支柱+OpenTelemetry 统一标准、SLO/错误预算平衡快慢;SRE 用精确告警抗疲劳、混沌工程主动暴露弱点、无指责 Postmortem 闭环、健康 on-call、消灭 toil。
1.指标、日志、链路分别回答什么问题?为什么需要三者配合?
查看答案
指标答"是否健康"(红不红),链路答"慢在哪"(定位请求路径),日志答"发生了什么细节"。告警让你知道出事,链路定位范围,日志查明原因——单靠任一类都难以闭环排障。
2.错误预算(Error Budget)解决了什么矛盾?怎么用它做决策?
查看答案
它把"可靠性"和"迭代速度"统一成一个数字:预算够就多发布,预算快透支就冻结发布先止血。避免研发要快、运维要稳的无休止扯皮。
3.GitOps 相比传统 CI/CD 的关键区别?为什么能抗配置漂移?
查看答案
传统是 push(人/流水线把变更推到集群);GitOps 以 Git 为唯一事实源,控制器持续 pull 并维持集群状态(selfHeal)。被手改后自动拉回 Git 声明态,变更可审计、回退 commit 即回滚,自然消灭漂移。
4.Terraform 的 state 为什么必须远程存 + 加锁?丢了会怎样?
查看答案
state 是 Terraform 记录"现实长啥样"的记忆。本地 state 多人协作会冲突、易误删;丢了它会以为资源不存在,下次 apply 重建一遍(可能覆盖线上库)。所以必须远程存(S3 等)+ 加锁(DynamoDB)+ 备份,且绝不上 Git。
5.混沌工程是不是"制造事故"?做它要注意什么?
查看答案
不是。它在受控条件下(有假设、有监控、可一键停止、从小范围起)注入故障,验证系统能否自愈/降级,目的是"安全暴露弱点"而非搞破坏。没有回滚预案、直接在生产全量跑,那是真制造事故。
下一步往哪走
路学完 DevOps 深化之后
① 回 FDE 模块 02/08:那里是"怎么做"(Docker/K8s/Prometheus),本页是"为什么这样设计"。
② 串系统设计:限流/熔断/容量规划在本页是 SRE 视角,在 tech-systemdesign 是架构视角,互为表里。
③ 动手:搭一套 Prometheus+Grafana+Jaeger(OTel),给自己的服务接上三支柱,亲手做次金丝雀发布与一次无指责复盘。
分支策略:Git Flow 与 Trunk-Based
分支怎么切,直接决定团队合并痛不痛、上线快不快。没有"最好"的模型,只有最适合你发布节奏的那一个。
三种主流模型
Git Flow 适合有明确发布节奏、多版本并行维护的团队;Trunk-Based 适合高频持续交付、CI/CD 成熟的团队;GitHub Flow 是 Trunk-Based 的轻量变体。
论核心差异
Git Flow:长期分支 master(正式)/develop(集成分支)+ 短期 feature/release/hotfix,缺点是分支多、合并重、反馈慢。Trunk-Based(主干开发):开发者从主干拉极短命分支(数小时~1天)或直接提交,频繁合回主干,主干始终可发布;依赖强 CI、特性开关(feature toggle)、小步提交。GitHub Flow:只有 main + 短 PR,合并即部署。
| 维度 | 说明 |
|---|---|
| 发布频率 | Git Flow 低(按版本);Trunk-Based 高(每日多次) |
| 合并冲突 | 长分支越多冲突越频繁;主干开发短分支冲突少 |
| CI 要求 | Trunk-Based 要求强 CI 守住主干;Git Flow 相对宽松 |
| 适合团队 | Git Flow 多版本并行;Trunk-Based 高频交付/CI 成熟 |
论特性开关
未完成功能用 toggle 藏起来,避免长分支;与「分支即环境」互补。hotfix 在 Git Flow 走 hotfix 分支,Trunk-Based 直接主干提交再 cherry-pick 到发布标签。
长命特性分支是 Trunk-Based 的反模式——它是合并冲突和集成地狱的根源。别把"按功能开长分支"当成主干开发的做法。
Q1. 什么团队适合 Trunk-Based?
参考答案
CI 成熟、小步提交、有特性开关、发布频繁(每日多次)的团队。
动手片段
下面这段可直接运行,把抽象概念变成"手感"。