← 返回题库 / 大纲

云原生与 Kubernetes

第 81 题:K8s 部署与 OOMKilled 治理 K8s

【真实企业业务场景】
订单 Pod 频繁 OOMKilled(exit 137),但 JVM 堆只设 2G,容器 limit 也 2G,加上元空间/栈/堆外后超 limit 被 kill。要求:正确设置 JVM 与容器内存关系。
【面试官问题】
    1. 为什么 OOMKilled?2. JVM 与 limit 关系?3. 怎么设才不 kill?4. 监控?5. 压测验证?
【候选人的标准回答】

第一步:分析。OOMKilled 是容器内存超 limit 被 cgroup kill,不是 JVM heap OOM。JVM 总内存=堆+元空间+栈+堆外,常超容器 limit。

第二步:挑战。内存账、limit 设置、监控。

第三步:架构。内存账:limit ≥ Xmx + MaxMetaspace + 线程栈×线程数 + 堆外(Netty)+ 系统;②设置:Xmx 留余量(如 limit 4G → Xmx 2.5G);③探针:liveness/readiness;④监控:容器内存使用率 + JVM 各区;⑤压测:验证峰值不超 limit。

第四步:选型。K8s resources.limits + JVM 参数 + 监控(cAdvisor/Prometheus)。

第五步:一致性。内存设置不影响语义;降 OOM 保稳定。

第六步:高可用。OOMKilled 后 Pod 重启;多副本防单点。

第七步:优化。用 UseContainerSupport(JDK 8u191+ 默认识别容器);限池大小。

【架构设计】
limit ≥ Xmx + Metaspace + 栈×线程 + 堆外 + 系统 Xmx 留余量; JDK识别容器; 监控内存+压测
【技术方案深度解析】

为什么 kill:cgroup limit 是硬上限,JVM 不知堆外/栈也算,总内存超 limit 即被 kill(137)。内存账:limit 必须覆盖全部,不能只等于 Xmx。UseContainerSupport:新版 JVM 自动读容器 limit 作为可用内存,避免按宿主机算错。最佳实践:Xmx ≈ limit×0.7~0.8,留其他区。

【关键技术点】
OOMKilledcgroupresources.limitsUseContainerSupport内存账JVM参数
【Java 实现示例】
// 容器 limit=4G, JVM 合理设置
-Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m -Xss256k
-XX:+UseContainerSupport -XX:MaxDirectMemorySize=512m
// K8s deployment
resources: limits: {memory: 4Gi, cpu: 2} requests: {memory: 4Gi, cpu: 1}
// 监控: container_memory_working_set_bytes 告警
【面试官可能继续追问】
【常见错误回答】
  • ❌ Xmx=limit——其他区超被 kill。正确:Xmx 留余量。
  • ❌ 不识容器——按宿主机算。正确:UseContainerSupport。
  • ❌ 只盯 heap——漏堆外/栈。正确:全内存账。
【架构师评分标准】
初级 0~40
不知 cgroup。
中级 40~60
知 limit,不论内存账。
高级 60~80
内存账+留余量+容器支持+监控。
架构师 80~100
再加:①QoS/Guranteed;②堆外(NMT);③requests 策略;④压测验证闭环。

第 82 题:HPA 自动扩容与容量弹性 HPA

【真实企业业务场景】
大促流量从 1 万涨到 10 万 QPS,但 Pod 数固定 10,CPU 100% 接口超时;手动扩又慢又贵。要求:设计 HPA 自动扩容,且避免震荡。
【面试官问题】
    1. HPA 原理?2. 基于什么指标?3. 震荡怎么防?4. 扩容上限?5. 扩容后仍慢?
【候选人的标准回答】

第一步:分析。HPA 按指标自动调副本数,实现弹性。需防震荡、设上限、配合理指标。

第二步:挑战。指标选择、震荡、上限、依赖。

第三步:架构。指标:CPU/内存(基础)或 QPS/自定义(更准,Prometheus Adapter);②防震荡:stabilizationWindow(冷却)、合理阈值;③上限:maxReplicas 防无限扩(成本);④依赖:扩容后 DB/Redis 能力需跟上(否则扩了也崩);⑤预案:大促前预扩 + HPA 保底。

第四步:选型。HPA v2 + Prometheus Adapter(自定义指标)+ KEDA(事件驱动)。

第五步:一致性。扩容无状态服务,实例间无状态一致。

第六步:高可用。弹性防过载;上限防成本爆炸。

第七步:优化。预扩(大促)+ HPA(突发);指标驱动。

【架构设计】
指标(CPU/QPS) → HPA → 调副本(含冷却/上限) 依赖: DB/Redis 能力需匹配; 大促预扩+HPA保底
【技术方案深度解析】

指标选择:CPU 通用但滞后(GC 也吃 CPU);QPS/RT 更贴近业务,用 Prometheus Adapter 暴露自定义指标。震荡:扩容后指标降→又缩→又涨,用 stabilizationWindow(如 300s)平滑。上限:maxReplicas 防成本失控,也防下游(DB)被打。依赖瓶颈:只扩应用,DB 成瓶颈则无效,需全链路容量匹配。

