题干与适用场景
团队希望把新版本先发布给一小部分真实请求,再逐步扩大流量。服务需要配置流量权重、观察 Canary 与稳定版本的差异,并在指标恶化时自动停止或回滚。请设计控制面与数据面,明确 SLO、指标窗口、状态持久化、权限、幂等和控制器失联时的行为。
这道题适合系统设计、平台工程和 SRE 岗位。核心考察是如何把“安全发布”变成可恢复的状态机,而不是只罗列 Kubernetes、服务网格和监控名词。回答应能解释流量路由、分析任务、决策阈值、人工介入和旧版本兼容。
面试官考察点
强回答会先定义 Canary 的暴露范围、持续时间和回滚目标,再把 Rollout、路由、指标查询与决策器解耦。它会区分“成功”“失败”“数据不足”三种结果,避免把指标缺失误判为通过;会用持久化状态和幂等操作恢复控制器重启;会说明分析窗口必须匹配发布阶段,如何防止小样本噪声、指标延迟和回滚风暴。
回答前需要澄清的问题
- 目标是单服务、跨服务编排,还是只控制 Kubernetes 工作负载?
- 流量按随机请求、用户稳定哈希、地区还是租户分割?是否必须保持用户粘性?
- 关键 SLO 是错误率、延迟、业务转化还是资源成本,稳定基线如何选?
- 每个阶段允许多久、失败几次触发回滚,分析不确定时是否暂停等待人工?
- 数据库 schema、消息格式和外部 API 是否向后兼容,回滚旧版本是否安全?
30 秒回答框架
“我会把服务拆成 Rollout 控制器、流量路由适配器、指标分析器和持久化状态库。每个发布都有稳定版本与候选版本、分阶段权重、分析窗口和明确的成功/失败/不确定条件;控制器只在分析成功时扩大流量,失败时把权重降回稳定版本,不确定时暂停并告警。所有命令带版本号和幂等键,状态写入后才能执行下一步。路由和指标系统短暂不可用时保持最后安全权重,恢复后从持久化状态继续。”
分步骤深入解答
第一步:定义目标和数量级
假设一个区域每天 200 次发布、每次最多持续 40 分钟,控制器需要处理每秒几十个状态与指标事件;真正的业务请求仍由现有网关和服务承载。这个估算决定控制面可以采用单个高可用控制器加队列,而不需要为每个请求创建工作流。
安全目标是限制爆炸半径:阶段权重例如 1% → 5% → 25% → 50% → 100%,只是可配置示例,不是普适阈值。每一步要有最短观察时间和最大等待时间,防止指标永远不够而占用发布槽位。
第二步:拆分控制面与数据面
控制面保存发布规格、版本摘要、当前阶段、目标权重、分析结果和操作者。数据面由网关或服务网格执行路由,把请求送到 stable 或 canary ReplicaSet。Argo Rollouts 的架构文档将 Rollout、两个版本的 ReplicaSet、Service/Ingress 与 AnalysisTemplate/AnalysisRun 分开,说明这些边界可以独立演进。
指标适配器只负责把查询发送到 Prometheus 等提供者并返回带时间范围的观测值;决策器根据策略计算结果,不直接修改路由。这样更换指标后端不会改变发布状态机。
第三步:设计可恢复的状态机
可以使用 Draft → Running → Paused → Promoting → Succeeded,并从 Running 或 Paused 转到 Aborting → RolledBack。每次状态转移带 rollout_id、期望版本、阶段序号和幂等键;数据库唯一约束或 compare-and-set 防止两个控制器同时推进。
Running + analysis=success -> Promoting(next_weight)
Running + analysis=failure -> Aborting(weight=0)
Running + analysis=inconclusive -> Paused(reason=insufficient_signal)
Paused + operator=resume -> Running
Aborting + route=stable -> RolledBack控制器重启后从最后一次已提交的状态重放未完成动作。路由更新和状态写入无法形成跨系统原子事务,因此动作必须可重复:设置同一权重多次不会产生额外副作用,且每次读取实际路由后再决定下一步。
第四步:选择指标与统计窗口
每个阶段至少选一个可靠性指标和一个业务指标,例如 Canary 与 stable 的错误率差、P95 延迟差、关键请求成功率。比较时固定查询窗口、分母和过滤条件,避免把 Canary 的重试流量与 stable 的原始请求混为一谈。
分析窗口要覆盖指标采集延迟,并且不应长于阶段等待上限。Google SRE 的 Canary 指南指出,评价过程要把 Canary 与 control 对照,并提醒指标计算周期过长会让短阶段的信号变得模糊。小样本时将结果标记为不确定,暂停比贸然放量更安全。
第五步:处理流量分割和用户粘性
随机按请求分割适合无状态接口;需要一致体验时按用户或租户稳定哈希,并记录路由版本。按百分比路由时要处理新版本没有健康实例、权重未生效、跨区域不一致和缓存键混用。
路由适配器应返回实际生效的权重和版本摘要。控制器发现期望值与实际值不一致时暂停推进并告警,不能假设 API 成功就代表流量已经切换。
第六步:设计回滚、暂停和人工介入
失败动作先停止扩大 Canary,再把新流量降到零或安全权重;旧版本必须仍可运行,数据库迁移要采用向后兼容的 expand/contract 顺序。回滚本身也要有超时和重试上限,避免路由系统故障时无限重试。
分析结果为 Inconclusive 时暂停并保留现场:查询、样本数、版本、窗口和阈值都应可审计。Argo Rollouts 文档把 Inconclusive 作为暂停状态,交给人工判断继续或中止,这比把缺失数据当作成功更可控。
第七步:可靠性、权限和审计
控制器需要 leader election 或租约,队列事件至少一次投递,消费者用幂等键去重。发布规格、策略变更和人工批准写入不可变审计日志。只有发布 owner 能改变权重,指标读取凭证放在密钥管理系统,回滚权限与普通发布权限分离。
监控控制面 SLO:状态推进延迟、卡住发布数量、回滚耗时、路由实际权重偏差、分析查询失败率。控制面自身异常时,路由保持最后安全状态,并由值班人员接管,而不是自动把 Canary 放大到 100%。
第八步:验证和容量测试
用故障注入验证错误率升高、指标无数据、指标延迟、路由 API 超时、控制器重启、重复消息和数据库 schema 不兼容。检查每种故障的最终状态和告警,而不只看 happy path。
回放历史发布记录,测量分析查询成本和队列积压;对控制器做 N+1 实例故障测试。可把一百个并行发布作为压测上限,观察状态库锁冲突、指标提供者 QPS 和路由更新速率,再调整阶段并发上限。
设计取舍与边界
自动决策减少人工延迟,但阈值错误会把噪声放大成回滚或把真实回归当成通过。简单的绝对错误率阈值易解释,适合低流量服务;稳定版本对照或分层基线更能抵御全天流量变化,却需要更复杂的统计与样本对齐。高风险服务可以要求自动分析通过后再人工批准。
蓝绿发布提供快速切换和简单回滚,但通常需要双份容量;Canary 节省暴露面,却要维护流量分割和分析窗口。Feature flag 能独立控制功能开关,但不能替代二进制、依赖或 schema 的兼容性检查。选择应由回滚成本、流量形态和 SLO 风险决定。
落地计划与证据
先为单个无状态服务实现只读分析和手动暂停,确认 stable/canary 标签、指标分组和审计字段正确;再开放自动回滚,最后增加多区域、业务指标和并行发布限制。每个阶段保留人工 kill switch 和明确 owner。
Google SRE 将 Canary 定义为限时、部分流量的部署与评估,并要求把评估接入发布流程;Argo Rollouts 则提供 AnalysisTemplate/AnalysisRun、指标阈值以及成功、失败、不确定状态。公开系统设计面试材料也把流量分割、guardrail 评估和自动回滚作为 Canary 设计要点。本题据此聚焦控制面状态机和可恢复边界,而非泛泛比较部署名词。
常见误区与追问
只说“按 10% 流量观察”
没有说明用户分组、观察多久、指标分母和失败动作,无法证明系统安全。补充稳定基线、窗口、阈值、暂停和回滚路径。
把指标无数据当作通过
采集器故障会伪造健康信号。将无数据、NaN、延迟和样本不足标记为不确定,暂停并告警。
只回滚 Deployment,不处理路由
旧 Pod 恢复不代表请求已经离开 Canary。回滚必须核对实际权重、服务选择器和缓存/会话粘性。
数据库迁移不可逆
旧版本可能无法读取新 schema,路由切回也救不了请求。采用向后兼容的 expand/contract,并把迁移状态纳入发布门槛。
如果指标提供者停机怎么办?
保持最后安全权重,停止自动放量,记录未完成分析并通知 owner。恢复后从持久化阶段重跑,不能用默认“通过”填补空白。