路线图

面试 · 方案设计型

星辉 2026-07-02 阅读 18 min 3,804 字 路线图
面试 · 方案设计型 封面

系统性取舍(高级岗)。每题给选型对比 + 设计要点 + 注意事项。考察系统性思维、取舍意识、生产经验。


子类 1:CI/CD 架构设计#

题目1:给 20 人团队从零设计 CI/CD + 多环境配置管理#

设计需求: 20 人技术团队,10 个微服务,AWS 单云,已有 EKS 集群。需要从零设计 CI/CD 体系,包含代码托管、CI 构建、制品管理、多环境部署(QA/PRE/PROD)。团队规模预计 1 年内增长到 50 人。

方案设计:

选型决策:

  • 代码托管:GitHub(海外)或 Codeup(国内)
  • CI:GitHub Actions(海外)或 云效 Flow(国内)
  • 镜像仓库:AWS ECR
  • GitOps:ArgoCD 2.13+(开发同学需要 UI 看部署状态)
  • 配置语言:Kustomize(业务服务)+ Helm(第三方组件)
  • 部署模式:Pull-based GitOps(CI 不碰集群)

五层架构:

text
①代码层 → ②CI层 → ③制品层 → ④GitOps层 → ⑤运行时层
GitHub    Actions  ECR      ArgoCD      EKS

关键设计点:

  1. GitOps 仓库目录结构(目录即配置):

    text
    gitops/
    ├── argocd/applicationsets/     # ApplicationSet 定义
    ├── base/{project}/{service}/   # 共享模板(deployment/svc/pdb)
    ├── clusters/{env}/applications/{project}/{service}/  # overlay
    ├── scripts/deploy.py           # CI 与 GitOps 的胶水
    └── dockerfiles/{project}/{service}.Dockerfile
  2. 镜像 tag 策略:{commit短hash}-{YYYY-MM-DD-HH-MM-SS},全环境复用同一镜像(一次构建多环境晋升)。绝不用 latest(不可溯源、不可回滚)。

  3. deploy.py 是 CI 与 GitOps 唯一通路:CI 构建完镜像后调 deploy.py → 改 GitOps overlay 的 newTag → git push → ArgoCD 自动 sync。CI 不直接连 K8s API。

  4. 多环境晋升流程:QA(自动部署)→ schema check(pre warning + post fail)→ PRE(人工审批)→ PROD(人工审批 + 灰度)。

  5. ApplicationSet Matrix Generator:clusters × git directories 笛卡尔积,新服务建目录即被纳管,不需要手建 Application。

  6. 分支策略:GitHub Flow(feature → MR → main,main 永远可发)+ main 分支保护(禁直推,强制 MR)。

  7. 4 套标准模板覆盖 80% 场景:

    • unified-template:US+CN 不灰度
    • unified-canary-template:US+CN 需灰度
    • us-only-template:仅 US
    • cn-only-template:仅 CN

注意事项:

  • GitOps 闭环完整性:ApplicationSet 必须被 root Application 自管理(app-of-apps 模式),否则改 ApplicationSet 要手动 kubectl apply。
  • 多 ArgoCD 凭据路由:如果有多 ArgoCD 实例(历史遗留),deploy.py 要维护显式的服务到 ArgoCD 映射,不要按 region 推断。
  • CN 构建机特殊坑:Dockerfile 禁 # syntax=docker/dockerfile:1 和 --mount=type=cache(访问不了 Docker Hub),基础镜像用国内 mirror。
  • Schema Check 双 Stage:pre 部署前 warning + post 部署后 fail,把破坏性 DDL 关在 PROD 之外。
  • 回滚 = git revert:GitOps 声明式,ArgoCD 自动收敛回旧 tag,不需要重新跑流水线。
  • 上线节奏:分 4 阶段(奠基 1-3 周 → CI 闭环 4-6 周 → 扩面+防护 7-12 周 → 收尾+治理 13-26 周),不要一刀切上线。

题目2:设计 3 个标准模板覆盖 80% 服务#

设计需求: 10 个微服务,需要设计标准化的 CI/CD 模板,让新服务接入从 1 天降到 1 小时。

方案设计:

3 套模板设计:

模板命名规则适用场景Stage 数
standard-template{PROJECT}-{SERVICE}标准服务,单云单环境6
canary-template{PROJECT}-{SERVICE}核心服务,需要灰度8
infra-templateinfra-{SERVICE}基础设施服务,无审批4

standard-template 流程:

text
构建 → QA(auto) → schema-check-pre(warning) → 审批 → PRE(manual)
→ schema-check-post(fail) → 审批 → PROD(manual)

关键设计点:

  1. 命名即配置:流水线名被 step_parse 用 cut -d'-' 拆成变量。us-goalfymax-relay → REGION=us, PROJECT=goalfymax, SERVICE=relay。命名是强约束,不能随意改。

  2. YAML Anchors 去重:&deploy-runsOn(构建机池)、&fetch-script(拉 deploy.py)、&ding-success/&ding-fail(钉钉通知)、&validators(审批人)——这些重复片段用 anchor 引用,改一处全模板生效。

  3. 变量组分层:

    • service_common_env_group(通用):REGISTRY/REGION/AK/SK/钉钉
    • us_env_group(US 线):US 特有配置
    • cn_env_group(CN 线):CN 特有配置
    • db_schema_check_group:schema 检查相关
  4. 审批门(OR 门):核心 QA 3 人 / PRE·Canary 2 人 / US 标准 2 人 / CN 标准 1 人。任一人过即可(OR 门),避免单点阻塞。

  5. 钉钉通知:成功/失败分别推不同模板,@触发人(用 ${DING_AT_MOBILES} 变量组,不要写 ${DINGTALK} 未定义)。

注意事项:

  • 模板化的隐性收益:① 流水线变更可批量推(改模板下次新建自动用新值);② review 标准变明确(和模板比多了什么少了什么);③ 故障复盘归因变快(是模板设计问题还是服务特殊性问题)。
  • 不要为了 100% 覆盖强行模板化:20% 特殊服务走自定义路径并标记"长期例外"的原则同 1-概念·子类1·题目5,此处不赘。
  • 模板版本管理:模板入 Git 仓库 gitops/pipeline-templates/,变更走 PR Review。
  • 云效 Flow 特殊坑:stage 间永远线性(要并行双线必须同 stage 多 job);branchesFilter 必须字符串;cloneDepth 必须字符串 "0";step 级 envs: 字段被忽略,环境变量必须在 run: 里 export。

题目3:设计 PR 隔离环境#

设计需求: 5-50 人后端团队,并行 PR 常态 ≥3 个,需要每个 PR 有独立验证环境,不互相覆盖。

方案设计:

选型对比:

  • 方案 A(共享 QA):淘汰——互相覆盖、数据污染、排队
  • 方案 B(每 PR 独立域名+证书+Ingress):淘汰——域名/证书运维负担重、清理麻烦
  • 方案 C(PR Pod + X-env header 路由 + 自动清理):推荐

方案 C 架构:

text
开发者 → push feat 分支
  ↓
CI 构建 + deploy.py 渲染 PR overlay → git push
  ↓
ArgoCD ApplicationSet 扫到新目录 → 自动 sync
  ↓
PR Pod Ready → 机器人评论"环境就绪"
  ↓
开发者浏览器装 ModHeader 加 X-env: pr-{env_id}
  ↓
入口 HTTPRoute 按 header 路由到 PR Pod
  ↓
PR 关闭 → webhook 触发清理 → ArgoCD GC