【关键技术点】
HPA自定义指标stabilizationWindowmaxReplicasKEDA容量匹配
【Java 实现示例】
// HPA 基于 QPS 自定义指标
apiVersion: autoscaling/v2
metrics: - type: Pods
  pods: { metric: {name: http_qps}, target: {averageValue: 2000} }
behavior: scaleDown: stabilizationWindowSeconds: 300  # 防震荡
maxReplicas: 100  minReplicas: 10
// 大促前预扩: kubectl scale deploy --replicas=50
【面试官可能继续追问】
【常见错误回答】
  • ❌ 无上限扩——成本爆炸。正确:maxReplicas。
  • ❌ 不防震荡——频繁扩缩。正确:冷却窗口。
  • ❌ 只扩应用——下游崩。正确:全链路容量。
【架构师评分标准】
初级 0~40
手动扩。
中级 40~60
知 HPA,无冷却/上限。
高级 60~80
自定义指标+防震荡+上限+依赖匹配。
架构师 80~100
再加:①QPS 指标优于 CPU;②KEDA 事件驱动;③预扩+保底;④成本与容量权衡。

第 83 题:Pod 故障排查套路 排查

【真实企业业务场景】
订单 Pod 状态 CrashLoopBackOff / Pending / ImagePullBackOff,新人不清楚从哪查。要求:给出标准 Pod 故障排查路径。
【面试官问题】
    1. 排查顺序?2. Pending 原因?3. CrashLoopBackOff?4. 资源/探针?5. 日志与事件?
【候选人的标准回答】

第一步:分析。Pod 故障先分层:调度(Pending)→ 拉镜像(ImagePull)→ 启动(CrashLoop)→ 运行(探针/资源)。

第二步:挑战。分层定位、资源、探针。

第三步:架构。describe:看 Events(调度失败/拉镜像错);②Pending:资源不足/节点亲和/污点;③CrashLoop:看 logs + 退出码(137=OOM,1=应用错);④探针:liveness 配错致反复重启;⑤资源:limit 太小 OOMKilled。

第四步:选型。kubectl describe/logs/events + 监控。

第五步:一致性。排查不影响其他 Pod(隔离)。

第六步:高可用。多副本;探针正确防误杀。

第七步:优化。就绪探针控流量;资源 requests 合理。

【架构设计】
状态 → describe(Events) → 分层: Pending(资源/亲和) / ImagePull(镜像/秘钥) / CrashLoop(日志/退出码) / 探针误杀 / OOMKilled(limit)
【技术方案深度解析】

排查顺序:kubectl get pod → describe(Events 最关键)→ logs → 退出码。Pending:集群资源不够 / nodeSelector 不匹配 / 污点未容忍。CrashLoop:退出 137=OOM(Q81),1/2=应用启动异常(看 logs)。探针误杀:liveness 初始延迟太短或阈值严,Pod 启动慢被 kill 重启;需配 initialDelaySeconds。

【关键技术点】
CrashLoopPendingdescribe Events退出码探针OOMKilled
【Java 实现示例】
// 探针配置(避免误杀)
livenessProbe: { tcpSocket: {port: 8080}, initialDelaySeconds: 30, periodSeconds: 10 }
readinessProbe: { httpGet: {path: /health}, initialDelaySeconds: 10 }
// 排查命令
kubectl describe pod order-xxx   # 看 Events
kubectl logs order-xxx --previous # 看上次崩溃日志
【面试官可能继续追问】
【常见错误回答】
  • ❌ 不看 describe Events——盲查。正确:先 Events。
  • ❌ 探针 initialDelay 太短——误杀。正确:配延迟。
  • ❌ 不区分就绪/存活——流量错。正确:分开配。
【架构师评分标准】
初级 0~40
不会查 Pod。
中级 40~60
知 logs,不论分层。
高级 60~80
describe+分层+退出码+探针。
架构师 80~100
再加:①调度/资源深度;②探针设计;③监控与事件聚合;④预案与 SOP。

第 84 题:滚动发布与优雅停机 发布

【真实企业业务场景】
滚动发布新版本时,旧 Pod 被直接 kill,正在处理的请求中断、Kafka 消费位丢失、连接池突断,用户感知 502。要求:实现零中断滚动发布与优雅停机。
【面试官问题】
    1. 为什么直接 kill 丢请求?2. 优雅停机怎么做?3. 探针配合?4. 消费位怎么保?5. 发布策略?
【候选人的标准回答】

第一步:分析。滚动发布需「先接新、再退旧、旧 Pod 处理完再杀」,靠就绪探针 + 优雅停机 + preStop 实现零中断。

第二步:挑战。中断、消费位、探针、策略。

第三步:架构。探针:新 Pod 就绪(readiness 通过)才接流量;②优雅停机:收到 SIGTERM 先停接收新请求、处理完在途请求、关连接池/MQ 再退;③preStop:sleep 几秒等 LB 摘流量;④消费位:Kafka 用 enable.auto.commit=false,停机前 commitSync;⑤策略:maxSurge/maxUnavailable 控制节奏。

