面试 · 方案设计型
答题提示:系统设计题不要直接给方案。先问清楚约束条件(规模、SLA、成本预算、团队规模),再展开设计。每个方案都要能讲清楚“为什么这么选”和“代价是什么”。
答题结构:需求/约束 → 架构 → 关键设计点 → 取舍 → 验证 → 治理。题干(设计需求)与参考答案(其后各节)分开阅读。
API 版本速查(截至 2026,K8s 1.3x):HPA=
autoscaling/v2、NetworkPolicy/Ingress=networking.k8s.io/v1(Ingress 已冻结,新场景用 Gateway APIgateway.networking.k8s.io/v1,v1 已 GA)、Karpenter=karpenter.sh/v1、EncryptionConfiguration=apiserver.config.k8s.io/v1、Lease=coordination.k8s.io/v1。已废弃勿用:extensions/v1beta1、autoscaling/v2beta1/2、networking.k8s.io/v1beta1Ingress、PodSecurityPolicy。
子类 1:集群规划与高可用设计#
题目 1:etcd 集群规划 — 3 节点 vs 5 节点#
设计需求: 需要为一个生产 K8s 集群规划 etcd 集群,要求保证控制面数据的强一致性和高可用。
方案对比:
| 维度 | 3 节点 | 5 节点 |
|---|---|---|
| 容错能力 | 容忍 1 节点故障 | 容忍 2 节点故障 |
| Quorum | 需要 2 票(> 3/2) | 需要 3 票(> 5/2) |
| 写入延迟 | 更低(只需等 2 节点确认) | 略高(需等 3 节点确认) |
| 资源成本 | 3 × (4C8G + SSD) | 5 × (4C8G + SSD) |
| 适用场景 | 一般生产集群 | 大规模集群 / 金融级 / 跨区域部署 |
关键设计点:
- 奇数节点是硬要求:Raft 协议需要奇数节点形成 quorum,偶数节点(如 4 节点)容错能力 = 3 节点(都只能容忍 1 节点故障),但多了资源开销
- 磁盘是关键瓶颈:必须用 SSD(NVMe 最佳),不要与其他高 IO 服务共享磁盘。etcd 的 WAL 写入是同步的,磁盘延迟直接决定写入性能
- 独立部署 vs 与控制面混合:生产环境建议 etcd 独立部署(或使用托管 etcd),避免与控制面组件争抢资源
- 跨可用区部署:3 节点部署在 3 个 AZ,单个 AZ 故障不影响 etcd 可用性
- 定期备份:
etcdctl snapshot save+ 定期 defrag(碎片整理)
架构拓扑(文字描述):
AZ-a: etcd-0 (4C8G, 100GB SSD NVMe)
AZ-b: etcd-1 (4C8G, 100GB SSD NVMe)
AZ-c: etcd-2 (4C8G, 100GB SSD NVMe)
│
├── Raft Leader Election → 同一时刻仅一个 Leader 接受写入
├── 默认线性一致性读:通过 ReadIndex 与 Leader 确认最新 commit index,可由任意成员响应(并非全部打到 Leader);追求极致读吞吐可显式用 serializable 读走本地副本(可能读到旧值)
└── 3 节点部署 → 容忍 1 个 AZ 故障注意事项:
- etcd 存储配额
--quota-backend-bytes默认 2GB,最大不建议超过 8GB(超过后性能下降明显;写满触发 NOSPACE 告警后集群转只读,须 defrag + 解除 alarm 恢复) - 配置
--auto-compaction-mode=periodic --auto-compaction-retention=1h自动清理历史版本 - 监控指标:
etcd_disk_wal_fsync_duration_seconds_bucket(P99 < 10ms)、etcd_server_has_leader(必须 = 1)
题目 2:Control Plane 高可用方案#
设计需求: 设计一个生产级 K8s 控制面的高可用架构,确保 API Server 在任何单点故障下仍可用。
方案设计:
三层高可用架构:
┌──────────────────────┐
│ External LB │ (AWS NLB / 阿里云 SLB / HAProxy)
│ VIP: 10.0.0.100 │
└──────┬───────┬───────┘
│ │
┌────────────┘ └────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ API Server #1 │ │ API Server #2 │ (无状态,生产建议 ≥3 个跨 AZ;图中简化为 2)
│ AZ-a │ │ AZ-b │
└────────┬─────────┘ └────────┬─────────┘
│ │
└───────────┬─────────────────────┘
▼
┌──────────────────┐
│ etcd Cluster │ (3 或 5 节点,跨 AZ)
│ Raft Quorum │
└──────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼
┌──────────────┐ Leader Election ──────────┐
│ Scheduler │ ───────────── │ Scheduler (standby) │
│ Controller │ │ Controller (standby)│
│ Manager │ │ Manager │
└──────────────┘ └─────────────────────┘关键设计点:
- API Server 无状态:3 个实例部署在不同 AZ,前面挂 L4 LB(TCP 6443)。API Server 本身不存状态,可以任意水平扩展
- Scheduler/ControllerManager Leader Election:多实例同时只有一个 Leader 工作(通过 kube-apiserver 中的
Lease对象coordination.k8s.io/v1抢锁实现,最终持久化到 etcd,而非直接读写 etcd),Leader 挂了自动选举新 Leader - LB 选择:AWS 用 NLB(Network Load Balancer),阿里云用 SLB,自建用 HAProxy + Keepalived
- 证书管理:API Server 证书必须包含 LB 的 VIP/域名,否则客户端通过 LB 访问时 TLS 验证失败
注意事项:
- etcd 与控制面组件之间的网络延迟必须 < 10ms
- API Server 过载保护优先用 APF(API Priority and Fairness,
flowcontrol.apiserver.k8s.io/v1,1.29 GA) 做请求分级限流;--max-requests-inflight(默认 400 非 mutating)/--max-mutating-requests-inflight(默认 200)是 APF 关闭时的兜底,需按集群规模调整 - 控制面节点不要调度业务 Pod(加 taint 隔离)
题目 3:多 Master 部署拓扑设计#
设计需求: 某公司需要在 3 个可用区各部署一个 Master 节点,设计多 Master 的部署拓扑。
方案设计:
部署拓扑:
Region: cn-hangzhou
├── AZ-a
│ ├── Master-1 (API Server + Scheduler + ControllerManager)
│ ├── etcd-1
│ └── Worker Nodes (3-5 台)
├── AZ-b
│ ├── Master-2 (API Server + Scheduler + ControllerManager)
│ ├── etcd-2
│ └── Worker Nodes (3-5 台)
└── AZ-c
├── Master-3 (API Server + Scheduler + ControllerManager)
├── etcd-3
└── Worker Nodes (3-5 台)
跨 AZ 通信:
├── etcd 节点间:内网 HTTPS 2380(peer)/ 2379(client)
├── API Server → etcd:内网 HTTPS 2379
├── Worker → API Server:通过 LB(跨 AZ 负载均衡)
└── Worker 间 Pod 通信:CNI 跨 AZ 路由(VPC 原生或 Overlay)关键设计点:
- kubeadm 部署:第一个 Master 执行
kubeadm init,后续 Master 通过kubeadm join --control-plane加入 - etcd 部署模式:
- Stacked etcd(与控制面同节点):简单,适合中小集群
- External etcd(独立节点):性能更好,etcd 不受控制面影响
- 跨 AZ 延迟要求:etcd 节点间延迟 < 10ms,否则 Raft 写入性能骤降
- Worker 节点分布:每个 AZ 至少 3 台 Worker,保证 Pod 可以跨 AZ 分散部署
注意事项:
- 跨 AZ 的 etcd 延迟是关键指标,部署前必须实测
- 使用 Pod 反亲和性让 CoreDNS 副本分布在不同 AZ
- 跨 AZ 流量可能产生费用(取决于云厂商网络架构)
题目 4:集群规模估算#
设计需求: 预计运行 100 个微服务(每个 2-3 个副本),总计约 300 个 Pod。需要估算集群节点规模、性能瓶颈。
估算方法:
1. 节点规模估算:
| 计算维度 | 估算 |
|---|---|
| 总 Pod 数 | 300(业务)+ 50(系统组件)+ 50(buffer)= 400 |
| 每节点 Pod 上限 | 建议 < 110(默认 kubelet max-pods) |
| 节点核心数 | 假设 c6a.2xlarge(8C32G),每节点跑 30-40 Pod |
| 节点总数 | 400 / 35 ≈ 12 台 → 加 buffer 15 台 |
2. Service 数量估算:
- 100 个 Service(每个微服务一个)+ 系统 Service ≈ 120 个
- 每个 Service 对应 iptables 或 ipvs 规则
- iptables 模式下规则数 = Service × Endpoints → 可能上千条
- 超过 1000 个 Service 建议切换到 ipvs 模式(O(1) 查找)
3. 控制面性能瓶颈:
| 组件 | 瓶颈点 | 阈值参考 |
|---|---|---|
| etcd | 存储大小 | < 8GB |
| etcd | 磁盘 IOPS | SSD NVMe,P99 fsync < 10ms |
| API Server | 并发请求 | 默认 max-requests-inflight=400(mutating=200) |
| kube-scheduler | 调度吞吐 | 约 100 Pods/s(默认配置) |
| CoreDNS | 内存 | 大集群需调至 512Mi limit |
注意事项:
- 单集群建议上限:5000 节点 / 150000 Pod / 10000 Service(以上需分割为多集群)
- 实际瓶颈通常是 etcd(存储和 IOPS)→ 大集群考虑 etcd 独立部署或托管 etcd
- Service/节点规模大时用 IPVS 或 kube-proxy nftables 后端(1.31 beta、2025 年 GA) 替代 iptables,避免规则线性膨胀;Cilium/Calico 的 eBPF 数据面可直接绕过 kube-proxy
题目 5:kubeadm vs Managed K8s 选型#
设计需求: 团队需要决定自建 kubeadm 集群还是使用云厂商托管 K8s(如 ACK/EKS/GKE)。
方案对比:
| 维度 | kubeadm 自建 | Managed K8s(ACK/EKS) |
|---|---|---|
| 控制面管理 | 团队负责高可用、升级、备份 | 云厂商负责(SLA 99.95%) |
| etcd 运维 | 自行管理(备份/恢复/扩缩) | 托管 etcd(无需运维) |
| 版本升级 | 手动执行,需停机窗口 | 控制台/CLI 触发,滚动升级 |
| 网络插件 | 自由选择 Calico/Cilium/Flannel | 预集成(ACK=Terway, EKS=VPC CNI) |
| 成本 | 控制面节点成本 + 运维人力 | 按集群收费(ACK Pro ~¥3k/月) |
| 自定义能力 | 完全控制所有组件配置 | 有限(不能改 API Server 参数) |
| 适用场景 | 混合云/边缘/合规约束 | 标准云原生场景 |
选择逻辑:
- 选 Managed K8s(推荐大多数场景):
- 团队规模 < 10 人,无专职 SRE
- 控制面运维投入产出比低
- 需要快速扩容多集群
- 选 kubeadm:
- 混合云/多数据中心(统一部署工具链)
- 需要自定义 API Server 参数(如 OIDC、Admission Webhook)
- 合规要求控制面不能出企业网络
- 边缘计算场景(轻量级 K3s/k0s)
注意事项:
- Managed K8s 的控制面升级窗口不可控(云厂商决定),提前了解升级策略
- kubeadm 集群务必配置 etcd 自动备份(CronJob)
- 混合场景:核心业务用 Managed K8s,边缘节点用 kubeadm/K3s
子类 2:多租户与命名空间设计#
题目 1:命名空间划分策略设计#
设计需求: 一个中型公司(50+ 开发人员,5 个业务团队)需要设计 K8s 命名空间划分策略,支持多环境(dev/qa/staging/prod)。
方案设计:
命名空间命名规范:
<team>-<env> 模式
team-alpha-dev team-beta-dev
team-alpha-qa team-beta-qa
team-alpha-staging team-beta-staging
team-alpha-prod team-beta-prod
# 共享基础设施
kube-system monitoring
ingress-nginx cert-manager
argocd vault隔离层次设计:
第一层:集群级隔离
├── Prod 集群(独立集群,物理隔离)
└── Non-Prod 集群
├── Dev/QA/Staging 共用集群
└── 按 namespace 逻辑隔离
第二层:命名空间级隔离
├── 每个团队 × 每个环境一个 namespace
├── ResourceQuota 限制 CPU/Memory/Storage
├── LimitRange 设置默认 request/limit
└── NetworkPolicy 限制跨 namespace 流量
第三层:RBAC 权限隔离
├── 每个 namespace 一个 ServiceAccount
├── RoleBinding 限制团队只能操作自己的 namespace
└── ClusterRole 只读(如查看节点状态)注意事项:
- 不要用 namespace 做安全边界(namespace 是管理边界,NetworkPolicy + RBAC 才是安全边界)
- Prod 和 Non-Prod 必须物理隔离(不同集群或至少不同节点池)
- 避免 namespace 爆炸(> 50 个 namespace 管理困难)
题目 2:ResourceQuota 配额体系设计#
设计需求: 为多团队共享集群设计 ResourceQuota 策略,防止某个团队耗尽集群资源。
方案设计:
三层配额架构:
# 第一层:集群总配额(防止超卖)
# 集群所有 namespace requests 之和 < 节点总 capacity
# 第二层:Namespace 配额(按团队分配)
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-alpha-quota
namespace: team-alpha-prod
spec:
hard:
requests.cpu: "20" # 最多申请 20 核
requests.memory: "40Gi" # 最多申请 40GB
limits.cpu: "40" # 使用上限 40 核
limits.memory: "80Gi" # 使用上限 80GB
persistentvolumeclaims: "10" # 最多 10 个 PVC
requests.storage: "500Gi"
services: "20"
configmaps: "50"
secrets: "50"
pods: "100"
services.nodeports: "0" # 禁止使用 NodePort
# 第三层:单个 Pod 限制(LimitRange)
apiVersion: v1
kind: LimitRange
metadata:
name: team-alpha-limits
namespace: team-alpha-prod
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi" # 未指定 limit 的容器默认值
defaultRequest:
cpu: "200m"
memory: "256Mi" # 未指定 request 的容器默认值
max:
cpu: "4"
memory: "8Gi" # 单个容器最大限制
min:
cpu: "50m"
memory: "64Mi" # 单个容器最小限制关键设计点:
- 配额 = 反超卖:
sum(namespace requests) < cluster allocatable,否则 Pod 无法调度 - default vs defaultRequest:不指定 limit/request 时自动注入,避免 BestEffort Pod
- max 限制:防止单个容器申请过大资源
- Scope 区分:可以用
scopes: [BestEffort, NotBestEffort, Terminating, NotTerminating]精确控制
注意事项:
- 配额变更需要与团队协商,避免一刀切
- 配额过紧会导致开发体验差(申请被拒),过松导致资源浪费
- 配合监控:Prometheus 告警
namespace_quota_usage > 80%
题目 3:多租户方案选型 — vCluster vs Capsule vs HNC vs Namespace#
设计需求: 为 SaaS 平台设计多租户隔离方案。每个租户需要独立的命名空间、RBAC、网络策略,且租户之间完全隔离。租户数 100+。
方案对比:
| 维度 | 原生 Namespace | vCluster | Capsule | HNC (Hierarchical Namespace) |
|---|---|---|---|---|
| 隔离程度 | 逻辑(RBAC+NP) | 物理(独立 API Server) | 逻辑(增强 RBAC) | 逻辑(层级继承) |
| API Server 开销 | 无 | 每个租户一个 vCluster(K3s) | 无 | 无 |
| 租户管理员体验 | 需学 K8s RBAC | 完整的 K8s 管理员体验 | 简化的租户 CRD | 继承父 namespace 配置 |
| 资源效率 | 最高 | 最低(每租户额外 Pod) | 高 | 高 |
| 适用租户数 | < 50 | < 100 | 100-500 | 50-200 |
| 运维复杂度 | 低 | 高(管理 n 个集群) | 中(学习 Capsule CRD) | 中(学习 HNC CRD) |
选择建议:
- 原生 Namespace:租户 < 30,隔离要求不高,团队熟悉 K8s
- vCluster:租户需要完整 K8s 体验(CRD/Custom Controller),强隔离要求
- Capsule:租户 50+,需要 namespace 额度管理和租户自助服务
- HNC:需要层级化(如 team → sub-team → env)的 namespace 策略继承
Capsule 架构示意:
Capsule Tenant "tenant-A"
├── Namespace: tenant-a-dev
├── Namespace: tenant-a-prod
├── NetworkPolicy: 跨 namespace 隔离 + 只允许到 ingress
├── ResourceQuota: 汇总所有子 namespace 配额
└── RBAC: Tenant Owner 角色(只能操作自己的 namespace)注意事项:
- vCluster 的每个虚拟集群需要跑 API Server + Controller Manager + etcd/kine(≈ 300MB 内存 / 租户),100 个租户 = 30GB 开销
- Capsule 的租户配额是 summary 级别,子 namespace 配额由租户管理员自行分配
- HNC 的层级继承可能造成意外影响(如某父 namespace 的 NetworkPolicy 被所有子 namespace 继承)
题目 4:网络隔离策略分层设计#
设计需求: 在多租户 K8s 集群中设计网络隔离策略,实现:同 namespace 内 Pod 自由通信、跨 namespace 默认拒绝、特定 namespace 可访问共享服务。
方案设计:
默认策略(Deny-All Baseline):
┌──────────────────────────────────────┐
│ 1. 每个 namespace 默认拒绝所有 ingress │
│ 2. 每个 namespace 默认拒绝所有 egress │
└──────────────────────────────────────┘
逐层放行(Least Privilege):
┌──────────────────────────────────────┐
│ 3. 允许同 namespace 内 Pod 互访 │
│ 4. 允许 DNS 查询(UDP 53 → kube-system)│
│ 5. 允许访问 Ingress Controller │
│ 6. 允许访问监控/metrics 端点 │
│ 7. 允许特定跨 namespace 业务调用 │
└──────────────────────────────────────┘配置示例(关键 NetworkPolicy):
# 1. 默认拒绝所有 ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: team-alpha
spec:
podSelector: {} # 匹配所有 Pod
policyTypes:
- Ingress
---
# 2. 默认拒绝所有 egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Egress
---
# 3. 放行 DNS(必须最先放行)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: team-alpha
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53注意事项:
- DNS 放行规则必须放在最前面(几乎所有服务都需要 DNS)
- Calico/Cilium 支持更细粒度的网络策略(FQDN 级别、L7 级别)
- 测试网络策略用
netshoot容器:kubectl run tmp --rm -it --image=nicolaka/netshoot -- /bin/bash
子类 3:网络架构设计#
题目 1:CNI 选型 — Flannel vs Calico vs Cilium#
设计需求: 为生产 K8s 集群选择 CNI 插件。集群规模 50 节点、2000 Pod,需要 NetworkPolicy、高性能、可观测性。
方案对比:
| 维度 | Flannel | Calico | Cilium |
|---|---|---|---|
| 数据平面 | vxlan/host-gw | iptables/ipvs | eBPF |
| NetworkPolicy | 不支持原生(需额外组件) | 支持原生 + 自有扩展 | 支持原生 + L7(DNS/HTTP) |
| 性能 | 中等(vxlan 有开销) | 高(BGP 模式近乎原生) | 最高(eBPF 内核处理) |
| 可观测性 | 无 | 基本 flow log | Hubble(L3/L4/L7 + API 调用图) |
| Service Mesh | 需独立部署 Istio | 需独立部署 Istio | 内置(Cilium Service Mesh) |
| 学习曲线 | 低 | 中 | 高(需理解 eBPF) |
| 适用规模 | 中小集群 | 中大集群 | 大集群(500+ 节点) |
选择逻辑:
集群规模选择:
├── < 100 Pod → Flannel(简单够用)
├── 100-2000 Pod → Calico(NetworkPolicy + 高性能)
└── > 2000 Pod → Cilium(eBPF 性能 + Hubble 可观测性)
功能需求选择:
├── 只需要基本网络 → Flannel
├── 需要 NetworkPolicy → Calico
├── 需要 L7 策略 + 可观测性 → Cilium
└── 需要 Service Mesh → Cilium(无 sidecar)vs Istio(sidecar)关键设计点:
- IPAM 模式选择:
- Calico:推荐使用 Kubernetes API 作为 IPAM 后端(简单可靠)
- Cilium:推荐 Cluster Pool(自动分配 Pod CIDR)
- 路由模式选择:
- 同 VPC/二层可达:BGP 或 Direct Routing(性能最好)
- 跨 VPC/三层隔离:VXLAN 或 Geneve(Overlay 模式)
- eBPF 要求:Cilium 最低 kernel 4.19,推荐 >= 5.4(kube-proxy replacement 及部分 eBPF 高级特性需 5.10+),云环境注意节点内核版本
注意事项:
- Calico BGP 直连模式适用于自建/裸金属等可控网络;AWS VPC 不支持与节点做 BGP peering,云上 Calico 多用 VXLAN/IPIP overlay 或直接改用云厂商 VPC CNI
- Cilium 替换 kube-proxy(新版用
kubeProxyReplacement: true,旧取值strict/probe/partial已废弃)有严格内核要求 - CNI 迁移风险极高,选型后基本不会再换
题目 2:Service Mesh 选型 — Istio vs Cilium vs Linkerd#
设计需求: 100+ 微服务需要使用 Service Mesh 实现流量管理、mTLS、可观测性。
方案对比:
| 维度 | Istio (sidecar 模式) | Cilium Service Mesh | Linkerd |
|---|---|---|---|
| 流量管理 | VirtualService + DestinationRule(最强大) | L7 HTTP Route(eBPF) | SMI + 自有扩展 |
| mTLS | 自动(istiod 签发,早期组件叫 Citadel) | 自动(基于 SPIFFE) | 自动(identity-based) |
| 可观测性 | Kiali + Jaeger/Zipkin | Hubble(无需额外组件) | Linkerd Viz |
| 资源开销 | 高(每个 Pod 一个 sidecar ~100MB) | 低(无 sidecar,eBPF 内核处理) | 中(rust proxy 约 10MB) |
| 性能影响 | 中等(两次网络栈处理) | 最低(eBPF 在内核处理) | 低(rust proxy 开销小) |
| 生态成熟度 | 最成熟(CNCF Graduated) | 正在追赶(功能不如 Istio 全) | 成熟(CNCF Graduated) |
| 学习曲线 | 最高 | 中 | 低 |
| 社区活跃度 | Google + 社区 | Isovalent (Cisco) | Buoyant |
选择建议:
- Istio:需要复杂的流量管理(金丝雀/A/B Test/熔断/限流),已有独立运维团队
- Cilium:新项目,追求性能和运维简便,愿意接受功能尚未完全对标 Istio
- Linkerd:只需 mTLS + 基础流量管理,运维简单优先
架构示意 (Istio):
Service A (sidecar: istio-proxy)
│
├── mTLS ──→ Service B (sidecar)
│ │
│ └──→ Service C (sidecar)
│
├── Telemetry → Prometheus (metrics)
├── Tracing → Jaeger (traces)
└── Logs → stdout → Fluent Bit → Loki注意事项:
- Istio sidecar 资源开销估算:100 个 Pod × 100MB = 10GB 额外内存
- Istio Ambient 模式(无 sidecar,1.24 GA):L4 mTLS 由每节点 ztunnel 承载、L7 才按需拉起 waypoint proxy,资源开销显著低于 sidecar 模式,新项目可优先评估(对标 Cilium 的无 sidecar 卖点)
- 启用 mesh 前必须扫所有 workload 的
excludeOutboundPortsannotation - 基础 Service selector 不能加
version标签限制(会破坏 Istio DR subset 路由)
题目 3:Ingress Controller 选型#
设计需求: 为多租户平台选择 Ingress Controller,需求:HTTPS 终止、基于域名的路由、限流、WAF 集成。
方案对比:
| 维度 | NGINX Ingress | Traefik | Istio Gateway | AWS ALB Controller |
|---|---|---|---|---|
| 协议支持 | HTTP/HTTPS/TCP/UDP | HTTP/HTTPS/TCP/UDP | HTTP/HTTPS/TCP(gRPC) | HTTP/HTTPS |
| 限流 | 注解配置 | 中间件 | EnvoyFilter | WAF 集成 |
| 金丝雀发布 | 注解(canary-by-header) | 中间件 | VS weight routing | ALB target group 权重 |
| 自动证书 | cert-manager 配合 | 内置 ACME | cert-manager 配合 | ACM(AWS 原生) |
| WAF | 需外部 | 需外部 | 需外部 | AWS WAF 原生集成 |
| 适用场景 | 通用场景 | 云原生 + 自动发现 | 已用 Istio 时首选 | AWS 环境最优 |
选择建议:
- NGINX Ingress:通用,社区最成熟,功能全面
- Traefik:追求自动服务发现,配置简洁
- Istio Gateway:已使用 Istio Service Mesh 时的最佳选择(统一管理南北/东西流量)
- AWS ALB Controller:纯 AWS 环境,需要 WAF + ACM + Cognito 集成时最佳
注意事项:
- Gateway API v1 已 GA(
gateway.networking.k8s.io/v1,2023-10),Ingress API 已进入维护冻结(不再加新特性);新平台优先 Gateway API(Gateway + HTTPRoute,角色分离、原生支持流量切分与多协议),Ingress 仅作存量兼容 - 多 Ingress Controller 共存时注意 ingressClassName 隔离
- ALB 每个 Ingress 创建一个 ALB(成本),可用 IngressGroup 合并
题目 4:NetworkPolicy 分层设计#
设计需求: 设计一个覆盖 L3/L4/L7 的 NetworkPolicy 体系,实现生产环境的零信任网络架构。
方案设计:
安全环模型(Defense in Depth):
第一环:集群边界(Ingress Controller)
├── HTTPS 终止 + cert-manager 自动证书
├── Rate Limiting
├── WAF 规则
└── 只允许白名单 path/method
第二环:命名空间边界(NetworkPolicy)
├── 默认拒绝所有跨 namespace 流量
├── 白名单放行特定 namespace/service
└── 禁止 Pod 出公网(通过 egress gateway)
第三环:Pod 边界(NetworkPolicy + mTLS)
├── 只允许声明过的 ingress/egress
├── Istio mTLS STRICT 模式
├── L7 策略:HTTP method + path + header 控制
└── Sidecar 资源限制 egress 范围
第四环:出口控制
├── Egress NetworkPolicy:只允许特定域名
├── Cilium DNS Policy:FQDN 级别出口过滤
├── 出口流量审计(Hubble / Istio access log)
└── 异常流量检测与告警关键 NetworkPolicy 实现:
# 出口限制到特定 FQDN(Cilium 特有)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: egress-whitelist
namespace: production
spec:
endpointSelector:
matchLabels:
app: my-service
egress:
- toFQDNs:
- matchName: "api.trusted-partner.com"
- matchName: "monitoring.internal.com"
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP注意事项:
- 不要一步到位全部 deny(会导致业务中断),按 namespace 逐步收紧
- DNS egress 永远是第一个放行的规则
- 出口 IP 白名单最好配合 egress gateway 或 HTTP proxy
子类 4:CI/CD 与 GitOps 设计#
题目 1:ArgoCD vs FluxCD 选型#
设计需求: 技术团队(20+ 人)需要实现 GitOps,管理 200+ 应用在多集群上的部署。
方案对比(核心差异):
| 维度 | ArgoCD | FluxCD |
|---|---|---|
| 架构 | 单体 API Server + Application Controller | 五控制器解耦(source/kustomize/helm/image/notification) |
| 抽象层 | Application/ApplicationSet | Kustomization + HelmRelease |
| Web UI | 内置丰富 UI | 无(依赖 CLI 或第三方) |
| 多租户 | AppProject + RBAC | 通过 namespace + RBAC 隔离 |
| 多集群 | 每个集群部署 Application Controller 或 Hub-Spoke | 每集群安装 Flux 控制器 |
| 同步模式 | Manual/Auto Sync + Prune | Continuous Reconciliation + Prune |
| 依赖编排 | Sync Waves + Hooks | dependsOn + Health Checks |
| Image Updater | Argocd-image-updater(独立项目) | image-reflector + image-automation(内置) |
| 性能 | 受 Application 数量和 repo-server 影响 | 控制器解耦,水平扩展好 |
| 架构哲学 | Server-First(重 UI) | Controller-First(CRD 即 API) |
选择建议:
- ArgoCD:需要 Web UI 给开发同学看 sync 状态,快速上手,团队 < 50 人
- FluxCD:追求 GitOps 纯粹性(CI 完全无感集群),需要嵌入 Backstage/IDP,多集群场景 50+
- 不建议:两套并用(增加学习成本和运维复杂度)
FluxCD 架构设计(重点):
┌─────────────────────────┐
│ Git / OCI / Bucket │
└───────────┬─────────────┘
│
▼
┌──────────────────────────────┐
│ source-controller │ CRD: GitRepository
│ 拉源码 → 缓存 → artifact │
└───────────┬──────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ kustomize-controller │ │ helm-controller │
│ CRD: Kustomization │ │ CRD: HelmRelease │
│ kustomize build → apply │ │ helm install/upgrade │
└──────────────────────┘ └──────────────────────┘
│ │
└───────────┬───────────────┘
▼
┌───────────────┐
│ Kubernetes │
└───────────────┘FluxCD 关键优势:
- 无中央 Server 单点:每个控制器独立部署,Apiserver 活着就能 reconcile
- CI 完全无感集群:CI 不拿 kubeconfig,只改 git(Pull-based GitOps 的极致)
- 水平扩展自然:每个控制器独立调
--concurrency - 集成简单:上层平台只需
kubectl applyCRD,不需调 ArgoCD API
注意事项:
- Flux 的 Kustomization 依赖 chain(dependsOn)是声明式的,Argo 的 Sync Waves 是数字排序的
- Flux 没有 Application 的聚合状态,需要自己构建 Dashboard(如用 Weave GitOps 或自定义 Grafana)
- 迁移成本:Argo → Flux 需要重写 Application YAML 为 Kustomization + GitRepository
题目 2:Helm vs Kustomize 配置管理#
设计需求: 管理多环境的 K8s 配置,要求:base 模板复用,环境差异通过 overlay 覆盖,配置变更可追溯。
方案对比:
| 维度 | Helm | Kustomize |
|---|---|---|
| 配置方式 | Go template({{ .Values.xxx }}) | YAML Patch(strategic merge / JSON patch) |
| 模板 vs 纯 YAML | 模板语言,不渲染看不到最终效果 | 纯 YAML,可视即可得 |
| 多环境 | 多个 values 文件 | overlay 目录(base + 环境 overlay) |
| 复杂度 | 中(学习 template 语法) | 低(学习 patch 语法) |
| 发布管理 | Helm Release(helm install/upgrade/rollback) | 无原生 release 管理(依赖 GitOps) |
| 与 GitOps 集成 | ArgoCD/Flux 均原生支持 | ArgoCD/Flux 均原生支持(Flux 更推荐 Kustomize) |
选择建议:
- Kustomize + GitOps(推荐):适合 GitOps 模式。配置即代码,base + overlay 模式清晰。kubectl 内置(
kubectl apply -k)。 - Helm + GitOps:社区 chart 生态丰富(如 kube-prometheus-stack),适合使用第三方 chart 的场景。
- 混合模式:用 Helm 消费社区 chart,用 Kustomize 管理自定义配置(ArgoCD 和 Flux 都支持 Helm Chart + Kustomize 混合)
Kustomize Overlay 设计示例:
apps/frontend/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
├── overlays/
│ ├── production/
│ │ ├── kustomization.yaml ← resources: [../../base], patches
│ │ └── replica-count.yaml ← patch: replicas: 5
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── resource-limits.yaml
│ └── qa/
│ ├── kustomization.yaml
│ └── ingress.yaml
└── components/
└── monitoring/
└── kustomization.yaml ← 可复用组件(ServiceMonitor 等)三大高频踩坑(来自生产经验):
- overlay 目录里放了 YAML 但
kustomization.yaml的resources:没列出 → 文件不被部署 - Patch 的
target.name写死导致某些服务 patch 不生效 → overlay 只有一个 Deployment 时只写kind: Deployment - PR 隔离 overlay 给 Service 改了 selector,但基础 Service 的 selector 仍匹配 PR Pod → 流量反向泄漏
题目 3:多环境发布策略设计#
设计需求: 为核心服务设计一套安全发布策略,支持灰度验证和快速回滚。
三种策略对比与选择:
| 维度 | 滚动发布(Rolling Update) | 蓝绿发布(Blue-Green) | 金丝雀发布(Canary) |
|---|---|---|---|
| 资源成本 | 低(复用现有节点) | 高(需双倍资源) | 中(验证节点额外资源) |
| 风险控制 | 中(逐步替换) | 低(可瞬间切回) | 最低(小比例验证) |
| 回滚速度 | 慢(反向滚动,分钟级) | 快(秒级切流量) | 快(调整流量比例) |
| 实现复杂度 | 低(Deployment 原生) | 高(管理两套环境) | 高(流量染色/分割) |
| K8s 实现 | Deployment RollingUpdate | Service selector 切换 / 两套 Deployment | Argo Rollouts / Flagger(底层 Istio VS weight 或 Gateway API 权重) |
| 适用场景 | 无状态、DB 向前兼容 | 需快速验证 | 核心服务、A/B 测试 |
混合策略设计:
发布流程:
1. 金丝雀发布(Canary 5% → 观察 15min)
├── Istio VS weight: baseline=95, canary=5
├── 监控:错误率、延迟 P99、CPU/Memory
└── 异常 → 立即回滚(weight: canary=0)
2. 滚动发布(逐步替换)
├── maxSurge: 1, maxUnavailable: 0(零停机)
├── 新 Pod Ready 后才删旧 Pod
└── ReadinessProbe 必须配置(否则判断不了新 Pod 是否 Ready)
3. 蓝绿切换(关键版本)
├── 部署 Blue(当前)、Green(新版本)两组 Deployment
├── Service selector 初始指向 Blue
├── 验证 Green 健康 → 切 Service selector 到 Green
└── 回滚 → 切回 Blue关键配置(零停机的完整五要素):
apiVersion: apps/v1
kind: Deployment
spec:
# 1. 滚动策略:先起新再删旧,全程不减少可用副本
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多出 1 个 Pod
maxUnavailable: 0 # 发布过程中始终保持所有 Pod 可用
template:
spec:
# 4. 优雅关闭窗口:需 > preStop + 应用退出时间
terminationGracePeriodSeconds: 45
containers:
- name: app
# 2. readinessProbe:未 Ready 不进 Endpoints,避免打到没准备好的 Pod
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
# 3. preStop:先 sleep 让 Endpoints 摘除生效,避免"关闭中仍收到新连接"
lifecycle:
preStop:
exec: { command: ["/bin/sh", "-c", "sleep 10"] }
---
# 5. PDB:滚动发布/节点维护/驱逐时,保证最小可用副本,防止被一次性打空
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
spec:
minAvailable: 80% # 或 maxUnavailable,二选一
selector:
matchLabels: { app: my-app }摘流时序:收到 SIGTERM 时,kubelet 与 Endpoints 摘除是并发的,若不 sleep,旧 Pod 可能在被摘除前就关闭连接 → 客户端 5xx。preStop sleep 覆盖这段传播延迟,之后应用再优雅退出(drain in-flight 请求)。
渐进式发布工具(金丝雀/蓝绿别手搓):
- Argo Rollouts:用
RolloutCR 替代 Deployment,声明式定义 canary steps(setWeight/pause/analysis),配合 AnalysisTemplate 自动读 Prometheus 指标决定继续或回滚。 - Flagger:在原生 Deployment 之上做自动化金丝雀,与 Istio/Linkerd/App Mesh/NGINX/Gateway API 集成,按指标自动加权与回滚。
- 二选一即可,别与手工 Istio VS weight 混用(状态源冲突)。
注意事项:
- 无 ReadinessProbe → 新 Pod 未 Ready 就接收流量 → 滚动发布出现 5xx
- 数据库 Schema 变更必须向前兼容(先加字段不删字段,先双写再切新逻辑)
maxUnavailable: 0+maxSurge: 1的前提是集群有足够资源起额外 Pod- 金丝雀发布需要能区分 baseline 和 canary 的 metrics(如
istio_requests_total{destination_version=~"canary"},注意指标名是istio_requests_total带 s)
验证: 发布过程中持续压测(如 vegeta/hey)观察是否有非 200 响应;kubectl rollout status 应无中断;主动删一个 Pod 验证 PDB 拦截与优雅退出(无连接被重置)。
题目 4:PR 隔离环境设计#
设计需求: 开发者推送 PR 时自动创建隔离的测试环境,PR 合并后自动销毁。要求:PR 环境与基线环境完全隔离,不能相互影响。
方案设计:
PR 环境生命周期:
GitHub PR Open / Push
│
▼
CI Pipeline
│
├── Build Image + Push to Registry
├── 生成 PR Overlay YAML → Commit to GitOps Repo
│ └── Render template from gitops/templates/pr/{project}/{service}/
├── ArgoCD Sync → 创建 PR 环境
│ ├── Namespace: pr-{pr_number}-{service}
│ ├── Deployment: version=pr-{pr_number}-{service}
│ ├── Service: selector: app=pr-{pr_number}-{service}
│ ├── HTTPRoute: host=pr-{pr_number}.qa.example.com
│ └── Istio DR/VS: subset=pr-{pr_number}
└── 通知开发者:http://pr-{pr_number}.qa.example.com
PR Merged / Closed
│
├── 删除 GitOps 中 PR overlay 目录
├── ArgoCD Sync → 自动清理 PR 资源
├── ArgoCD prune → 删除 Namespace + 所有资源
└── 回收 PR 号码(避免 namespace 名冲突)关键设计点(PR 隔离 Checklist):
- Service Selector 完全隔离:PR Pod 的
labels.app必须替换为pr-{pr_number}-{service},确保基础 Service 不匹配 PR Pod - HTTPRoute 隔离:PR 环境使用独立 hostname(
pr-{pr_number}.qa.example.com) - Istio DR/VS 隔离:PR Pod 保留
app=baselabel(被 DR subset 识别),但 Service selector 改为 PR 专用 - 数据库隔离:PR 环境使用独立数据库(或测试数据库的独立 schema)
- 消息队列隔离:PR 环境使用独立 consumer group,防止消费生产消息
- 资源限制:PR Namespace 配 ResourceQuota(如 max 2CPU/4Gi),防止 PR 环境耗尽集群资源
- 自动清理:PR 关闭后定时任务自动清理,防止资源泄漏
注意事项(来自生产事故):
- PR overlay 模板结构变化时,必须同步重渲染存量 overlay(否则旧 PR 下次推送 SyncFailed)
- 基础 Service selector 加
version=baseline会破坏 Istio DR subset 路由(PR Pod 不进 endpoints → 503) - Kustomize PR overlay 中 Deployment selector 变化会触发 immutable 错误(selector 不可变)
子类 5:可观测与告警体系设计#
题目 1:Prometheus + Grafana 监控架构设计#
设计需求: 为多集群(3 集群 × 100 服务)设计监控体系。需支持:指标采集、长期存储、Dashboard、告警。
方案设计:
多集群监控架构(Push 模式):
┌──────────────────────────────────────────────────┐
│ 中央存储:VictoriaMetrics / Thanos │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ VM Cluster (vminsert → vmstorage → vmselect) │ │
│ │ - 压缩率高(比 Prometheus 省 7 倍存储) │ │
│ │ - 支持多租户 │ │
│ └─────────────────────────────────────────────┘ │
│ ▲ │
│ │ remote_write │
│ ┌─────────┴──────────┐ │
│ │ Thanos Receive │ │
│ │ (备选方案) │ │
│ └────────────────────┘ │
└──────────────────────────────────────────────────┘
▲ ▲
│ remote_write │ remote_write
┌──────┴──────┐ ┌───────┴──────┐
│ Cluster A │ │ Cluster B │
│ Prometheus │ │ Prometheus │
│ (15d local) │ │ (15d local) │
└─────────────┘ └──────────────┘分层设计:
# 第一层:数据采集
---
- Prometheus Operator + ServiceMonitor(自动发现)
- kube-prometheus-stack(一站式部署 Prometheus + Grafana + Alertmanager)
- Node Exporter(DaemonSet,节点指标)
- kube-state-metrics(Deployment、Pod、PVC 等资源状态)
# 第二层:指标聚合
---
- Prometheus per cluster(自建,15 天本地保留)
- remote_write → VictoriaMetrics 集群(1 年保留)
- Recording Rules 预计算常用 PromQL(减少查询延迟)
# 第三层:可视化
---
- Grafana(统一 Dashboard,数据源指向 VictoriaMetrics)
- 集群概览 Dashboard
- 业务 SLO Dashboard
- 成本分析 Dashboard
- Grafana Alerting(替代 Alertmanager 的 UI 管理)
# 第四层:告警
---
- PrometheusRule → Alertmanager
- Alertmanager 路由: 按 severity / team 分发到不同钉钉群
- VictoriaMetrics vmalert(作为备份评估引擎)监控覆盖四层(采集目标,别只盯节点):
| 层级 | 数据源 | 核心指标 |
|---|---|---|
| 节点层 | node-exporter(DaemonSet) | node_cpu_seconds_total、node_memory_MemAvailable_bytes、node_filesystem_avail_bytes |
| 容器层 | cAdvisor(kubelet 内置) | container_cpu_usage_seconds_total、container_memory_working_set_bytes、OOM/重启 |
| K8s 资源层 | kube-state-metrics | kube_deployment_status_replicas、kube_pod_status_phase、kube_node_status_condition |
| 应用层 | ServiceMonitor(自定义 /metrics) | QPS、延迟 P50/P99、错误率 |
关键组件选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 指标采集 | kube-prometheus-stack | K8s 原生,CRD 管理,社区活跃 |
| 长期存储 | VictoriaMetrics | 压缩率高(7:1),PromQL 兼容 |
| 可视化 | Grafana | 事实标准,丰富的 Dashboard 生态 |
| 日志 | Loki | 索引少、成本低,与 Prometheus/Labels 兼容 |
| 链路追踪 | Tempo | 与 Loki/Prometheus 统一标签体系 |
注意事项:
- Prometheus Remote Write 需要评估网络带宽(每样本约 20 bytes × samples_rate)
- VictoriaMetrics 单机上限约 100 万 samples/sec,超出需用集群模式
- Recording Rules 必须预计算(否则 Grafana 查询卡死)
题目 2:SLO/SLI 体系与 Error Budget 告警设计#
设计需求: 为 3 个核心服务定义 SLO(99.9% 可用性),设计 Error Budget 消耗告警。
方案设计:
1. SLI 指标选取(用户视角):
| 服务 | SLI 类型 | 指标 | 目标 |
|---|---|---|---|
| API Gateway | 可用性 | HTTP 2xx/3xx 比例 | 99.9%(月) |
| API Gateway | 延迟 | P99 响应时间 | < 500ms |
| 订单服务 | 可用性 | gRPC OK 比例 | 99.95%(月) |
| 支付服务 | 延迟 | P99 响应时间 | < 200ms |
2. Recording Rules(Prometheus):
# 可用性 SLI(5 分钟窗口)
- record: job:http_requests:success_rate5m
expr: |
sum(rate(http_requests_total{status=~"2..|3.."}[5m])) by (job)
/
sum(rate(http_requests_total[5m])) by (job)
# Error Budget Burn Rate(1h / 6h / 3d 多窗口)
- record: job:http_requests:error_budget_burn_rate1h
expr: |
(
1 - sum(rate(http_requests_total{status=~"2..|3.."}[1h])) by (job)
/ sum(rate(http_requests_total[1h])) by (job)
)
/ (1 - 0.999) # 除以 (1 - SLO),SLO=99.9%
# Error Budget 剩余百分比(30 天窗口)
- record: job:http_requests:error_budget_remaining30d
expr: |
1 - (
(1 - sum(rate(http_requests_total{status=~"2..|3.."}[30d])) by (job)
/ sum(rate(http_requests_total[30d])) by (job))
/ (1 - 0.999)
)3. 多窗口 Burn Rate 告警(Google SRE 推荐):
# P1:快速消耗(2% Error Budget 在 1 小时内烧完 → burn rate > 14.4)
- alert: SLOBurnRateCritical
expr: |
job:error_budget_burn_rate1h > 14.4
AND
job:error_budget_burn_rate5m > 14.4
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.job }} Error Budget 快速消耗"
# burn rate 14.4 = 在1小时内消耗了 2% 的月度 Error Budget(14.4×1h/720h≈2%);6h 窗口 burn rate 6 → 5%,3d 窗口 burn rate 1 → 10%
# P2:中速消耗(5% Error Budget 在 6 小时内 → burn rate > 6)
- alert: SLOBurnRateHigh
expr: |
job:error_budget_burn_rate6h > 6
AND
job:error_budget_burn_rate30m > 6
for: 15m
labels:
severity: warning
# P3:慢速消耗(10% Error Budget 在 3 天内 → burn rate > 1)
- alert: SLOBurnRateLow
expr: |
job:error_budget_burn_rate3d > 1
AND
job:error_budget_burn_rate6h > 1
for: 1h
labels:
severity: info多窗口交叉逻辑:
- 长窗口(1h/6h/3d)→ 检测持续性问题,过滤短暂毛刺
- 短窗口(5m/30m)→ 确认问题当前正在发生,过滤历史遗留的"尾巴"
- 两个窗口 AND → 必须是"持续且当前活跃"的故障才告警
Error Budget 策略:
- 消耗 > 50% → 冻结非紧急发布,只允许修复性发布
- 消耗 > 80% → 所有发布暂停,SRE review 必须
- 消耗 100% → 触发 SLO 违规复盘,改进措施必须落地
推进 SLO 文化的 3 阶段:
第一阶段(1-2 月):只观察不告警
├── 建立 Dashboard,让团队熟悉指标含义
└── 修正 SLI 计算逻辑
第二阶段(3-4 月):只 P1 告警
├── 每月 Error Budget 回顾会议
└── 建立 SLO 违规复盘流程
第三阶段(5 月+):发布决策集成
├── Error Budget 冻结策略
├── 发布前评估预算余量
└── 每季度调整 SLO 目标题目 3:日志采集方案选型 — EFK vs Loki#
设计需求: 为多集群设计日志采集方案,日均日志量 500GB,保留 30 天,需要全文检索。
方案对比:
| 维度 | EFK (Elasticsearch + Fluentd + Kibana) | Loki + Promtail + Grafana |
|---|---|---|
| 索引方式 | 全文索引(高成本) | 标签索引(低成本) |
| 存储成本 | 高(日志体积 × 1.5-2) | 低(日志体积 × 0.3-0.5) |
| 全文检索 | 强(Kibana Query DSL) | 弱(LogQL,标签过滤 + 内容 grep) |
| 运维成本 | 高(ES 集群需要调优) | 低(依赖对象存储) |
| 与 Prometheus 集成 | 无 | 同标签体系,Grafana 无缝关联 |
选择建议:
- Loki + Promtail(推荐):K8s 原生场景,日志量不大,不需要复杂全文检索。标签与 Prometheus 统一,Grafana 里 Metric → Log 一键跳转。
- EFK:需要复杂全文检索、聚合分析、复杂 Dashboard(如安全审计)
- 混合:JVM/应用日志 走 Loki,安全审计日志走 EFK
Loki 架构设计:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Pod A │ │ Pod B │ │ Pod C │
│ stdout │ │ stdout │ │ stdout │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ Promtail (DaemonSet) │
│ - 添加 K8s 标签(namespace/pod/container) │
│ - 推送日志到 Loki Distributor │
└─────────────────────┬────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ Loki (Distributed) │
│ Distributor → Ingester → Object Store │
│ (S3/GCS/OSS 做长期存储,低成本) │
└──────────────────────────┬───────────────┘
│
▼
┌──────────────┐
│ Grafana │ LogQL 查询 + 与 Metric 关联
└──────────────┘题目 4:Alertmanager 路由与抑制规则设计#
设计需求: 统一管理多集群 Alertmanager 告警路由,实现告警分级分发、去重抑制、静默管理。
方案设计(基于实际生产经验):
Alertmanager 架构:
┌─────────────┐
│ Prometheus │ Pro集群
└──────┬──────┘
│ alerts
▼
┌─────────────┐
Webhook Bridge │Alertmanager │ ← 唯一收敛点
(业务侧告警) ──→│ (主实例) │
│
┌────────────┴────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Watchdog? │ │ Routing: │
│ → HC.io │ │ severity → │
└─────────────┘ │ team │
└──────┬───────┘
│
┌────────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ DingTalk Prod│ │DingTalk Staging│ │ Feishu P0 │
└──────────────┘ └──────────────┘ └──────────────┘路由规则:
# Alertmanager 路由配置
route:
receiver: webhook-dingtalk-prod # 默认接收器
group_by: ['alertname', 'cluster', 'namespace']
group_wait: 30s # 同组告警聚合等待
group_interval: 5m # 同组告警重复间隔
repeat_interval: 4h # 未恢复告警重发间隔(太长导致遗漏,太短导致噪音)
routes:
# Watchdog 独立通道(永远排除在 silence 之外)
- matchers:
- alertname = "Watchdog"
receiver: dead-mans-switch
group_wait: 10s
continue: false
# P0 → 飞书 On-call 群(带 @所有人)
- matchers:
- severity = "P0"
receiver: feishu-oncall
group_wait: 10s
repeat_interval: 15m
continue: true
# Staging/QA → 对应钉钉群
- matchers:
- env =~ "staging|qa"
receiver: webhook-dingtalk-staging
continue: false
# 邮件备份(P0/P1)
- matchers:
- severity =~ "P0|P1"
receiver: email-fallback
continue: true抑制规则(Inhibit Rules):
inhibit_rules:
# 集群级故障抑制具体节点/Pod 告警
- source_matchers:
- alertname = "KubeAPIDown"
target_matchers:
- severity =~ "warning|info"
equal: ['cluster']
# 节点 NotReady 抑制其上 Pod 的 Restart/Pending 告警
- source_matchers:
- alertname = "NodeNotReady"
target_matchers:
- alertname =~ "Pod.*Restart|Pod.*Pending"
equal: ['cluster', 'instance']
# 高 severity 抑制低 severity 同名告警
- source_matchers:
- severity = "P0"
target_matchers:
- severity =~ "P1|P2"
equal: ['alertname', 'cluster']Silence 规范(来自生产事故教训):
# 绝对禁止:alertname=~".+"(会误伤 Watchdog 心跳)
# 正确姿势:显式排除 Watchdog
# 安全 silence 脚本
amtool silence add \
alertname!=Watchdog \
--comment="上线窗口 silence(排除 Watchdog)" \
--duration=2hWatchdog 反向告警设计:
- 一条
alert: Watchdog规则永远 firing(expr: vector(1)) - 走独立 receiver 推 healthchecks.io
- 30s 无心跳 → healthchecks.io 触发反向告警到独立钉钉群
- 主告警群 和 反向告警群 必须分开(不同群/不同机器人)
注意事项:
- 钉钉机器人关键词必须覆盖 firing "告警" 和 resolved "恢复" 两类消息
- Silence 用
isEqual: false负匹配排除 Watchdog - 告警适配器选 config-as-data 而不是 config-as-URL(避免 URL encode 双重转义)
子类 6:成本治理与资源规划#
题目 1:FinOps 三阶段体系设计#
设计需求: 某公司 K8s 月账单 $52k,需建立 FinOps 体系统一管理成本。要求:成本可观测、可分摊、可优化。
方案设计(基于实际案例):
FinOps 三阶段:
阶段 1: Inform(看清花费)— 不可跳过
├── 建立标签体系(Label Schema)
├── 部署 OpenCost / Kubecost
├── 强制标签准入(Kyverno Policy)
└── Grafana 成本 Dashboard
阶段 2: Optimize(优化浪费)— 按 ROI 排序
├── 闲置资源识别(VPA Recommender)
├── 清理孤儿 PVC(无 Pod 使用的 EBS 卷)
├── 启用 Karpenter Consolidation
├── Spot 节点混合策略
└── 批量降低虚高 requests
阶段 3: Operate(固化流程)— 防止反弹
├── CI 集成成本检查(PR 改 resources → 自动估算月度影响)
├── 周度/月度自动报告
├── 超预算自动告警
└── 季度 FinOps Review(各团队 Owner 负责成本趋势)强制标签体系:
# 必须标签(Kyverno ClusterPolicy 强制校验)
required_labels:
- app.kubernetes.io/name # 服务名
- team # 负责团队
- env # 环境
- cost-center # 成本中心编号关键设计:
- 先 Inform 后 Optimize:跳过第一步直接优化 = 优化了也不知道效果
- 先 Showback 后 Chargeback:先给团队看成本(不扣预算),建立意识后再真扣
- 闲置成本分摊:按实际使用比例分摊闲置资源(鼓励团队写准确 requests)
题目 2:资源 Request 优化策略#
设计需求: 集群中 60% Deployment 的 CPU request 远高于实际用量(CPU 实际用 200m 但 request 写了 2C),导致节点数过多、成本浪费严重。
优化方案:
1. 部署 VPA(仅 Recommender 模式,不自动更新):
helm install vpa fairwinds-stable/vpa \
--namespace vpa --create-namespace \
--set updater.enabled=false \ # 不自动重启 Pod
--set admissionController.enabled=false \
--set recommender.enabled=true # 只跑推荐2. 批量生成推荐报告:
# 找出 requests 虚高的 Deployment(用量 < 20% requests)
kubectl get vpa -A -o json | jq -r '
.items[] |
select(.status.recommendation != null) |
.metadata.namespace as $ns |
.metadata.name as $name |
.status.recommendation.containerRecommendations[]? |
{ns: $ns, name: $name, container: .containerName,
cpu_recommended: .lowerBound.cpu,
cpu_current: (.target.cpu // "N/A"),
mem_recommended: .lowerBound.memory}
' | jq -s 'sort_by(.cpu_recommended)'3. 用 Goldilocks 可视化:
helm install goldilocks fairwinds-stable/goldilocks
kubectl label namespace production goldilocks.fairwinds.com/enabled=true
# Dashboard 直接展示推荐值 + 采纳后节省金额4. 发给各团队 PR 降 requests:
不自己改 → 让团队 Owner 根据推荐报告提 PR 改 resources。配合 Infracost 估算月度节省金额(增加说服力)。
题目 3:Spot/On-Demand 混合策略设计#
设计需求: 降低 EC2 节点成本 40%,同时保证生产环境稳定性。
方案设计:
Karpenter NodePool 混合配置:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"] # 优先 Spot,没有时 fallback On-Demand
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"] # 多规格可选提升 Spot 可用性
expireAfter: 720h # 节点最多跑 30 天(定期轮换)
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized # v1 合法取值:WhenEmpty | WhenEmptyOrUnderutilized(旧的 WhenUnderutilized 已废弃)
consolidateAfter: 30s
budgets:
- nodes: "10%" # 每次中断不超过 10% 节点,限制爆炸半径分层策略:
第一层:无状态服务
├── 全量 Spot
├── 配置 PDB(minAvailable: 1)
├── 安装 Node Termination Handler
└── 容忍 2% 中断率
第二层:有状态服务
├── 全部 On-Demand
├── annotation: karpenter.sh/do-not-disrupt: "true"
└── 0% 容忍中断
第三层:批处理/后台任务
├── Spot
├── 无 PDB(中断后自动重试)
└── 优先使用 cheapest instanceSpot 中断处理:
- 部署 AWS Node Termination Handler(提前 2 分钟收到中断通知)
- 收到通知 → cordon + drain 节点 → Karpenter 自动创建新节点 → Pod 在新节点启动
- PDB 保护关键服务(至少保留 1 个副本)
题目 4:Karpenter vs Cluster Autoscaler 选型#
设计需求: 选择 K8s 集群节点自动伸缩方案。
方案对比:
| 维度 | Cluster Autoscaler (CA) | Karpenter |
|---|---|---|
| 扩缩速度 | 分钟级 | 秒级(直接调用 EC2 API,不经过 ASG) |
| 节点选择 | 固定 NodeGroup/ASG | 动态选择最优实例(规格/价格/AZ) |
| 碎片整理 | 不支持 | Consolidation(主动合并负载到更少节点) |
| Spot 支持 | 基本支持 | 原生优先级 + 多 fallback |
| 容量预留 | 无 | 支持(capacityReservation) |
| 复杂度 | 简单(与 ASG 集成) | 中(需学习 NodePool + NodeClass) |
| 适用场景 | 传统 ASG 模式 | 追求极致利用率和成本优化 |
选择建议:
- CA:已有 ASG 体系、节点规格固定、团队 < 5 人的中小集群
- Karpenter:追求成本优化、需要 Consolidation、节点规格灵活的集群
- AWS EKS 环境优先用 Karpenter(原生集成,更快更省)
多 NodePool 分工设计(按工作负载画像隔离节点池):
# 示意:不同负载用不同 NodePool,各自定制实例族与合并策略
# CPU 密集:instance-category=["c"],consolidationPolicy=WhenEmptyOrUnderutilized
# GPU 推理:instance-category=["g","p"],consolidationPolicy=WhenEmpty(加载模型贵,不轻易合并)
# 每个 NodePool 设 limits(cpu / nvidia.com/gpu)作硬上限,防误配导致成本失控Pod 用 nodeSelector: karpenter.sh/nodepool: <name> + 对应 toleration 落到指定池。
CA 场景的碎片整理:CA 本身不做 Consolidation,需配 Descheduler(LowNodeUtilization 插件)驱逐低利用率节点上的 Pod → CA 回收空节点。Karpenter 则原生做这件事(WhenEmptyOrUnderutilized,且尊重 PDB,比手动 drain 安全)。
题目 5:OpenCost 部署与多团队费用分摊#
设计需求: 部署 OpenCost 实现 K8s 成本分摊,各团队按实际用量承担成本。
部署方案:
# 安装 OpenCost(接入已有 Prometheus)
helm install opencost opencost/opencost \
--namespace opencost --create-namespace \
--set opencost.prometheus.external.enabled=true \
--set opencost.prometheus.external.url=http://prometheus.monitoring:9090分摊模型设计:
| 成本类型 | 分摊方式 |
|---|---|
| 直接分配成本 | Pod 独占的 CPU/Memory → 按 team label 直接归属 |
| 共享基础设施 | kube-system/monitoring/ingress → 按各团队请求比例分摊 |
| 闲置成本 | 节点买了但没用的部分 → 按各团队实际使用比例分摊(激励写准确 requests) |
| 集群管理费用 | EKS 控制面费 / LB 费 → 按团队请求比例分摊 |
成本查询 API:
# 按 team label 分摊的成本
curl -s "http://opencost.opencost:9003/allocation" \
-d "window=lastmonth" \
-d "aggregate=label:team" \
-d "shareIdle=true" \
-d "shareTenancyCosts=true" | \
jq '.data[0] | to_entries[] | {team: .key, cost: (.value.totalCost | floor)}'告警规则:
# 月度成本预测超预算
- alert: MonthlyCostProjectionExceeded
expr: (sum(increase(opencost_total_cost[24h])) * 30) > 45000
for: 1h
labels:
severity: warning
annotations:
summary: "月度成本预测 ${{ $value | printf \"%.0f\" }} 超预算"
# Namespace 成本异常翻倍(对比上周同期)
- alert: NamespaceCostAnomaly
expr: |
(sum by (namespace) (increase(opencost_total_cost[1h]))
/
sum by (namespace) (increase(opencost_total_cost[1h] offset 7d))) > 2
for: 30m题目 6:闲置资源巡检与治理#
设计需求: 建立周期性的闲置资源巡检机制,定期清理僵尸资源,防止成本反弹。
巡检项目清单:
| 巡检项 | 脚本/命令 | 频率 | 清理策略 |
|---|---|---|---|
| 孤儿 PVC | 查找无 Pod 引用的 Bound PVC | 每周 | 人工确认后删除 |
| 未使用 ConfigMap/Secret | 查找无引用者 | 每周 | 人工确认后删除 |
| 旧镜像/快照 | crictl rmi --prune / 云厂商快照 API | 每周 | 删除 30 天前的快照 |
| 闲置 ELB/ALB | AWS CLI 查找无 target 的 LB | 每月 | 确认后删除 |
| 未关联 EIP | 云厂商 API | 每月 | 释放 |
| 旧 EBS 快照 | 超过保留期限的快照 | 每月 | 删除 |
孤儿 PVC 巡检脚本(CronJob):
#!/bin/bash
# find-orphan-pvc.sh
kubectl get pvc -A -o json | jq -r '
.items[] | select(.status.phase == "Bound") |
"\(.metadata.namespace)/\(.metadata.name)"
' | while IFS='/' read ns pvc; do
count=$(kubectl get pods -n "$ns" -o json | jq --arg pvc "$pvc" \
'[.items[].spec.volumes[]? | select(.persistentVolumeClaim.claimName == $pvc)] | length')
[ "$count" -eq 0 ] && echo "ORPHAN PVC: $ns/$pvc"
done防止反弹机制:
- CI 集成成本检查:PR 改 resources → Infracost 自动估算月度影响
- 季度 FinOps Review:各团队 Owner 对 namespace 成本趋势负责
- Namespace 预算 Quota:ResourceQuota 设置 CPU/Memory 上限,超标 Pod 调度失败
- 周度成本自动报告:每周一 CronJob 跑,各 namespace 成本发到团队 Slack 频道
实际案例数据($52k → $31k):
| 优化措施 | 节省 | 投入 |
|---|---|---|
| 清理孤儿 PVC(68 个, 3.8TB) | $1,200/月 | 1 天 |
| 配置 VPC Endpoint(消除 NAT 流量费) | $3,800/月 | 1 天 |
| 关停开发环境夜间/周末节点 | $3,000/月 | 半天 |
| Karpenter Consolidation + Spot | $7,000/月 | 1 周 |
| 批量按 VPA 推荐降 Requests | $6,000/月 | 2 周集中 Sprint |
| 合计 | $21,000/月 |
常见措施降本幅度参考(估算,因负载而异):
| 优化措施 | 预期降本 | 实施复杂度 |
|---|---|---|
| 资源 requests 优化(VPA Recommender) | 20-40% | 中 |
| Spot 实例(非关键服务) | 50-70% | 中 |
| 节点合并(Karpenter Consolidation) | 15-25% | 低 |
| 定时伸缩(KEDA cron / 关停非工作时段) | 30-50%(非工作时间) | 低 |
| 存储卷降级(gp3 替代 io1、清理孤儿卷) | 20-40% | 低 |
子类 7:存储、弹性与安全设计#
题目 1:有状态服务存储方案设计(StatefulSet + StorageClass + PVC 保留策略)#
设计需求: 为一个 3 副本的数据库类有状态服务设计存储方案,要求:每副本独占持久卷、Pod 重建后数据不丢、扩缩容与删除时卷的生命周期可控。
架构与关键设计点:
- StorageClass 定义供给策略(动态供给 + 允许扩容 + 拓扑感知):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # 云上用 CSI driver(in-tree 卷插件已迁移到 CSI)
parameters:
type: gp3
iops: "6000"
allowVolumeExpansion: true # 允许在线扩容(配合 CSI + 文件系统 resize)
volumeBindingMode: WaitForFirstConsumer # 延迟绑定,保证卷与消费它的 Pod 落在同一 AZ
reclaimPolicy: Retain # PV 回收策略:Retain 防误删丢数据StatefulSet 用
volumeClaimTemplates:每个副本自动生成data-<sts>-<ordinal>独占 PVC,Pod 重建后按序号重新绑定同一卷(稳定网络标识 + 稳定存储)。PVC 保留策略(
persistentVolumeClaimRetentionPolicy,1.32 GA):
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # 删除 StatefulSet 时保留 PVC(数据安全)
whenScaled: Delete # 缩容时删除多余副本的 PVC(避免孤儿卷积累成本)(1.32 之前删除 StatefulSet 会留下孤儿 PVC 需手动清理;GA 后可声明式控制。)
取舍:
WaitForFirstConsumervsImmediate:前者避免"卷在 AZ-a、Pod 被调度到 AZ-b 挂不上",代价是绑定稍慢。跨 AZ 块存储(EBS/云盘)无法 Multi-Attach,必须拓扑对齐。reclaimPolicy: RetainvsDelete:Retain 安全但会留下需人工回收的 Released PV;Delete 省心但误删即丢数据。数据库类一律 Retain。- 块存储(RWO、低延迟、独占)vs 文件存储(RWX、可共享、延迟高):数据库选块存储;多 Pod 共享读写才用 CephFS/NFS/EFS。
验证:
- 删 Pod 后
kubectl get pvc卷仍在、数据不丢;扩容 PVC 后容器内df -h文件系统已增大。 - 演练
kubectl delete sts --cascade=orphan,验证 PVC 是否按whenDeleted策略保留。
治理: 孤儿 PVC / Released PV 纳入周度巡检(见成本治理);StorageClass 分级(fast-ssd / standard / archive),用 ResourceQuota 的 requests.storage 限制各租户用量。
题目 2:弹性伸缩体系设计(HPA v2 + KEDA + VPA + Karpenter 分工)#
设计需求: 一个集群含 Web 服务(按 QPS/CPU 伸缩)、消息消费者(按队列积压伸缩、闲时缩到 0)、Java 常驻服务(内存难估),设计一套完整弹性方案。
四层弹性分工(关键:各管一维,避免打架):
| 层次 | 组件 | 伸缩对象 | 触发信号 |
|---|---|---|---|
| Pod 水平 | HPA(autoscaling/v2) | 副本数 | CPU/内存/自定义指标(QPS、P99) |
| 事件驱动 | KEDA | 副本数(含 scale-to-zero) | 队列长度、Kafka lag、cron 等 30+ scaler |
| Pod 垂直 | VPA | requests/limits | 历史用量推荐 |
| 节点 | Karpenter / CA | 节点数与规格 | 待调度 Pod |
关键设计点:
- HPA v2 支持多指标取 max,结合
behavior控制扩缩速率(防抖):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 缩容前观察 5min,避免抖动KEDA scale-to-zero:消费者闲时缩到 0 副本,来消息时 KEDA 按 activation 阈值拉起(原生 HPA 最小只能到 1,KEDA 补足 0↔1 这一段)。
VPA 与 HPA 不并用于同一资源维度:VPA Auto 模式改 CPU/内存 requests,会与基于 CPU 的 HPA 互相打架(VPA 改 request 直接影响 HPA 的利用率计算)。规避:VPA 只用 Recommender 模式出建议、人工/CI 采纳;或 VPA 管内存、HPA 管 CPU,各行其道。
取舍: 可预测的周期负载(如每天 9 点高峰)用定时扩容(KEDA cron scaler / 预热)比纯响应式 HPA 更平滑;突发流量靠 HPA + 秒级扩节点的 Karpenter。
验证: 压测打到阈值观察副本上涨、kubectl describe hpa 看 metrics 与 events;消息堆积验证 0→N,消费完回落到 0。
治理: maxReplicas 必须有上限,防止指标异常打爆集群;HPA 依赖 metrics-server / Prometheus Adapter,监控其可用性(不可用时 HPA TARGETS 显示 <unknown>、停止伸缩)。
题目 3:集群安全基线设计(RBAC 最小权限 + PSA + Secret 静态加密)#
设计需求: 为生产集群设计安全基线,覆盖:人与服务的最小权限、工作负载的安全准入、敏感数据在 etcd 中的加密。
三层安全设计:
RBAC 最小权限:
- 按 namespace 授
Role/RoleBinding,跨集群只读才用ClusterRole;禁止随手cluster-admin。 - ServiceAccount 默认不自动挂 token(
automountServiceAccountToken: false),按需显式挂载;用kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa>审计实际权限。 - 特权 verb(
escalate、bind、impersonate、*)单独评审。
- 按 namespace 授
PSA(Pod Security Admission,1.25 GA,取代已移除的 PodSecurityPolicy):
# namespace 打标签即启用三档策略(privileged / baseline / restricted)
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted # 强制拦截
pod-security.kubernetes.io/audit: restricted # 审计日志
pod-security.kubernetes.io/warn: restricted # 客户端告警restricted要求 runAsNonRoot、drop ALL capabilities、seccompProfile=RuntimeDefault、禁 hostNetwork/hostPID/privileged。- 镜像来源、签名校验等 PSA 覆盖不到的维度,用 Kyverno / OPA Gatekeeper 补充。
- Secret 静态加密(默认不加密!):
- K8s 默认 Secret 在 etcd 中仅 base64 编码,并非加密。需在 API Server 配置
EncryptionConfiguration(apiserver.config.k8s.io/v1)手动开启:
- K8s 默认 Secret 在 etcd 中仅 base64 编码,并非加密。需在 API Server 配置
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: ["secrets"]
providers:
- kms: # 生产首选 KMS v2(1.29 GA),密钥托管在外部 KMS、支持轮转
apiVersion: v2
name: aws-kms
endpoint: unix:///var/run/kmsplugin/socket.sock
- identity: {} # 兜底:允许读取历史未加密数据- 开启后需
kubectl get secrets -A -o json | kubectl replace -f -重写存量 Secret,使其落盘加密。 - 更进一步:敏感凭据用外部密钥系统(Vault / External Secrets Operator)托管,K8s 内只存引用。
取舍: identity provider 放第一位=不加密;放最后=加密写、兼容读旧数据。KMS v2 比静态 aescbc 密钥更安全(密钥不落配置文件、可轮转),但引入 KMS 依赖(KMS 挂了 API Server 无法解密 Secret)。
验证: 直接从 etcd 读取校验密文:etcdctl get /registry/secrets/<ns>/<name> 应看到 k8s:enc:kms:v2: 前缀而非明文。
治理: 定期轮转 KMS 密钥并重写 Secret;RBAC 与 PSA 策略纳入 GitOps 版本管理;准入策略变更走审计与评审。
附录:面试答题框架#
- 方案题回答结构:明确约束条件 → 架构/分层 → 关键设计点(YAML/命令示例)→ 取舍 → 验证 → 治理与风险
- 不要直接给方案:先问清楚规模(集群数/服务数/Pod 数)、SLA 要求、已有技术栈、团队规模
- 展示取舍意识:每个方案都有代价,能说出"为什么选 A 不选 B"比直接给方案更有说服力
- 踩坑意识:补充 1-2 个真实的坑点/注意事项,体现生产经验
- 量化思维:成本题和性能题中引用数字(如"合并后可降低 15-25% 节点数")
- API 版本要跟上:别用废弃组(
autoscaling/v2beta*、extensions/v1beta1Ingress、PSP);新场景优先 Gateway API、PSA、Karpenterv1、KMS v2(详见开头速查)
注:可观测/成本/存储/RBAC/PSA 等的排查型题目见同目录
4-场景排查型.md,本文件只收"方案设计",两者不重复。