关键设计点:

  1. 路由层不引入新域名:入口 Gateway 上挂 HTTPRoute,header X-env=pr-{env_id} 命中时打到 PR Service,否则走 base Service。每多一个 PR = 一个 HTTPRoute 资源(Kustomize patch 自动生成),不是一套域名/证书/Ingress。

  2. 同 namespace + namePrefix:PR Pod 用 pr-{env_id}- 前缀,复用 base 的 ServiceAccount/Secret/ConfigMap。不为每个 PR 拷贝这些资源。

  3. ApplicationSet Matrix Generator:监听 clusters/us-qa/applications/*-pr/* 目录,新目录出现自动生成 Application。

  4. 三层清理保障:

    • 触发 1:PR 关闭 webhook → Lambda 删 overlay
    • 触发 2:CronJob 每天扫 overlay 时间戳 + 远程分支存在性(TTL 14 天)
    • 触发 3:容量水位(PR Pod 数 >30 时按 LRU 清理)
  5. 数据库隔离(方案 A:共享 DB + env_tag 字段隔离):

    • 应用层中间件注入 env_tag = PR_ENV_ID
    • DB trigger 兜底(env_tag 为空时 SIGNAL ERROR)
    • 读路径默认过滤 env_tag = PR_ENV_ID OR env_tag = 'main'
  6. v2 升级(baggage 路由派):W3C baggage 跨 hop 透传 + OTel propagator 注入 + 下游无副本自动回 baseline。比克隆派省 85-90% 资源。

注意事项:

  • includeSelectors 不够:base Service 的 selector 会反向选 PR Pod → 必须把 deployment selector + pod label + service selector 全部替换成 pr-{env_id}-{base_app}。
  • CloudFront 默认不转发自定义 header:要配自定义 cache policy 把 X-env 加入 whitelist(参与缓存键 + 向源转发),否则 PR 流量被缓存命中 base Pod。
  • 异步消费者类服务不直接开 PR 隔离:PR Pod 和 base 共享 consumer group → 按概率截走消息用旧代码处理。要先做 per-PR consumer group 前缀 + backend 透传 X-env。
  • env_id 命名规则:分支名 slug 化(小写、只保留 a-z0-9-)→ 截断到 40 字符(K8s label value 最大 63 字符,留余量给 pr- 前缀和服务名)。
  • feature 分支多次 push 用同 image tag 不会更新:tag 必须含 git sha + 时间戳,不要用分支名当 tag。
  • 不建独立环境的克制:拓扑隔离解决不了数据隔离(DB/Kafka 复制不了),独立环境给假安全感;基于数据(事故频次)决策。

题目4:设计渐进式发布体系#

设计需求: 核心服务需要从"全量发布"升级到"渐进式交付"——只放一部分流量到新版、用指标自动判断好坏、好就逐步放量、坏就自动回滚。

方案设计:

5 大灰度策略选型:

  • 蓝绿 Blue-Green:双倍资源,秒级切流,适合要求干净切换
  • 金丝雀 Canary:低资源,细粒度可控,渐进式交付主力
  • 滚动 Rolling:K8s 默认,无额外资源,低风险变更
  • 影子 Shadow:镜像流量不返回,零用户风险,高风险变更先验
  • 特性开关 Feature Flag:与部署解耦,秒级熔断,A/B 测试

推荐组合:金丝雀(Argo Rollouts)+ 特性开关(LaunchDarkly/Unleash)

架构设计:

text
git push 新镜像
  ↓
Argo Rollouts Controller
  ├── ① 按 Rollout.steps 调整流量权重(改 Istio VS)
  │     setWeight 5% → pause 5m → analysis → setWeight 25% → ...
  ├── ② 到 analysis 步 → 起 AnalysisRun
  │     query Prometheus 对比 canary vs baseline
  │     ├── 成功率 ≥ 99%
  │     ├── P99 延迟 ≤ 500ms
  │     └── 错误率 ≤ baseline + 阈值
  ├── ③ 达标 → 下一步放量
  └── ④ 劣化 → setWeight 0 自动回滚(秒级切回 baseline)

关键设计点:

  1. 金丝雀分析(Canary Analysis)的 5 个门控:

    • 足够样本:低于最小流量阈值豁免分析(样本不足不下结论)
    • 相对比较:canary vs baseline 同时段对比(不是 vs 历史)
    • 容忍带:成功率 ≥ baseline-1% / 延迟 ≤ baseline×1.1(不要求完全一致)
    • 连续确认:连续 N 次分析通过才晋级(单次抖动不算数)
    • 冷启动豁免:canary 刚起 JIT/缓存未热,前 M 分钟不计
  2. 染色与路由:

    • 按身份(cookie/header 白名单)→ 内部人员先试
    • 按比例(权重分桶)→ 5%→25%→50%→100% 渐进放量
    • 跨 hop 透传:W3C baggage + OTel propagator
  3. 自动回滚触发:

    • 金丝雀分析失败(连续 N 次指标不达标)
    • SLO 错误预算烧速超门槛(canary 流量段 1h 烧 2% 月预算)
    • 关键指标劣化
  4. 特性开关与部署解耦:

    • 部署 = 把带新能力的代码发上去(开关默认关)
    • 放量 = 改开关(不重新部署)
    • 熔断 = 关开关(秒级)
  5. 成熟度演进路线:

    • L1 手动灰度(现状常见):手动放量 + 人盯 Grafana + 手动回滚
    • L2 半自动:指标看板自动化 + 金丝雀分析(人看结果手动晋级)
    • L3 全自动(目标):Argo Rollouts/Flagger + canary analysis 自动晋级 + SLO 门控自动回滚 + 特性开关解耦

注意事项:

  • 状态层不灰度:DB/Kafka/Redis 共享,灰度限无状态行为差异;schema 变更走 schema_check + 向前兼容。
  • mesh 不透传 baggage 跨 hop:app 调下游要 OTel propagator 注入,漏接服务 → 半 canary 半 baseline;Kafka 显式 inject/extract、Redis key 前缀。
  • 缓存隔离:灰度两版若共用 CDN 缓存会串味,cache key 要带染色字段。
  • CloudFront 权重传播非瞬时:改权重秒级~数十秒、不同边缘 PoP 不同步;硬回滚靠 function 内置 DISABLED 常量(改函数 publish),不能只指望 KVS 归 0。
  • 不要停在 L1 手动灰度:纯手动放量 + 靠人盯指标,没有指标驱动的自动晋级/回滚,MTTR 受人响应速度限制。

子类 2:可观测性体系设计#

题目1:设计 Prometheus + Grafana 监控架构#

设计需求: 100 个微服务,2 个 K8s 集群(US + CN),需要设计可观测性监控架构。

方案设计:

三层架构:

text
①采集层:exporter + ServiceMonitor
  ├── node-exporter(节点指标)
  ├── kube-state-metrics(K8s 对象指标)
  ├── YACE(AWS CloudWatch 指标拉进 Prometheus)
  ├── blackbox-exporter(外部可达性探测)
  └── 业务 /metrics(应用自定义指标)

②存储层:Prometheus + 远端存储
  ├── 每集群一个 Prometheus(replicas 2, retention 15d)
  ├── remote_write 到中央 VictoriaMetrics/Mimir(长期存储)
  └── externalLabels 标记 cluster/region/env

③展示层:Grafana + Alertmanager
  ├── Grafana 统一看板(跨集群查询)
  └── Alertmanager 统一告警路由

关键设计点:

  1. 每集群一个 Prometheus:不要一个 Prometheus 管所有集群(网络打通难 + 单点压力)。每集群本地 Prometheus 抓取,通过 remote_write 发到中央存储。

  2. Prometheus CR 关键配置:

    yaml
    spec:
      replicas: 2                    # HA
      scrapeInterval: 30s
      evaluationInterval: 30s
      retention: 15d
      retentionSize: 45GB
      serviceMonitorSelector: {}     # 空 = 全收(不靠 release 标签)
      ruleSelector: {}               # 空 = 任何 PrometheusRule 都加载
      externalLabels: {cluster: us-prod, env: production}

    注意:ruleSelector: {} 空 = 全收。如果配了 matchLabels: {release: monitoring},PrometheusRule 的 metadata.labels 必须匹配否则不加载(静默失效)。

  3. YACE 把 AWS 指标拉进 Prometheus:CloudWatch 的 RDS/ElastiCache/ACM 等指标通过 YACE 转 Prometheus 格式。注意:① 资源必须有 tag 才被发现;② metric 名转义诡异(ElastiCacheProcessingUnits→aws_elasticache_elasti_cache_processing_units_sum),写规则前先 curl yace:5000/metrics | grep ^aws_ 确认;③ dimension 大小写敏感 + 用 {dimension_X!=""} 过滤 global 聚合行。

  4. Alertmanager 统一告警路由:

    yaml
    route:
      receiver: webhook-dingtalk-observe
      group_by: [alertname, namespace, severity]
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 30m
      routes:
        - match: { alertname: Watchdog }
          receiver: dead-mans-switch    # 心跳走独立通道
        - match: { severity: P0 }
          receiver: dingtalk-p0
  5. Watchdog 心跳兜底:一条 alert: Watchdog expr: vector(1) 永远 firing,配独立 receiver 推 healthchecks.io。30s 收不到心跳 → 反向告警到独立钉钉群(不能与主告警同群,否则主链路挂了反向也挂)。

  6. "持久化资源必建告警"纪律:新建 PVC/RDS/EBS 的同一 PR/操作必须包含 PrometheusRule,否则会"满了 N 天没人发现"(Loki filesystem PVC 撑满 CrashLoop 20 天无人知是典型案例,详见 3-原理·Loki 写入路径题)。

注意事项:

  • 高 cardinality 是 Prometheus OOM 最常见原因:不要把 user_id/request_id 当 label,会导致 index 爆炸。
  • ruleSelector 不匹配是静默失效:PrometheusRule 的 metadata.labels 必须匹配 Prometheus.spec.ruleSelector,否则规则不加载且不报错。必须从 /api/v1/rules 接口验证规则真的被加载。
  • configmap 热更新延迟:改 webhook-dingtalk 模板后 /-/reload 只重载 config.yml,模板文件要 rollout restart。
  • resolved 通知黑洞:钉钉关键词白名单要同时覆盖 firing + resolved("告警/运维/恢复"),否则 firing 能发但 resolved 被拒(原理详见 3-原理·Alertmanager 路由题)。
  • Loki 用对象存储不要用 filesystem:filesystem 后端 PVC 会满,生产用 S3/OSS + retention 自动清理。

题目2:设计多云告警统一治理体系#

设计需求: 现有 5 套告警系统并行(US-Prom / sandbox-Prom / CN-Prom / 阿里云 CMS / 业务侧 SQL 轮询),200 条规则散落,告警直接捕获率仅 13%。需要统一治理。

方案设计:

选型对比:

  • 方案 A(维持现状):淘汰——故障频率到一定程度后修补速度赶不上规则腐化速度
  • 方案 B(全量迁到一套):淘汰——业务侧 103 条规则中 30+ 条是基于业务表的复合 SQL,改写成 PromQL 需要业务 emit metric,开发改造成本不可控
  • 方案 C(保留两套但治理 + 互通):推荐——不强求技术统一,只要求"出口统一、规则不重叠、定期对账"

方案 C 架构:

text
①基础设施/时序指标 → Prometheus → Alertmanager
②业务复合判定 → 业务侧 SQL 引擎 → webhook bridge → Alertmanager
③云资源 → YACE → Prometheus → Alertmanager
                                          ↓
                        统一 Alertmanager(route/inhibit/silence)
                                          ↓
                        prometheus-webhook-dingtalk → 钉钉群
                                          ↓
                        Watchdog → healthchecks.io → 反向告警独立群

关键设计点:

  1. Alertmanager 是唯一收敛点:业务侧通过 webhook bridge(POST 到 Alertmanager API v2 /api/v2/alerts)注入。silence/inhibit/route/模板渲染只有一处。

  2. 渠道梳理 + 禁止复用历史 Lambda:扫所有 SNS topic + Lambda,发现硬编码业务名的 Lambda(如 title = "RabbitMQ 告警: ...")立即记入"禁止复用清单"。新增资源告警走独立 SNS + 独立 Lambda,绝不复用历史 Lambda。

  3. silence 审计与负匹配规范:

    • 周巡检 CronJob 扫长期 silence(>7 天)+ 永久 silence + .+ 全匹配
    • 所有"全部 silence"必须用负匹配排除 Watchdog:alertname!=Watchdog
    • Watchdog 被误杀检测脚本退出码 2 = 立即告警
  4. 规则归档 + 对账:

    • 所有 PrometheusRule 入 GitOps(clusters/{env}/monitoring/)
    • 对账脚本:git yaml 规则数 vs Prometheus API 加载的规则数,不匹配报 DRIFT
    • 每周巡检必须含"规则总数 vs 集群规则总数对账"
  5. 业务侧 webhook bridge:业务侧 SQL 轮询不动业务代码,让它 POST 到 sidecar,sidecar 翻译成 Alertmanager API v2 格式。endsAt = now + 5min 利用 Alertmanager 的"超时自动 resolved"机制——业务侧每分钟轮询,停止续推 5 分钟后自动 resolved。

注意事项:

  • 告警体系是有腐化速度的活物:规则只增不减是默认状态、silence 是写完就忘的状态、Lambda/SNS 这种"不在 GitOps 里"的资源最容易腐化。半年没治理就是失控。
  • 必须主动制造一次 firing 验证全链路:上线告警不能只看 kubectl get prometheusrule,必须 curl 测 firing + resolved 两类消息都真能推到目标群。
  • 老 Lambda 退役要做并行对账 14 天:把老 Lambda 接过的所有 alarm 列出来,确认每条都已通过 YACE 在 Prom 侧覆盖,并行 14 天看两边告警次数是否吻合,吻合后再下线。
  • 统一告警网关:在钉钉前加聚合/去重/路由层,或把业务侧引擎也经 Alertmanager,避免"同一故障三套系统各发一条"。

题目3:设计 SLO + 告警体系#

设计需求: 把告警范式从"症状阈值"(CPU > 80%)升级到"用户旅程 SLO + 错误预算烧速"。

方案设计:

监控对象多类目体系(关键纠错:不是所有监控都叫 CUJ):

text
├── 1. CUJ-based SLO     ← 用户旅程,SLO + burn rate(允许错误预算)
├── 2. Invariant 监控    ← 系统必须保持的属性,零容忍异常告警(不积预算)
├── 3. 运维 SOP 健康度   ← 运维动作成功率,不进 SLO 看板
├── 4. 横向技术依赖      ← 跨 CUJ 共用基础设施,RED 信号 + inhibit
├── 5. 黑盒探测         ← synthetic monitor,兜底
└── 故障模式参考库       ← 不独立监控,是 runbook 排查项

关键区别:CUJ 失败 → 用户感知 → 允许累积错误预算 → burn rate 告警;Invariant 违反(数据泄漏/计费错)→ 系统属性破缺 → 零容忍立刻告警。

SLO 标准设计:

  1. 定义用户旅程(14 个真 CUJ 示例):

    • CUJ-1 登录成功率
    • CUJ-2 对话完成率
    • CUJ-3 沙箱拉起成功率
  2. SLI 公式(good events / total events):

    promql
    # SLI-1 登录成功率
    sum(rate(goalfymax_sso_callback_total{status="success"}[5m]))
    / sum(rate(goalfymax_sso_callback_total[5m]))
  3. SLO 目标 + 错误预算:

    • SLO 99.5%/30d → 错误预算 = (1-99.5%)×30d = 允许 3.6h 不可用/月
    • 把"还能坏多久"变成可量化的预算
  4. 多窗口烧速告警(multi-window multi-burn):

    级别短窗烧速短窗含义通道
    P0(叫醒)14.4×1h1h 消耗 2% 月预算pager 群
    P1(工时)6×6h6h 消耗 5% 月预算观察群
    看板(趋势)1×3d3d 消耗 10% 月预算不发钉钉

    为什么"多窗口":短窗判"现在烧得快不快"(快速发现)× 长窗判"是不是真的在持续烧"(防误报)。两个都超阈值才告警。

  5. 预算耗尽政策(需业务老板签字):

    • ≥50% 剩余 → 正常发布
    • 50%~25% → 发布前 review,禁灰度>50%
    • 25%~10% → 仅修复性发布,新功能冻结
    • <10% → 完全冻结,全员投入修复
  6. 合成监控兜底真实流量盲区:

    • cuj-prober 主动拨测 5 条链路(登录/沙箱/文件/部署/访问 URL)
    • 60s 一轮,7×24 主动打,能在无真实流量时发现链路断
    • counter SLI ground truth + 自定义 buckets + OTel trace → Tempo

注意事项:

  • 不是所有监控都叫 CUJ:业界标杆才 10-15 个 CUJ,第一版调研把 71 个点全叫 CUJ 是错的——把"跨用户隔离"(系统不变量)、「版本回滚」(运维动作)、「idcipher 改造」(技术债)都混进 CUJ。
  • SLO 不是越高越好:100% SLO 意味着零错误预算,无法发版无法创新。Google 推荐 99%-99.95% 区间。
  • SLI 要可度量可埋点:如果当前没有埋点,先做埋点改造再定 SLO。
  • SLO 要业务方签字:SLO 决定了"还能坏多久",影响发布节奏,必须有业务方认可。
  • 预算耗尽政策不能是数字摆设:要真的执行"预算耗尽冻结发布",否则 SLO 体系形同虚设。
  • 烧速告警用 Sloth 自动生成:手写 multi-window multi-burn-rate 规则极易写错、难维护。Sloth 声明 SLO objective + SLI 查询,自动生成 recording rule + 三档烧速 alert。

题目4:设计日志采集方案选型#

设计需求: 100 个微服务,多云(AWS + 阿里云),日志量约 100GB/天,需要统一日志采集和查询方案。

方案设计:

选型对比:

  • EFK(Elasticsearch + Fluentd + Kibana):全文索引快,但存储成本高、运维重
  • Loki + Promtail:只索引 label,成本低,云原生友好
  • Datadog Logs:SaaS 一站式,按量计费贵
  • 云厂商原生(CloudWatch/SLS):开箱即用,但跨云不统一

推荐方案:Loki + Promtail(云原生场景 + 成本敏感 + 按 label 查询为主)

架构:

text
应用 stdout → Promtail(DaemonSet)→ Loki(SimpleScalable)→ S3/OSS
                                          ↓
                                     Grafana Explore(LogQL)

关键设计点:

  1. Loki 部署模式:SimpleScalable(target=1-3 个 ingester + querier + compactor),后端用对象存储(S3/OSS,不要用 filesystem——PVC 会满)。

  2. schema 配置(v13 + tsdb + 对象存储):

    yaml
    loki.schemaConfig.configs[0]:
      from: 2024-04-01
      store: tsdb
      object_store: s3
      schema: v13
  3. retention 配置:默认永久保留会撑爆,配 retention_period: 30d + compactor 自动清理。

  4. Promtail 配置:

    • 采集 K8s Pod stdout(自动发现 + 添加 metadata label)
    • label 严格控制基数(app/namespace/env,不要 user_id/request_id)
    • pipeline_stages 解析 JSON 日志提取字段
  5. 结构化日志要求:应用输出 JSON(非文本),必填 ts/level/msg/service/trace_id/request_id + 业务字段(user_id/latency_ms/status_code…),敏感信息脱敏——字段规范与落地细节同 1-概念·子类3·题目6,此处不赘。

  6. 跨云统一查询:US Loki → S3,CN Loki → OSS,Grafana 配两个数据源按 cluster label 切换。

注意事项:

  • Loki PVC 满是高频事故:filesystem 后端 PVC 会满(详见 3-原理·Loki 写入路径题)。生产必须用对象存储 + retention 清理。
  • 高 cardinality 是 Loki 灾难:把 user_id 当 label 会导致 stream 数爆炸,ingester 内存爆 + index 爆。Loki 严格限制 label 基数。
  • 全文搜索慢是设计代价:Loki 按 label + 时间范围扫描,全文搜索 |= "error" 要扫描所有 chunk。查询模式要"先按 label + 时间范围缩小,再过滤内容"。
  • Loki 3.x ruler 规则不能直接放 S3:必须走 ruler API 上传(POST /loki/api/v1/rules/{ns}),kiwigrid sidecar 的本地文件模式对 S3 backend 无效。
  • 跨集群查询用联邦:每个集群一个 Loki,Grafana 配 federated query 跨集群查询。

子类 3:安全架构设计#

题目1:设计零信任安全体系#

设计需求: 从"10 个 P0 漏洞 + 4 条独立路径可拖全量用户 PII + 13 人 prod cluster-admin + 0 MFA + 生产库对 0.0.0.0/0 全开"的安全审计出发,设计零信任整改方案。

方案设计:

五支柱架构:

text
①零信任接入 → Headscale mesh 替代公网直连
②暴露收敛   → 公网面收敛到 VPC + Istio Gateway
③权限最小化 → K8s/IAM 从 admin 降到最小
④凭据治理   → 静态 AK/SK → EKS Pod Identity
⑤数据保护   → 误删/勒索可恢复(S3 Versioning + KMS)

关键设计点:

  1. 零信任接入选型(Headscale 自建):

    • 控制面落阿里云国内直连(SaaS 的 DERP 在境外跨境劣化)
    • Go 单二进制复用现有 ECS 零增量
    • WireGuard mesh + ACL file 模式入 git
    • 网段规划铁律:只 advertise VPC CIDR,永不广播 ClusterIP(多集群 172.20/16 互撞)
  2. 暴露收敛"先摸真实调用方再删":

    • 8 个 RDS SG 0.0.0.0/0 → 逐个 zcat 访问日志确认零业务流量才删
    • 7 个管理面板关匿名/补 TLS(Prometheus/Alertmanager 删 HTTPRoute,Grafana 关匿名,Nexus 补 LE TLS)
    • CN geo-block:CloudFront GeoRestriction(>99%)+ Route53 Geolocation(~90%,黑洞目标指 zone 外 blocked.invalid. RFC 6761)
    • S3 公开桶整改 + PAB 强化
  3. 权限最小化"观察→最小→并行验证→切换→回收":

    • K8s 开发从 cluster-admin → 内置 view + 自定义 pod-exec(can-i 实测 get secrets/create deploy = no)
    • p2s 透传 SA 从能 dump 68-84 secret 收到 0(删 resources:["*"] 通配,显式列业务用到的 9 个资源)
    • IAM *FullAccess → 具体桶(Simulator 验证 16/16 业务 allowed、14/14 越权 deny)
  4. 凭据治理 → EKS Pod Identity:

    • service principal pods.eks.amazonaws.com 不绑集群,跨集群一个 Role 复用
    • 6 服务迁移(agent/backend/eval/oa-backend/openclaw/sandbox)
    • 18 个 Nacos dataId 摘 AK
    • SDK 默认凭据链自动拾取,零业务改动
  5. 数据保护:

    • US S3 40 桶 Versioning + Lifecycle→Glacier(误删/勒索可恢复)
    • 监控脚本周观察旧版本累积 + Glacier transition + 月成本
    • 加密密钥(ChaCha20Poly1305/idcipher AES)拟迁 KMS

注意事项:

  • 整改不能搞挂业务:每一步都先摸真实流量/CloudTrail 再动手。公网收敛 7 个对象全部先逐个 zcat 访问日志确认零业务流量才删。
  • 预算≈0:零信任不新购(Headscale 自建 $0 替代 $7,200-$80,000/年 ZTNA SaaS)、Pod Identity 复用 EKS addon。
  • "一个缺陷往往是一类":发现某组件缺 toleration / node role 缺 policy / SA 通配过宽,必当集群级缺陷排查所有同款。
  • Route53 黑洞必须指 zone 外:blocked.invalid.(RFC 6761 保留 TLD),否则被 zone 内 *.goalfyai.com wildcard 捕获反噬。
  • 回收要等观察期:新旧凭据并行 14 天 CloudTrail 无 AccessDenied 再禁旧 AK,30 天后才真删。
  • 离职只回收身份:ACL 移除 user(秒级生效),节点不删(避免 peer 同步 bug)。

题目2:设计密钥管理方案#

设计需求: 统一管理 100 个微服务的密钥(数据库密码、API Key、TLS 证书),支持自动轮换和审计。

方案设计:

选型对比:

  • Vault:完整密钥管理系统,支持动态密钥,运维重
  • 云 KMS:加密原语服务,简单但功能单一
  • SOPS:文件加密工具,GitOps 友好
  • External Secrets Operator:K8s 原生,与 Vault/云 SM 集成

推荐组合:Vault(动态密钥)+ 云 KMS(数据加密)+ ESO(K8s 集成)+ SOPS(GitOps 静态 Secret)

架构:

text
开发者 → 写代码(不涉及 Secret)
  ↓
GitOps 仓库 → 只存 ExternalSecret/SecretStore CRD(无敏感值)
  ↓
ArgoCD 同步 → K8s 创建 ExternalSecret 对象
  ↓
ESO Controller → 读 SecretStore 配置 → 调 Vault API
  ↓
Vault → 验证 K8s ServiceAccount → 检查 Policy → 返回 Secret
  ↓
ESO → 创建/更新 K8s Secret
  ↓
Pod → 通过 envFrom/volumeMount 使用 Secret

关键设计点:

  1. Vault HA 部署:

    • 3 节点 Raft(不需要 Consul)
    • Auto-unseal(AWS KMS/阿里云 KMS)—— Pod 重启后不需要手动 unseal
    • dataStorage PVC 20Gi gp3
  2. Kubernetes Auth Method:

    bash
    vault auth enable kubernetes
    vault write auth/kubernetes/config \
      kubernetes_host="https://kubernetes.default.svc" \
      token_reviewer_jwt=@/var/run/secrets/.../token
    vault write auth/kubernetes/role/myapp \
      bound_service_account_names=myapp-sa \
      bound_service_account_namespaces=production \
      policies=myapp-policy \
      ttl=1h
  3. Policy 最小权限:

    hcl
    path "secret/data/myapp/prod/*" {
      capabilities = ["read"]
    }
    path "database/creds/app-role" {
      capabilities = ["read"]
    }
  4. 动态密钥(Database Engine):

    • Vault 用 vault-admin 凭据连接数据库
    • 每次 ESO 刷新调 vault read database/creds/app-role → Vault 生成全新临时账号
    • TTL 1h,refreshInterval 45m(确保新账号在旧账号过期前生成)
    • 旧账号 TTL 到期自动吊销(DROP ROLE)
  5. ESO 配置:

    yaml
    apiVersion: external-secrets.io/v1beta1
    kind: SecretStore
    spec:
      provider:
        vault:
          server: "http://vault.vault.svc:8200"
          path: "secret"
          version: "v2"
          auth:
            kubernetes:
              mountPath: "Kubernetes"
              role: "myapp"
              serviceAccountRef:
                name: "myapp-sa"
  6. Secret 轮换触发 Pod 重启:装 Reloader(reloader.stakater.com/auto: "true" annotation)监听 Secret 变化自动重启 Pod。

注意事项:

  • KV v2 路径混淆:ESO 的 remoteRef.key 填 CLI 风格(不带 /data/),API 路径才带 secret/data/——CLI/Policy/API 三者差异同 3-原理·子类4·题目2,此处不赘。
  • K8s 1.24+ ServiceAccount Token:不再自动创建 Secret,需要手动创建或用 TokenRequest API。
  • Vault Pod 重启 sealed:生产必须配 Auto-unseal(AWS KMS/阿里云 KMS),否则半夜 Pod 被 K8s 驱逐要爬起来手动 unseal 三次。
  • ESO 同步失败排查:常见原因——SecretStore 连不上 Vault(NetworkPolicy)、K8s Auth Role 绑定的 SA 不对、Vault Policy 没有 read 权限、KV v2 路径写错。
  • 不要把所有密钥都塞 Vault:简单场景用云 KMS + SOPS 更轻量。Vault 适合需要动态密钥 + 完整审计的场景。
  • Git 仓库里永远不出现敏感值:base64 不是加密,echo bXlwYXNzd29yZA== | base64 -d 就能还原。GitOps 仓库只存 ExternalSecret/SecretStore CRD。

题目3:设计镜像供应链安全方案#

设计需求: 防止镜像被篡改、依赖投毒、CI 被攻陷后恶意镜像进入生产。

方案设计:

全链路安全流水线:

text
代码提交 → SAST 扫描 → 依赖扫描(SCA) → 构建镜像 → 镜像扫描
       → 镜像签名(Cosign) + SBOM → 部署时验证签名 → 运行时安全

关键设计点:

  1. 代码阶段:

    • pre-commit 钩子:gitleaks(硬编码密钥)+ semgrep(漏洞模式)+ detect-private-key
    • PR 阶段:SonarQube 质量门禁 + Semgrep 自定义规则 + Snyk/Trivy 依赖扫描
  2. 构建阶段:

    • Runner 隔离(不同项目不同 Runner)
    • 构建不出网(只允许白名单域名)
    • 不在 Runner 存长期 Secret(通过 Vault 动态注入)
    • 多阶段构建 + distroless 基础镜像减少攻击面
  3. 镜像阶段:

    • Trivy 扫描(HIGH/CRITICAL 阻断发布,--ignore-unfixed 过滤无修复版本的)
    • Cosign 签名(Keyless 模式,短期证书绑定 CI identity)
    • SBOM 生成(Syft)+ 附加到镜像(cosign attach sbom)
  4. 部署阶段:

    • Kyverno ClusterPolicy 强制验证签名(production/staging 命名空间的 Pod 必须有 Cosign 签名)
    • 未签名镜像拒绝创建
    • 镜像 tag 用 {commit}-{timestamp} 不可变,避免 tag 覆盖
  5. 运行时:

    • Falco/Tetragon 检测异常进程(如容器内 curl 下载文件)
    • kube-bench 定期跑 CIS Benchmark
    • 持续合规扫描(防止配置漂移)

注意事项:

  • 渐进引入不要一刀切:三周渐进(第一周只跑不阻断收集基线 → 第二周新增代码阻断 → 第三周存量修复),详见 1-概念·质量门禁题。
  • 维护白名单:每个工具有误报,建立白名单流程(安全团队 Review + 记录理由 + 到期时间)。
  • 门禁要快:SAST 扫描超过 5 分钟开发就会跳过,用增量扫描(只扫改动文件)。
  • Cosign Keyless 依赖 Rekor:网络不通时用传统密钥签名。自部署 Fulcio/Rekor 可避免依赖公共服务。
  • SBOM 的价值在爆发时:Log4Shell 爆发时,有 SBOM 的团队 5 分钟查影响面,没 SBOM 的要几天。
  • 签名验证用 Audit 模式先观察:Kyverno policy 先 validationFailureAction: Audit 跑一段时间,确认无误报再切 Enforce。

题目4:设计 RBAC 最小权限体系#

设计需求: 13 人 prod cluster-admin 需要降权到最小权限,同时不搞挂业务。

方案设计:

K8s RBAC 降权方案:

text
开发人员权限:
  ├── 内置 view ClusterRole(通用读:pods/log/deploy/svc/events/ingress)
  ├── 自定义 pod-exec ClusterRole(pods/exec create+get,支持排障)
  └── 绑定到 IAM ARN(US)或 UID 字符串(CN ACK)

权限矩阵(can-i 实测):
  ✅ list pods/log、exec、list deploy/svc/events
  ❌ get secrets、list nodes、create deployments

关键设计点:

  1. 先试纯 pods-only readonly 不够用:开发反馈 describe deploy/get svc/get events 用不了影响排障。改为「内置 view 覆盖通用读 + 自定义 pod-exec 补 exec」。

  2. p2s 透传 SA ClusterRole 收紧:

    • 删 resources:["*"] 通配(= 能 dump 集群所有 secret)
    • 显式列 9 个业务实际用到的资源(appdeploys/pods/log/svc/events/jobs/httproute)
    • 修前:us-p2s 84 secret 可读 / cn-prod 68 可读
    • 修后:业务流程全通,secret/cm/deploy/node 全 403
  3. IAM 服务账号收紧:

    • *FullAccess → 具体桶
    • 用 CloudTrail Access Advisor 查实际使用范围(90 天内实际只用 2 桶)
    • SimulatePrincipalPolicy 验证最小权限(16/16 业务 allowed、14/14 越权 deny)
    • 新旧 policy 并行 7 天 CloudTrail 无 AccessDenied 再卸旧
  4. Pod Identity 替代静态 AK/SK:

    • service principal pods.eks.amazonaws.com 不绑集群
    • 一个 Role 可绑任意集群 Association(跨集群复用关键)
    • SDK 默认凭据链自动拾取(token 经 169.254.170.23 endpoint 注入)
    • 验证:Pod 内 aws sts get-caller-identity 应显示 AssumedRole

注意事项:

  • resources:["*"] 通配是隐形 secret 泄露:一条 core/* 通配 = 能 dump 全集群 secret(含 HARBOR_ADMIN_PASS/DB/OIDC)→ 显式列业务用到的资源。
  • ACK 不允许同集群同时 grant system + custom:CN view 这条只能直 apply CRB(K8s 层),ACK 控制台只显示 pod-exec(custom)。
  • 观察实际使用 → 起草最小权限 → 并行验证 → 切换 → 回收:每个收权限动作都先用 CloudTrail/Access Advisor 验证真实使用范围,新旧并行观察期 7-14 天再切,旧的留 14-30 天再删。
  • 纯收权限不搞挂业务的根本:先摸真实调用方再动手,绝不拍脑袋收权限。
  • can-i 实测验证:kubectl auth can-i get secrets -n production 应返回 no;kubectl auth can-i list pods -n production 应返回 yes。

子类 4:IaC 架构设计#

题目1:设计 Terraform 多环境多账号管理#

设计需求: 3 个 AWS 账号(prod/staging/dev),每账号 2-3 个 region,需要用 Terraform 统一管理基础设施。

方案设计:

架构:

text
terraform/
├── modules/                    # 可复用模块
│   ├── vpc/
│   ├── eks/
│   └── rds/
├── environments/               # 每环境一个 state
│   ├── prod/
│   │   ├── main.tf
│   │   ├── backend.tf          # S3 + DynamoDB lock
│   │   └── terraform.tfvars
│   ├── staging/
│   └── dev/
└── global/                     # 跨环境共享资源(IAM/Route53)

关键设计点:

  1. State 分环境隔离:每环境一个 state 文件(prod/vpc.tfstate / staging/vpc.tfstate),不要混在一个 state。避免一个环境 apply 影响其他环境。

  2. Backend 配置:

    hcl
    backend "s3" {
      bucket         = "my-tfstate-prod"
      key            = "prod/vpc.tfstate"
      region         = "us-west-2"
      dynamodb_table = "tf-locks-prod"
      encrypt        = true
    }
    • S3 + DynamoDB lock(必须加锁防并发 apply)
    • encrypt = true(state 里可能有敏感数据)
    • S3 versioning 开启(误删可恢复)
  3. Module 复用:把 VPC/EKS/RDS 封装成 module,环境间通过 tfvars 差异化配置。

  4. 跨账号访问:用 assume_role:

    hcl
    provider "aws" {
      region = "us-west-2"
      assume_role {
        role_arn = "arn:aws:iam::<PROD_ACCOUNT>:role/TerraformCrossAccount"
      }
    }
  5. Terragrunt 管理 DRY(可选):多个环境的 backend/provider 配置重复,Terragrunt 可以继承减少重复。

注意事项:

  • State 文件不入 Git:可能含密码且每次 apply 都变。远端存储 + 加锁 + 加密 + 备份。
  • 不要手动改 state:用 terraform state mv/rm 命令,不要直接编辑 JSON。
  • state 加锁必须配:DynamoDB lock 表必须存在,否则并发 apply 会 state 损坏。
  • 跨账号 role trust policy:source 账号的 role 必须在 target 账号的 trust policy 里。
  • OpenTofu 替代 Terraform:严格开源要求或想要 state encryption 等新特性时,改 CLI 名字即可迁移。
  • 大规模 state 要拆分:3000+ 资源的 state plan 需要 2 分钟,拆成多个小 state(如 VPC/EKS/RDS 各一个)。

题目2:设计 Ansible 配置管理规范#

设计需求: 50 台 K8s worker 节点,需要统一配置(内核参数、安装监控 agent、分发证书)。

方案设计:

目录结构:

text
ansible/
├── inventory/
│   ├── hosts.ini               # 主机清单
│   └── group_vars/
│       ├── all.yml             # 全局变量
│       └── prod.yml            # prod 环境变量
├── roles/                      # 可复用 role
│   ├── common/                 # 基础配置(内核参数/时间同步)
│   ├── node-exporter/          # 监控 agent
│   └── certificate/            # 证书分发
├── playbooks/                  # playbook
│   ├── site.yml                # 总入口
│   └── rolling-update.yml      # 滚动更新
└── ansible.cfg                 # 配置

关键设计点:

  1. Role 模块化:每个功能封装成 role(common/node-exporter/certificate),role 间通过 dependencies 声明依赖。

  2. inventory 分组:按环境(prod/staging)+ 角色(worker/master)分组,playbook 按 group 执行。

  3. 幂等性保证:

    • 优先用 Ansible module(file/user/apt/service)而非 shell/command
    • 必须用 shell 时加 creates=(文件存在则跳过)
    • 跑 --check dry run 验证幂等
  4. 滚动更新:用 serial: 1(一次一台)避免同时影响所有节点:

    yaml
    - hosts: worker
      serial: 1
      tasks:
        - name: drain node
          command: kubectl drain {{ inventory_hostname }}
        - name: upgrade kernel
          apt: name=linux-image-generic state=latest
        - name: uncordon node
          command: kubectl uncordon {{ inventory_hostname }}
  5. 变量分层:group_vars/all.yml(全局)→ group_vars/prod.yml(环境)→ host_vars/<hostname>.yml(单机)。

注意事项:

  • Ansible 适合配置管理不适合基础设施编排:创建 VPC/RDS 用 Terraform,节点内配置用 Ansible。
  • SSH 连接要稳定:网络不稳定时 ansible_ssh_retries: 3 + ansible_ssh_timeout: 30。
  • 不要在 Ansible 里存敏感数据:用 Ansible Vault 加密 vars 文件,或从 Vault/SSM 动态获取。
  • CI 里重复跑 playbook 验证幂等:幂等性是 Ansible 的核心,第二次跑应该 0 changed。

题目3:设计混合云 IaC 方案#

设计需求: AWS + 阿里云双云,需要统一 IaC 管理双云资源。

方案设计:

架构:

text
terraform/
├── modules/
│   ├── aws-vpc/
│   ├── alicloud-vpc/
│   ├── aws-eks/
│   └── alicloud-ack/
├── environments/
│   ├── prod-aws/
│   ├── prod-alicloud/
│   └── prod-cross-cloud/       # 跨云资源(DNS/CDN)

关键设计点:

  1. Provider 分云:每云一个 provider,不要混在一个 provider 配置里:

    hcl
    provider "aws" {
      region = "us-west-2"
    }
    provider "alicloud" {
      region = "cn-beijing"
    }
  2. Module 分云但接口统一:aws-vpc 和 alicloud-vpc 的 input/output 变量尽量一致(如都输出 vpc_id),方便上层调用。

  3. 跨云资源单独管理:DNS(Route53/阿里云 DNS)、CDN(CloudFront/阿里云 CDN)等跨云资源放在 prod-cross-cloud 目录。

  4. State 分云:每云一个 state,避免一个 state 太大。跨云资源单独一个 state。

  5. 凭据管理:AWS 用 aws configure / IRSA,阿里云用 ALICLOUD_ACCESS_KEY / RAM Role。

注意事项:

  • 阿里云 provider 文档可能滞后:新功能可能 provider 还没支持,要查 GitHub issues。
  • 跨云网络打通:专线/VPN 配置复杂,建议用云厂商托管服务(如 AWS Direct Connect + 阿里云高速通道)。
  • 资源命名规范:跨云资源命名要统一(如 {env}-{service}-{cloud}),避免混淆。
  • 成本治理:双云成本要统一看板,用 CloudZero/Vantage 或自建脚本聚合 AWS Cost Explorer + 阿里云费用中心。

子类 5:平台工程设计#

题目1:设计内部开发者平台(IDP)#

设计需求: 100 人技术团队,10 个微服务,希望开发者自助创建新服务、部署、看监控,不需要平台团队介入。

方案设计:

IDP 架构:

text
开发者 → Backstage(IDP 前端)
  ├── 软件目录(服务清单/owner/依赖)
  ├── 软件模板(scaffolder:新服务从模板生成)
  ├── TechDocs(文档统一入口)
  └── 插件集成(ArgoCD/Grafana/PagerDuty/Sentry)
        ↓
平台 API(封装底层工具)
  ├── 创建 GitOps overlay → ArgoCD 自动 sync
  ├── 创建 ECR 仓库
  ├── 创建流水线(云效/GitHub Actions)
  └── 注册到服务发现

关键设计点:

  1. 软件目录(Software Catalog):每个服务在 Backstage 注册一个 Component,包含 owner、依赖、API、文档。catalog-info.yaml 入服务仓库。

  2. 软件模板(Scaffolder):新服务从模板生成——填表单(服务名/语言/owner)→ Backstage 自动创建 Git 仓库 + GitOps overlay + ECR 仓 + 流水线 + 监控。整个流程 5 分钟,不需要平台团队介入。

  3. 黄金路径文档化:在 Backstage TechDocs 里写清楚"新建一个 Go HTTP 服务的黄金路径"——按模板做 CI/CD/监控/告警/日志/密钥全自动到位。

  4. 插件集成:Backstage 的 ArgoCD 插件看部署状态、Grafana 插件看监控、PagerDuty 插件看告警、Sentry 插件看错误。开发者不需要跳多个工具。

  5. Scorecards:给每个服务打分——是否有 README、是否有 owner、是否接监控、是否跑 SAST。驱动团队主动补齐。

注意事项:

  • IDP 不是工具堆砌:核心是"黄金路径"——开发者按推荐路径做,一切自动化。不是把所有工具都塞进 Backstage。
  • 平台团队 ownership:IDP 本身需要平台团队维护(Backstage 升级、插件维护、模板更新)。1-2 人平台团队维护 100 人 IDP 是合理的。
  • 不要强制所有服务都走 IDP:20% 特殊服务走自定义路径并标记"长期例外"(同 1-概念·子类1·题目5)。
  • Scorecards 要渐进:先有"是否有 README"这种基础项,再加"是否接监控"等高级项,不要一开始就 100 分要求。
  • Backstage 学习曲线陡:需要 React 开发能力定制插件,小团队可考虑 Port/Humanitec 等 SaaS 替代。

题目2:设计开发者黄金路径#

设计需求: 为"新建一个 Go HTTP 服务"设计一条黄金路径,让开发者 30 分钟内完成从创建到部署到 QA 的全流程。

方案设计:

黄金路径步骤:

  1. 创建服务(5 分钟):

    • 开发者在 Backstage 点"Create Component"
    • 选"Go HTTP Service"模板
    • 填表单:服务名、owner、是否需要数据库
    • Backstage 自动创建:Git 仓库 + Dockerfile + base Kustomize + overlay 模板 + ECR 仓 + 流水线
  2. 写业务代码(按需):

    • clone 仓库
    • 模板已包含:health check endpoint、结构化日志、OTel SDK、Prometheus /metrics、Graceful shutdown
    • 开发者只关心业务逻辑
  3. push 代码(1 分钟):

    • push 到 feature 分支 → PR
    • CI 自动跑:单测 + SAST + 依赖扫描 + 镜像构建 + 镜像扫描 + Cosign 签名
  4. PR Review + 合并(按需):

    • main 分支保护(禁直推,强制 MR)
    • Review 通过后 squash merge 到 main
  5. 自动部署 QA(5 分钟):

    • push main 触发部署流水线
    • deploy.py 改 GitOps overlay 的 newTag → git push
    • ArgoCD ApplicationSet 检测到变化 → 自动 sync
    • Pod Ready → 钉钉通知"部署成功"
  6. 验证(按需):

    • 开发者访问 QA 域名验证
    • Grafana 看监控、Loki 看日志、Tempo 看 Trace

模板包含的"开箱即用"能力:

  • Dockerfile(多阶段构建 + distroless)
  • base Kustomize(deployment + service + pdb + configmap)
  • HPA(CPU 70%)
  • ServiceMonitor(Prometheus 抓取)
  • PrometheusRule(基础告警:Pod 重启、5xx 率)
  • ExternalSecret(从 Vault 拉密钥)
  • NetworkPolicy(默认 deny + 放行必要流量)
  • OTel SDK(trace + metrics)
  • 结构化日志(zap/slog)
  • ReadinessProbe + LivenessProbe
  • Resource requests/limits
  • SecurityContext(runAsNonRoot + readOnlyRootFilesystem)

注意事项:

  • 黄金路径不是强制:80% 走模板、20% 特殊服务走自定义路径并标记"长期例外"(同 1-概念·子类1·题目5)。
  • 模板要持续维护:技术栈升级(如 Go 1.22 → 1.23)时更新模板,新建服务自动用新版本,老服务不强制迁移。
  • 边际成本要低:第 11 个服务接入和第 1 个一样快(30 分钟),这是黄金路径成功的标志。
  • 不要过度工程:模板包含基础能力即可,不要塞入业务特定的东西(如特定数据库 SDK)。
  • Scorecards 驱动补齐:老服务没走黄金路径的,用 Scorecards 显示"缺 README/缺监控/缺告警",驱动团队主动补齐。

题目3:设计多租户 K8s 平台#

设计需求: SaaS 平台,多个租户共享 K8s 集群,需要隔离但成本可控。

方案设计:

隔离方案选型:

  • 方案 A(每租户独立集群):最强隔离,但成本高(每集群固定开销)
  • 方案 B(共享集群 + namespace 隔离):成本低,但隔离弱
  • 方案 C(共享集群 + namespace + 资源配额 + NetworkPolicy):推荐,成本和隔离平衡

方案 C 架构:

text
K8s 集群
├── tenant-a namespace
│   ├── ResourceQuota(CPU/内存/PVC 限制)
│   ├── LimitRange(默认/最大资源)
│   ├── NetworkPolicy(default-deny + 放行必要流量)
│   ├── RBAC(tenant-a 团队只管自己 namespace)
│   └── 业务 Pod
├── tenant-b namespace(同上)
└── tenant-c namespace(同上)

关键设计点:

  1. namespace 即租户边界:每租户一个 namespace,RBAC 限制租户只能操作自己 namespace。

  2. ResourceQuota 资源配额:

    yaml
    spec:
      hard:
        requests.cpu: "32"
        requests.memory: "64Gi"
        persistentvolumeclaims: "20"
        services.loadbalancers: "2"
        pods: "200"
  3. NetworkPolicy 微分段:

    • default-deny-all(默认拒绝所有入站出站)
    • allow-internal(namespace 内互通)
    • allow-egress-dns(放行 DNS)
    • allow-egress-https(放行外部 API)
  4. RBAC 最小权限:

    • 租户管理员:自己 namespace 的 admin(不含 secrets)
    • 租户开发者:自己 namespace 的 view + pod-exec
  5. 数据隔离(参考子环境 ID 撞车事故):

    • 每租户独立数据库 schema 或独立 Aurora cluster
    • 业务表 AUTO_INCREMENT 起点错开(如 tenant-a 从 1,tenant-b 从 1000 万)
    • RabbitMQ/Kafka 独立 broker 或严格 topic 前缀
    • Redis 独立实例或 key 前缀
  6. Pod Security Admission:所有 namespace 配 restricted 级别。

注意事项:

  • 不要只靠 namespace 隔离:namespace 是软隔离,内核层仍共享。高安全要求用 gVisor 或独立集群。
  • ResourceQuota 要合理:太严导致 Pod Pending,太松导致资源争抢。根据租户付费等级分配。
  • NetworkPolicy 默认 deny:先部署 default-deny-all,再逐条放行必要流量。不要先 allow 再 deny。
  • 数据隔离是最难的:参考"子环境 ID 撞车污染 10 万条数据"事故——4 件套(id 重叠 + broker 共用 + 过滤缺失 + 配置漏改)缺一不会爆雷,但省事姿势会让 4 件套同时为真。
  • 多租户 SaaS 的租户隔离不在本范围:本方案是环境维度(dev/qa/prod)的隔离,不是租户(tenant_id)维度的隔离,后者通常需要在应用层解决。

题目4:设计成本治理 FinOps 体系#

设计需求: 月费从失控峰值 $36,829 治理到日均 ~$640,建立持续成本治理体系。

方案设计:

FinOps 心法(贯穿全程的纪律):

text
摸真实用量(CloudTrail/Cost Explorer/CloudWatch)
  → 评 ROI 与风险
  → 止血优先
  → 观察期/灰度
  → 根治/回收
     ↑ 省钱不破可用性(稳定性红线)

关键设计点:

  1. 账单拆到分钟级:按 service × USAGE_TYPE × log group × 资源定位。成本突增先拆 USAGE_TYPE 再拆 log group。

    bash
    # 按服务拆
    aws ce get-cost-and-usage --group-by Type=DIMENSION,Key=SERVICE
    # 按 USAGE_TYPE 拆
    aws ce get-cost-and-usage --group-by Type=DIMENSION,Key=USAGE_TYPE
  2. 成本事故秒级止血(案例:Aurora 审计日志税一天烧 $170):

    • 三层拆解定位:service → USAGE_TYPE → log group → QPS → commit
    • 止血方案选择:① 关 CW export(本地可回查仅 47min);② 收窄事件(选用,保留 CONNECT/DDL/DCL);③ 全关 audit
    • 止血 ≠ 根治:触发因素是 agent 轮询 bug(交开发根治),结构性缺陷是"成本侧没有突增告警"(要补 CloudWatch Logs Ingestion 单日 >50GB 告警)
  3. 结构性浪费识别:

    • "缩不动节点"= requests 虚高:实际 CPU 2-6% 但 requests 占 90% → 降 requests 模板(100m→25m)
    • "越扩越多"= 内存 HPA 误配:常驻内存型服务禁用 memory-Utilization HPA——加副本不降单 pod 内存(虚扩到 13 副本的完整案例见 4-场景·子类5·题目2)
    • Serverless vs Provisioned:低吞吐稳定负载用 Provisioned 便宜(MSK Serverless $540 → t3.small×2 $75)
  4. 僵尸回收"五件套"确认零业务:

    • NLB 流量(无请求)
    • ConnectionCount(无连接)
    • 应用日志(无业务调用)
    • 节点 CPU(无活动)
    • CloudTrail 真人活动(无 API 调用) 全部为零才删,删前做 final snapshot。
  5. ROI 否决权(FinOps 的克制):

    • QA/PRE 定时缩容能省 $300-500/月,但引入 Lambda + EventBridge + 钉钉 Bot 新基础设施(新故障面)+ 夜间 QA 不可用 → 主动否决
    • MSK Serverless → Provisioned 省 $1,470/月,无故障面纯收益 → 做
    • 区别在"这笔省钱的代价是什么"
  6. 稳定性红线(省钱不破可用性的护栏):

    • P0 服务(agent/backend/dispatch/ai-gateway)min≥2 仅 On-Demand
    • Prod DB 不合并、PRE 写保护
    • Spot 仅限 P2 服务且必配 PDB + 拓扑约束
    • 合并集群必加 NetworkPolicy + ResourceQuota

注意事项:

  • 成本治理是有腐化速度的活物:每个新加的资源、每次故障复盘、每次临时 silence,都在往里堆熵。半年没治理就是失控。
  • 止血 ≠ 根治 ≠ 防回退:事故先秒级止血(恢复服务),再根治(改代码/配置),再防回退(同类全排查 + 告警)。三层分清。
  • 双层归因:故障不只问"为什么烧钱"(轮询 bug),更问"为什么 3 天才发现"(没有成本突增告警)——后者才是真正该修的。
  • EOL 成本炸弹:K8s 1.33 在 2026-07-29 截止 → 提前改 EXTENDED 买窗口,否则被强制升级。
  • Savings Plans 采购:稳定 1 月后再采购,不要在治理初期就 lock-in。
  • 不为了"省"而省:能用 SaaS 省一个全职工程师工作量的(如 CI 用云效/GitHub Actions 而不自建 Jenkins),不要为了省 SaaS 费用而自建。