第四步:选型。K8s RollingUpdate + Spring 优雅停机 + Kafka 手动提交。

第五步:一致性。在途请求处理完,消费位提交,无丢失。

第六步:高可用。滚动保最少可用(maxUnavailable=0);零中断。

第七步:优化。配合灰度(Q35)降低风险。

【架构设计】
新Pod就绪→接流量→旧Pod收SIGTERM→preStop摘流量→处理在途→commit消费位→退出 maxSurge/maxUnavailable 控节奏
【技术方案深度解析】

为什么中断:默认 SIGTERM 后宽限期(terminationGracePeriodSeconds)内强制 kill,在途请求/未提交消费位丢失。优雅停机:Spring 注册 ShutdownHook,先关端口(不再收新)、等线程池任务完、关资源。preStop sleep:等 LB/就绪探针摘掉流量再处理在途。消费位:停机前 commitSync 防重复消费。

【关键技术点】
滚动发布优雅停机SIGTERMpreStop就绪探针消费位提交
【Java 实现示例】
// Spring 优雅停机
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
// Kafka 停机前提交
public void shutdown(){ consumer.commitSync(); } // ShutdownHook
// K8s: preStop 等流量摘离
lifecycle: preStop: { exec: { command: ["sh","-c","sleep 10"] } }
strategy: rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
【面试官可能继续追问】
【常见错误回答】
  • ❌ 直接 kill——请求/消费位丢。正确:优雅停机。
  • ❌ 无 preStop——流量未摘即杀。正确:preStop 摘流。
  • ❌ 自动提交消费——位丢。正确:手动提交。
【架构师评分标准】
初级 0~40
直接 kill。
中级 40~60
知探针,不论优雅停机。
高级 60~80
探针+优雅停机+preStop+消费位+节奏。
架构师 80~100
再加:①宽限期调优;②长事务处理;③幂等兜底;④与灰度协同。

第 85 题:服务网格与灰度流量 Mesh

【真实企业业务场景】
在 K8s 上用 Istio 做灰度,要求「1% 用户走 v2,且 v2 调下游也走 v2」,但发现下游调用没按灰度标路由,链路错乱。要求:理清 Mesh 灰度与全链路标透传。
【面试官问题】
    1. Mesh 灰度怎么路由?2. 为什么下游没跟?3. 标怎么透传?4. 与 Q35 应用级灰度区别?5. mTLS?
【候选人的标准回答】

第一步:分析。Mesh 灰度靠 Sidecar 按 VirtualService 路由,但跨服务标透传需应用配合(把标放进请求头),否则下游不知灰度。

第二步:挑战。路由、标透传、与应用级灰度配合。

第三步:架构。路由:VirtualService 按 header(x-version)路由 1% 到 v2;②标透传:应用把 x-version 在调用下游时透传(Feign 拦截器,Q35);③全链路:标从入口到每个服务一致;④mTLS:Mesh 自动服务间加密;⑤配合:Mesh 管流量,应用管标透传与数据兼容。

第四步:选型。Istio(VirtualService/DestinationRule)+ 应用标透传 + OTel baggage。

第五步:一致性。同用户全链路同版本(标透传保证)。

第六步:高可用。灰度故障自动回滚(流量权重);mTLS 零信任。

第七步:优化。基于指标自动推进(Argo Rollouts)。

【架构设计】
入口(x-version:v2) → Sidecar路由v2 → 应用透传x-version给下游 → 下游Sidecar路由v2 (全链路同版本) mTLS: 服务间自动加密
【技术方案深度解析】

为什么下游没跟:Sidecar 只管「入站请求按规则路由」,但应用调下游时的请求头若无 x-version,下游 Sidecar 不知该路由哪版。解决:应用层把灰度标透传(Q35)。Mesh vs 应用级:Mesh 管流量与 mTLS(无侵入),应用管标生成/透传与数据兼容,两者互补。mTLS:Istio 自动签发证书,服务间零信任。

【关键技术点】
VirtualService标透传全链路灰度mTLSIstioDestinationRule
【Java 实现示例】
// 应用透传灰度标(Feign拦截器)
public void apply(RequestTemplate t){
  t.header("x-version", GrayContext.get()); // 透传下游
}
// Istio VirtualService: 1% 路由 v2
apiVersion: networking.istio.io/v1beta1
route: - destination: {host: order-v2} weight: 1
        - destination: {host: order}    weight: 99
match: [{headers: {x-version: {exact: v2}}}]
【面试官可能继续追问】
【常见错误回答】
  • ❌ 只配 VS 不透明传——下游错乱。正确:应用透传标。
  • ❌ Mesh 包办一切——数据兼容不管。正确:应用配合。
  • ❌ 无 mTLS——零信任缺。正确:开启。
【架构师评分标准】
初级 0~40
不知 Mesh 灰度。
中级 40~60
知 VS,不论透传。
高级 60~80
VS路由+标透传+全链路+应用配合。
架构师 80~100
再加:①mTLS 零信任;②与应用级灰度协同;③自动推进/回滚;④非 K8s 兜底。
第九部分 · 云原生与 Kubernetes(第 81~85 题) · 返回大纲