面试 · 系统串联题
这份是什么:这里收录的是「锚点题」——真实生产反复用、K8s 面试跑不掉、且能一题炸开成一棵知识树的综合题。考官抛出一个高频问题后,会顺着你的回答一层层往深追(现象 → 机制 → 边界 → 定位 → 治理),一道题就能把调度、网络、存储、探针、QoS、RBAC 等横向串起来。
与 1~5 类的关系:
1-概念解释型/2-对比辨析型/3-原理追问型是单点知识(一个概念、一对对比、一个机制)。4-场景排查型/5-方案设计型是单场景的排查树或方案。- 本档凌驾于 5 类之上:每道锚点题都会串出上述多个单点,并给出「考官追问链」,帮你把散点连成网。考前重点刷这一份——顺着追问链自己讲一遍,卡壳的地方回对应类补齐。
怎么用:先盖住「参考答题骨架」,自己顺着「考官追问链」从第 1 层讲到最后一层;讲不下去的层,就是你的知识断点。「踩坑/加分点」是区分「背过」和「真做过」的分水岭,务必内化成自己的话。
技术基线:本档结论对齐 2026 年年中主流版本(K8s 1.33+,原生 sidecar 与 nftables kube-proxy 模式均已在 1.33 GA;iptables 仍是 kube-proxy 上游默认模式)。
锚点题 1:Pod 起不来,给我一套完整的排查树#
为什么是锚点题:这是 K8s 面试出现频率最高的问题,没有之一。它同时是运维日常最高频的动作。答得好不好,直接暴露你是「跑过 demo」还是「扛过线上」。四大异常状态(Pending / ImagePullBackOff / CrashLoopBackOff / OOMKilled)背后是调度、镜像、探针、资源、cgroup 五套完全不同的机制,一题就能验区分度。
会串出的知识点地图:
- 通用起手式:
get pod(STATUS/READY/RESTARTS)→describe pod(看 Events,八成答案在这)→logs --previous(看崩溃前输出)→get events --sort-by(全局时间线) - Pending 分支:kube-scheduler 预选/优选、resources.requests、Taint/Toleration、nodeSelector/nodeAffinity、topologySpreadConstraints、PVC 未绑定(
WaitForFirstConsumer)、节点全 cordon - ImagePullBackOff / ErrImagePull 分支:镜像名/tag、imagePullSecret(
kubernetes.io/dockerconfigjson)、私有仓库网络/证书、Docker Hub 限流、CPU 架构不匹配(arm64/amd64)、imagePullPolicy: Never导致的ErrImageNeverPull - CrashLoopBackOff 分支:应用启动即退、命令/ENTRYPOINT 写错、探针误杀(liveness vs startupProbe)、配置/依赖缺失、退避算法、exit code 语义
- OOMKilled 分支:cgroup OOM(容器超 memory limit)vs 内核全局 OOM(节点内存耗尽)、可压缩 vs 不可压缩资源、runtime 对 cgroup 的感知(GOMEMLIMIT /
-XX:MaxRAMPercentage)
考官追问链:
- 现象层:
kubectl get pod里这四种状态你怎么一眼区分?分别对应生命周期的哪个阶段? - 起手式层:拿到一个起不来的 Pod,前三条命令敲什么?为什么
logs要加--previous? - 机制层:CrashLoopBackOff 的重启退避是怎么算的?exit code 137 / 143 / 127 分别代表什么?OOMKilled 的 137 和 liveness 探针杀容器的 137 怎么区分?
- 边界层:liveness、readiness、startup 三个探针分工是什么?为什么「应用启动慢」这种场景配 liveness 会把自己搞成 CrashLoop,正确做法是什么?
- 定位/治理层:describe 的 Events 空了、logs 也没东西(比如容器根本没起来),你下一步去哪查?(→ 节点级
journalctl -u kubelet/dmesg/ 运行时日志)如何从根上减少这类问题?
参考答题骨架:
先说通用四步,再分四支。
通用起手式
kubectl get pod <pod> -n <ns> -o wide # 看 STATUS / READY / RESTARTS / 所在节点
kubectl describe pod <pod> -n <ns> # 看 Events + 每个容器的 State/Last State/Exit Code
kubectl logs <pod> -c <container> --previous # 看"上一次"崩溃前的输出(关键)
kubectl get events -n <ns> --sort-by='.lastTimestamp' # 全局时间线,找连锁线索分支一:Pending —— 还没被调度/绑定到节点
- 本质:调度失败,
describe的 Events 里是FailedScheduling。 - 常见原因:
Insufficient cpu/memory:节点剩余资源装不下 Pod 的 requests(不是 limits)→kubectl describe node看 Allocated,调低 requests / 扩节点 / 开 Autoscaler。node(s) had taint {...} that the pod didn't tolerate:污点不容忍 → 加 toleration 或移除 taint。node(s) didn't match node selector/affinity:标签不匹配。didn't match pod topology spread constraints:拓扑约束过严。- PVC 没绑定:
WaitForFirstConsumer模式下要等调度、或根本没有可用 PV → 转存储排查。
- 区分:
Pending是「没落到节点」;如果已绑定节点但容器没起,状态是ContainerCreating(多为拉镜像/挂卷/CNI 分配 IP 阶段),别混。
分支二:ImagePullBackOff / ErrImagePull —— 镜像拿不到
ErrImagePull是首次失败,ImagePullBackOff是退避重试中。- 排查:
describe看 Events 里的具体报错。not found/manifest unknown:镜像名或 tag 写错、tag 不存在。unauthorized/pull access denied:私有仓库缺imagePullSecret,或 secret 类型不对(必须是kubernetes.io/dockerconfigjson),或 SA 没引用该 secret。dial tcp ... timeout:节点到仓库网络不通 / 私有 registry 证书问题 / Docker Hub 匿名拉取限流。no match for platform:镜像架构与节点不符(arm64 vs amd64)。ErrImageNeverPull:imagePullPolicy: Never但节点本地没这个镜像。
分支三:CrashLoopBackOff —— 起来又挂,反复重启
- 本质:容器进程反复退出,kubelet 按退避重启,间隔 10s→20s→40s…封顶 5 分钟。
describe看容器Last State: Terminated的 Exit Code + Reason,logs --previous看崩溃前输出:- Exit
0:正常退出(可能是command跑完就结束,本不该是长驻进程)。 1/2:应用自身报错(配置缺失、依赖不可达、端口占用)。137= 128+9(SIGKILL,多为 OOM 或 liveness 探针杀)。143= 128+15(SIGTERM,被优雅终止)。139= SIGSEGV(段错误)。126命令不可执行 /127命令未找到(ENTRYPOINT、command 写错)。
- Exit
- 高频误配:应用启动要 30s,却配了
livenessProbeinitialDelaySeconds太短 → 还在启动就被判失败杀掉 → 无限 CrashLoop。正解:用startupProbe兜住慢启动,startup 通过后 liveness 才开始生效。
分支四:OOMKilled —— 内存超限被杀
describe看Last State: Terminated, Reason: OOMKilled, Exit Code: 137。- 两种 OOM 要分清:
- cgroup OOM:容器用量超
limits.memory,被该容器 cgroup 的 OOM killer 杀,只杀这一个容器,Reason 明确写 OOMKilled。 - 内核全局 OOM:整个节点内存被打满,Linux 内核 OOM killer 按
oom_score_adj挑进程杀,可能殃及邻居。
- cgroup OOM:容器用量超
- 定位:
kubectl top pod看真实用量;看 limit 是不是设太小;查内存泄漏;查 runtime 是否感知 cgroup——老 JVM 不加-XX:+UseContainerSupport/MaxRAMPercentage会按宿主机内存算堆,Go 不设GOMEMLIMIT也可能自杀式 OOM。
踩坑 / 加分点:
- memory 是不可压缩资源,超 limit 直接杀;CPU 是可压缩资源,超 limit 只 throttle(限流)不杀——很多人把「CPU 打满导致 Pod 挂」归错因,其实 CPU 从不会因超限被杀,只会变慢。
- Events 有保留期(默认约 1 小时),排查慢了事件就没了,要养成第一时间
describe+ 存现场的习惯。 describe和logs都空、容器压根没进入运行的情况(比如 CNI 分不到 IP、挂卷 Multi-Attach、镜像层解压失败),必须上节点看journalctl -u kubelet、crictl ps -a/crictl logs、dmesg。这一步能不能想到,是「只会 kubectl」和「懂底层」的分水岭。- 原生 sidecar(1.33 GA,
initContainers+restartPolicy: Always)挂了也会拖垮主容器启动,排查时别漏了 init/sidecar 容器的独立日志。 - 治理侧一句话收口:合理 requests/limits + 探针分工(startup 兜启动、readiness 控流量、liveness 保活)+ 镜像预热/多架构清单 + PDB,能把这四类问题的发生率压到最低。
锚点题 2:集群里 A 服务访问 B 服务不通,你怎么分层排查?#
为什么是锚点题:微服务落 K8s 后,「服务间调不通」是最高频的线上故障,也是面试网络能力的照妖镜。它天然是一条自上而下的分层链,每一层都是一个独立知识点,答的过程就是在画 K8s 的整张网络拓扑。
会串出的知识点地图:
- DNS 层:CoreDNS、
/etc/resolv.conf、nameserver(kube-dns ClusterIP,kubeadm 默认10.96.0.10)、search域、ndots:5、FQDN<svc>.<ns>.svc.cluster.local、跨 ns 要带 namespace - Service / Endpoints 层:selector 与 Pod label 匹配、EndpointSlice、readiness 没过的 Pod 不进 Endpoints、targetPort
- 直连 Pod IP:二分法定位「问题在 Service 层还是 Pod/网络层」
- kube-proxy 层:iptables / IPVS / nftables 模式、ClusterIP 是虚 IP 靠 DNAT、kube-proxy DaemonSet 健康
- CNI / 跨节点层:overlay(VXLAN) vs BGP vs eBPF、MTU、节点安全组
- NetworkPolicy 层:默认全通 → 一旦被 policy 选中即默认拒绝、别忘放行 DNS egress、Calico/Cilium
- Ingress 层:仅当经外部入口时
考官追问链:
- 现象层:先别急着查,你怎么定位是「解析失败」「连接被拒」还是「连上了但超时」?在哪个 Pod 里、用什么命令测?
- DNS 层:
nslookup b-svc返回什么算正常?为什么有时候跨 namespace 调用要写全b-svc.other-ns?ndots:5是什么、会带来什么副作用? - Service 层:
kubectl get endpoints b-svc是空的,你会怀疑哪两件事?为什么「Pod 明明 Running 却不在 Endpoints 里」? - 二分定位:直连 Pod IP 能通、走 Service 不通,说明问题在哪层?反过来呢?
- 策略/底层层:确认到了 kube-proxy / NetworkPolicy 层,分别怎么验证?为什么加了 NetworkPolicy 后「偶发解析失败」是经典坑?
参考答题骨架:
我会在 A 的 Pod 内从上往下逐层排除(没有工具就 kubectl debug 起 netshoot ephemeral container)。
第 0 步 · 定位现象
kubectl exec -it <A-pod> -- sh
nslookup b-svc # 先分清:是解析不了?还是解析了连不上?
curl -v b-svc:8080 # 看是 DNS 失败 / connection refused / timeout第 1 层 · DNS
- 看
/etc/resolv.conf:nameserver应指向 kube-dns 的 ClusterIP(如10.96.0.10),search应含<ns>.svc.cluster.local svc.cluster.local cluster.local,options ndots:5。 - CoreDNS 是否健康:
kubectl get pod -n kube-system -l k8s-app=kube-dns,看有没有崩、有没有被限流。 - 跨 namespace 调用只写短名(
b-svc)会先在本 ns 拼后缀,解析不到 → 要写b-svc.<b-ns>或全 FQDN。 - 上游解析(外部域名)走 CoreDNS Corefile 的
forward。
第 2 层 · Service / Endpoints(最高频命中)
kubectl get svc b-svc -o wide # 有没有 ClusterIP,targetPort 对不对
kubectl get endpointslices -l kubernetes.io/service-name=b-svc # 后端有没有 Pod IP- Endpoints 为空 → 两大嫌疑:① Service 的
selector和 B 的 Podlabel不匹配;② B 的 Pod 没通过 readinessProbe——未就绪的 Pod 不会被加进 Endpoints(这是「Running 却收不到流量」的头号原因)。 - targetPort 与容器实际监听端口不一致也会「连上但拒」。
第 3 层 · 直连 Pod IP(二分法)
curl <B-pod-ip>:8080- 直连通、走 Service 不通 → 问题在 Service / kube-proxy 层。
- 直连也不通 → 问题在 Pod 应用本身或 CNI 网络层。
第 4 层 · kube-proxy
- 每个节点的
kube-proxyDaemonSet 是否健康(挂了则新 Service 规则不再下发)。 - 看模式并核对规则:iptables 模式
iptables-save | grep <clusterIP>;IPVS 模式ipvsadm -Ln;nftables 模式(1.33 GA)nft list ruleset。ClusterIP 是虚拟 IP,靠 kube-proxy 下发 DNAT 规则转到真实 Pod IP。
第 5 层 · CNI / 跨节点
- A、B 在不同节点却不通:overlay(VXLAN)路由 / BGP(Calico)/ eBPF(Cilium)问题;MTU 不匹配(VXLAN 封装吃掉约 50 字节,大包被丢、小包正常,表现为「curl 小接口好使、传大 body 卡死」);节点间安全组/防火墙没放 CNI 端口。
第 6 层 · NetworkPolicy
kubectl get netpol -n <ns>- 默认没有 policy 时全通;一旦有 policy 选中某 Pod,该 Pod 就默认拒绝所有未显式放行的流量。检查 B 的 ingress 是否允许来自 A(按 label/namespace)、A 的 egress 是否允许到 B。Calico/Cilium 的策略比原生更强(可按 FQDN、L7)。
第 7 层 · Ingress(仅当从集群外或经 ingress 进来):Ingress Controller、rules 的 host/path、backend service。
踩坑 / 加分点:
- NetworkPolicy 忘了放行 DNS egress(到 kube-dns 的 UDP/TCP 53)是最隐蔽的坑:表现是「服务时通时不通/偶发解析超时」,因为业务连接靠 IP 直连能过,但每次域名解析被策略拦。上了 default-deny egress,第一条必须放 DNS。
- readiness 探针没过导致 Endpoints 空——排查时先看这个,能省一半时间。
conntrack表打满(nf_conntrack: table full)会随机丢连接,高并发短连接场景要查net.netfilter.nf_conntrack_max。- 历史遗留的 CoreDNS +
ndots:5+ UDP conntrack 竞态会造成「DNS 偶发 5 秒延迟」,可用 NodeLocal DNSCache 或dnsConfig调 ndots 缓解(细节见4-场景排查型的 DNS 专题)。 Service.spec.externalTrafficPolicy: Local会让没有本地 endpoint 的节点直接丢包,也能表现成「部分不通」。
锚点题 3:如何让一次滚动更新做到真正零停机?#
为什么是锚点题:发布是每天都在做的高危动作,「零停机」几乎是所有面试官都会追的点。它能一路串出 Deployment 策略、三种探针、Pod 优雅终止的完整时序、信号处理、PDB——而且绝大多数人只会答 maxUnavailable,答不全 preStop 与 Endpoints 摘除的时序竞态,区分度极高。
会串出的知识点地图:
- Deployment 策略:
RollingUpdate的maxSurge/maxUnavailable(默认各 25%)、先扩后缩 - readinessProbe:决定何时把新 Pod 加入 Endpoints
- Pod 优雅终止时序:SIGTERM + preStop hook 与 Endpoints 摘除是并行异步的,中间有流量窗口
- preStop hook +
terminationGracePeriodSeconds(默认 30s):用sleep补上摘流量的时间差 - 应用信号处理:捕获 SIGTERM、PID 1 问题、
exec形式 CMD、tini - PDB(PodDisruptionBudget):保护自愿驱逐(drain/升级)时的最小可用数
- 长连接(gRPC/WebSocket)额外 drain
考官追问链:
- 现象层:只把
maxUnavailable设成 0 就零停机了吗?为什么还会有几秒 5xx / connection reset? - 机制层:删一个 Pod 的瞬间,K8s 内部并行发生了哪几件事?为什么「Endpoints 摘除」和「容器收 SIGTERM」的先后没有保证,会产生什么窗口?
- 补丁层:业界为什么普遍在
preStop里sleep几秒?这几秒到底在等谁?和terminationGracePeriodSeconds是什么关系? - 应用层:应用没捕获 SIGTERM 会怎样?为什么
CMD ["sh","-c","java ..."]这种写法会让优雅关闭失效? - 运维边界层:滚动更新受 PDB 约束吗?那 PDB 到底在什么场景救你?长连接服务怎么额外处理?
参考答题骨架:
零停机是**「保容量 + 控流量进出时机 + 优雅退出」三件事的合力**,缺一不可。
1)Deployment 策略——保容量
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # 更新期间可用副本数不掉(先拉起新的再停旧的)
maxSurge: 1 # 允许临时多 1 个副本maxUnavailable: 0 保证任意时刻健康副本数 ≥ 期望值。
2)readinessProbe——控流量「进」的时机
- 没有 readiness,新 Pod 一进
Running就被加进 Endpoints 开始收流量,但应用可能还没热身好(连接池、缓存、JIT)→ 打过去就报错。 - readiness 通过后才进 Endpoints,这是新 Pod 平滑接流量的前提。
3)优雅终止时序——控流量「出」的时机(核心难点) 删 Pod 瞬间,K8s 并行做两件事,二者没有先后保证:
- A:kubelet 对容器发 SIGTERM + 执行
preStophook。 - B:EndpointSlice controller 把该 Pod 从 Endpoints 摘除 → 各节点 kube-proxy / ingress 异步更新转发规则。
竞态窗口:如果 A 比 B 快,容器已经开始关闭,但规则还没更新完,仍有新连接被路由到正在关闭的 Pod → connection refused / reset。
补丁:preStop 里 sleep,把关闭动作往后拖,给 B 的传播留时间:
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"] # 等 Endpoints/iptables 摘干净再让进程收尾
terminationGracePeriodSeconds: 45 # 必须 > preStop sleep + 应用 drain 时间时序:SIGTERM 发出/preStop 开始 → sleep 10s(期间流量被摘除)→ 应用优雅关闭 → 若超过 grace period 仍未退 → SIGKILL 强杀。preStop + 应用关闭时间 必须小于 terminationGracePeriodSeconds(默认 30s),否则被强杀。
4)应用信号处理
- 进程要捕获 SIGTERM:停止接新请求、drain 在途请求、关连接池,然后退出。
- PID 1 陷阱:
CMD ["sh","-c","java -jar app.jar"]里真正的进程不是 PID 1(是 sh),信号不透传给 java → 收不到 SIGTERM → 只能等 grace period 到点被 SIGKILL 硬杀,在途请求全断。用 exec 形式(CMD ["java","-jar","app.jar"])或加tini做 init。
5)PDB——保护运维场景
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 2 # 或 maxUnavailable
selector: { matchLabels: { app: my-svc } }- 注意:滚动更新本身由 Deployment 控制器负责,不受 PDB 约束;PDB 约束的是自愿驱逐(
kubectl drain、节点升级、Cluster Autoscaler 缩容)。它保证「运维操作不会一次干掉太多副本打穿容量」。零停机的完整答案必须把这块也讲到,否则「发布零停机但升级节点时被打穿」。
踩坑 / 加分点:
- 只配
maxUnavailable不配 readiness = 假零停机:新 Pod 没热身就接流量,照样 5xx。 preStop sleep是业界公认的补丁,根因是 Endpoints 摘除的传播延迟无法与 SIGTERM 同步;不加就是「发布必抖 3~5 秒」。- PID 1 信号不透传是最常见的「优雅关闭没生效」根因,且极隐蔽(本地跑好好的)。
- 长连接(gRPC、WebSocket、DB 连接池):SIGTERM 后已建立的长连接不会自动断,要应用主动发 GOAWAY / 关连接,并配合 LB 层的 connection draining;只靠 K8s 机制不够。
- 加分总结一句:
maxUnavailable=0保容量 → readiness 控进流 → Endpoints 摘除 + preStop sleep 控出流 → SIGTERM 优雅关闭在 grace period 内完成 → PDB 兜运维场景,五环相扣才是真零停机。
锚点题 4:一个节点变成 NotReady,集群里会发生什么?你怎么处理?#
为什么是锚点题:节点故障是生产必然遇到的事,这题既考底层(kubelet/运行时/PLEG 心跳机制),又考「故障后集群的自动行为」(污点驱逐 + 宽限期),还考运维操作(cordon/drain)和数据安全边界(StatefulSet 不自动重建)。能把「NotReady ≠ Pod 立即停」和「5 分钟驱逐宽限期」讲清楚的人,才是真扛过节点故障的。
会串出的知识点地图:
- 判定机制:kubelet 上报 node lease(默认 10s 一次)+ node status;kube-controller-manager 的
node-monitor-grace-period(默认约 40s)判 NotReady - NotReady 根因:kubelet 挂/失联、容器运行时(containerd/CRI)挂、PLEG is not healthy、CNI 未就绪(NetworkUnavailable)、资源压力(Memory/Disk/PID Pressure)
- 自动驱逐:node controller 打
node.kubernetes.io/not-ready:NoExecute/unreachable:NoExecute污点;Pod 默认带tolerationSeconds=300(5 分钟宽限)后才被驱逐重建 - 数据面影响:Pod 可能仍在跑(僵尸);readiness 无法上报导致流量黑洞;StatefulSet Pod 卡 Terminating 不自动重建(防脑裂)
- 运维动作:
cordon(只停调度)→drain --ignore-daemonsets(驱逐,受 PDB 约束)→ 维修 →uncordon/delete node
考官追问链:
- 机制层:apiserver 是怎么知道一个节点「掉线」的?多久判定?为什么不是立刻?
- 现象层:节点变 NotReady 的常见根因有哪些?「PLEG is not healthy」是什么意思,通常指向哪里?
- 自动行为层:判定 NotReady 后,上面的 Pod 会立刻被迁走吗?为什么默认要等 5 分钟?这个 5 分钟怎么来的、能不能调?
- 数据面边界层:节点失联但 Pod 其实还在跑,会不会仍有流量打过去(黑洞)?为什么 StatefulSet 的 Pod 会卡在 Terminating 不重建,强删有什么风险?
- 处理层:确认节点要维修,
cordon和drain分别干什么?drain 卡住不动通常是什么原因?
参考答题骨架:
判定机制
- kubelet 每 ~10s 续约 node lease,并周期上报 node status。
- kube-controller-manager 的 node lifecycle controller 在
node-monitor-grace-period(默认约 40s)内收不到更新 → 把 Node 标为NotReady。
NotReady 常见根因
- kubelet 进程挂了 / 与 apiserver 网络断(
unreachable)。 - 容器运行时挂(containerd/CRI 不响应)。
- PLEG is not healthy:Pod Lifecycle Event Generator 定期 relist 容器状态超时,通常是 runtime 卡住、磁盘 IO 慢、单节点容器数过多。
- CNI 没就绪 → node condition
NetworkUnavailable。 - 资源压力:
MemoryPressure/DiskPressure/PIDPressure。
判定后的自动行为(关键)
- node controller 给节点打
NoExecute污点:node.kubernetes.io/not-ready或.../unreachable。 - Pod 若不容忍该污点会被驱逐——但准入控制器默认给所有 Pod 注入
tolerationSeconds=300的容忍。所以节点抖动的头 5 分钟,Pod 不会被立刻迁走,避免短暂抖动引发大规模重调度。超过 5 分钟才在别的节点重建。 - 这个宽限期可通过 Pod 的 toleration
tolerationSeconds调小(加快故障转移),但会更敏感于抖动。
数据面影响(区分度所在)
- NotReady ≠ Pod 立即停止:kubelet 失联时容器可能还在宿主机上跑(僵尸),只是 apiserver 视角认为不可用。
- 流量黑洞:kubelet 失联无法上报 readiness,Pod 在 Endpoints 里的状态可能停在旧值,kube-proxy 仍把流量转过去 → 打到一个可能已经半死的实例。真正靠谱的摘除依赖健康探测与 Endpoints 更新。
- StatefulSet 的特殊性:节点
unreachable时,mysql-0会进入Terminating但不会自动重建——因为 K8s 无法确认老 Pod 真的死了,贸然在别处拉起同标识 Pod 可能双写导致数据脑裂。需要人工确认节点确实死了再--force --grace-period=0删除(有风险,要先做 fencing)。
处理流程
kubectl describe node <node> # 看 Conditions / Events 定位根因
# 上节点(或带外)确认:
systemctl status kubelet && journalctl -u kubelet -n 200
systemctl status containerd
df -h; free -m # 磁盘/内存压力
kubectl cordon <node> # 标记不可调度(不影响现有 Pod)
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data # 驱逐 Pod(受 PDB 约束)
# 维修 …
kubectl uncordon <node> # 恢复调度
# 若节点报废,确认 Pod 已在别处重建后:
kubectl delete node <node>踩坑 / 加分点:
cordon只停「新调度」,不动现有 Pod;drain才真正把 Pod 赶走——两者常被混。- drain 卡住不动的三大原因:① PDB 不允许(会打穿 minAvailable);② 有 emptyDir 数据(需
--delete-emptydir-data);③ 有非控制器管理的裸 Pod(需--force)。 - StatefulSet Pod 卡 Terminating 时无脑
--force删除是数据事故高发点(主库双写);正确姿势是先确认节点物理下线/断电(fencing)再删。 - 5 分钟驱逐宽限(
tolerationSeconds=300)是「快速故障转移」和「抗抖动」的权衡旋钮,金融级快切场景会调小,但要配合更稳的网络。 - DaemonSet Pod 不会被 drain 驱逐(
--ignore-daemonsets是常态),别指望 drain 能清空节点上的 CNI/日志 agent。
锚点题 5:一个请求从集群外进来,到 Pod 里被处理,完整路径是什么?#
为什么是锚点题:这是「南北向流量」的全景题,一口气串起 DNS、云 LB、Ingress、Service、kube-proxy、CNI、容器网络。能把每一跳讲清楚,等于把 K8s 网络的骨架画了一遍;而且它是排查「外部访问异常」「源 IP 丢失」「TLS 在哪终止」等一类问题的地图。
会串出的知识点地图:
- DNS → 云 LB:域名解析到公网 LB IP;L4/L7 LB
- Ingress Controller:本身是集群内 Pod(nginx/Envoy/Gateway API 实现),经 LoadBalancer/NodePort Service 暴露;做 host/path 路由 + TLS 终止
- Service / Endpoints:Ingress 引用 Service 拿后端 Pod IP(数据面常直连 Pod,绕过 kube-proxy)
- kube-proxy:若走 ClusterIP,做 ClusterIP→PodIP 的 DNAT(iptables/IPVS/nftables)
- CNI:跨节点 overlay/BGP/eBPF,同节点 veth/bridge,把包送进 Pod netns
externalTrafficPolicyLocal vs Cluster:源 IP 保留 vs 多一跳- Gateway API(2026 现代方向)、Cilium 无 kube-proxy(eBPF 直接做 Service LB)
考官追问链:
- 入口层:用户敲域名后,流量第一站到哪?Ingress Controller 自己是怎么被外部访问到的(它不也是个 Pod 吗)?
- L7 层:Ingress 拿到请求后按什么路由?TLS 通常在哪一跳终止?
- 转发层:Ingress 到后端,是走 Service 的 ClusterIP,还是直连 Pod IP?两者有什么区别?kube-proxy 在这条路里扮演什么角色?
- 底层网络层:包到了目标节点后,CNI 怎么把它送进 Pod 的网络命名空间?同节点和跨节点有何不同?
- 边界/坑层:
externalTrafficPolicy: Local和Cluster对源 IP 和转发路径有什么影响?各有什么坑?
参考答题骨架:
完整路径(自外向内)
客户端
│ ① DNS:域名 → Ingress 的公网 LB IP
▼
云负载均衡器(L4/L7)
│ ② 分发到各节点的 NodePort(externalTrafficPolicy 影响源 IP 与是否二次转发)
▼
Ingress Controller Pod(nginx / Envoy / Gateway API 实现)
│ ③ L7 路由:按 host/path 匹配 Ingress 规则;通常在此终止 TLS
│ ④ 引用后端 Service 拿 Endpoints,数据面多为"直连 Pod IP"做 LB
▼
(若走 ClusterIP) kube-proxy
│ ⑤ DNAT:ClusterIP → 某个健康 Pod IP(iptables/IPVS/nftables 规则)
▼
CNI 网络
│ ⑥ 跨节点:overlay(VXLAN)/BGP/eBPF 路由到目标 Pod 所在节点
│ 同节点:走 bridge + veth pair
▼
目标 Pod 的 network namespace
│ ⑦ 包进入 Pod netns,容器进程 accept 监听端口
▼
容器处理请求逐跳要点
- ② 云 LB → 节点:LoadBalancer 类型 Service 会在每个节点开 NodePort,云 LB 把流量分到各节点。
externalTrafficPolicy决定行为(见踩坑)。 - ③ Ingress Controller:它自己就是跑在集群里的 Pod,通过一个
type: LoadBalancer的 Service 对外暴露。它做 L7(host/path/重写/TLS 终止/限流)。 - ④~⑤ Service 抽象 vs 数据面:Ingress 逻辑上「转发给 Service」,但主流 Ingress Controller(nginx/Envoy)会 watch Endpoints 直接把流量打到 Pod IP,绕过 kube-proxy 以减少一跳;只有走 ClusterIP 的路径才由 kube-proxy 做 DNAT。
- ⑥ CNI:同节点走 veth/bridge;跨节点 overlay 封装(VXLAN)或纯路由(Calico BGP)或 eBPF(Cilium)。
踩坑 / 加分点:
externalTrafficPolicy: Cluster(默认):任意节点收到流量后可再转发到别的节点上的 Pod,负载更均衡,但会 SNAT 丢失客户端真实源 IP,且多一跳。externalTrafficPolicy: Local:只转发到本节点的 Pod,保留源 IP、少一跳,但如果该节点上没有对应 Pod 就直接丢包——必须配healthCheckNodePort让云 LB 只把流量发给有端点的节点。「源 IP 拿不到」和「部分节点 503」这两个经典问题都出在这里。- 双层 LB 叠加(云 LB + Ingress)会叠加延迟,高性能场景考虑 LB 直连或 Gateway API。
- TLS 终止位置决定后端是否明文、能否做 mTLS:终止在 Ingress(后端明文)vs 透传到 Pod(端到端加密)要按合规要求选。
- 现代加分点:Gateway API 正逐步取代 Ingress(角色分离、更强的路由表达);Cilium 无 kube-proxy 模式用 eBPF 在内核直接完成 Service 负载均衡,路径里就没有 iptables/kube-proxy 这一跳了。
锚点题 6:requests / limits、QoS、OOM、驱逐这几者是怎么联动的?#
为什么是锚点题:资源模型是 K8s 稳定性的地基,也是「Pod 莫名被杀/被驱逐/性能毛刺」几乎所有资源类故障的总根源。这题把调度、cgroup、QoS 分级、kubelet 软驱逐、内核硬 OOM 一条线串起来,能不能讲清「CPU 和内存的本质区别」是硬核区分点。
会串出的知识点地图:
- requests:调度依据(scheduler 按 requests 找节点)+ cgroup 保障下限(cpu 权重)
- limits:cgroup 硬上限
- QoS 三级:Guaranteed(requests==limits)/ Burstable / BestEffort(啥都没设)
- 可压缩 vs 不可压缩:CPU 超限只 throttle(CFS quota 限流)不杀;Memory 超限直接 OOMKill
- kubelet 软驱逐:节点资源到阈值主动驱逐,顺序 BestEffort → Burstable(超 requests 越多越先)→ Guaranteed
- 内核硬 OOM:按
oom_score_adj杀(Guaranteed 约 -998,BestEffort 约 1000) - runtime 感知 cgroup:
GOMEMLIMIT/-XX:MaxRAMPercentage;LimitRange 兜底
考官追问链:
- 基础层:requests 和 limits 各自在什么时候起作用?调度看哪个,运行时限哪个?
- QoS 层:三个 QoS 等级是怎么被判定出来的(不是手动设的)?分别在什么场景下有意义?
- 本质区别层:CPU 和内存超过 limit 时,行为为什么完全不同?「CPU 打满 Pod 被杀」这句话对吗?
- 驱逐层:节点内存快满了,kubelet 先驱逐谁?和内核 OOM killer 有什么区别、谁先动手?
- 治理层:怎么设 requests/limits 才既不浪费又不容易被杀?为什么 JVM/Go 应用要单独关照?
参考答题骨架:
requests vs limits
requests:① 调度依据——scheduler 按 requests 判断节点装不装得下;② cgroup 保障——CPU 转成cpu.shares/weight 保证竞争时的最小份额。limits:cgroup 硬上限——CPU 转成 CFS quota,Memory 转成 cgroup memory limit。
QoS 三级(由 requests/limits 关系自动判定)
| QoS | 判定条件 | 被驱逐/OOM 顺序 |
|---|---|---|
| Guaranteed | 每个容器 cpu、memory 的 requests 等于 limits | 最后被动,最安全 |
| Burstable | 设了 requests/limits 但不满足 Guaranteed | 居中(超 requests 越多越先) |
| BestEffort | requests、limits 都没设 | 最先被杀 |
CPU vs 内存的本质区别(核心)
- CPU 可压缩:超过 limit 只会被 CFS throttle(限流变慢),绝不会因此被杀。所以「CPU 打满导致 Pod 被杀」是错误归因——真相往往是被 throttle 导致响应变慢、探针超时才被重启。
- 内存不可压缩:超过
limits.memory→ 该容器 cgroup 的 OOM killer 直接杀容器(OOMKilled,exit 137)。
两种「因内存被杀」的路径
- 容器超 limit → cgroup OOM:只杀超限那个容器,Reason 明确 OOMKilled。
- 节点内存告急 → kubelet 软驱逐:
memory.available低于 eviction 阈值时,kubelet 主动驱逐 Pod 腾内存,顺序:先 BestEffort → 再 Burstable(且用量超出 requests 越多、priority 越低者越先)→ 最后 Guaranteed。被驱逐的 Pod 状态是Evicted。 - 内存瞬间打满、来不及软驱逐 → 内核全局 OOM:Linux 内核 OOM killer 按
oom_score_adj挑进程杀。kubelet 按 QoS 设不同分值:Guaranteed 约 -998(最不易被杀),BestEffort 约 1000(最易),Burstable 居中。
联动链一句话:requests 定调度落点 + QoS 分级 + 驱逐保护程度;limits 定 cgroup 硬顶 + 触发 OOM 的线;QoS 决定内存紧张时「谁先死」(软驱逐顺序 + 内核 oom_score)。
踩坑 / 加分点:
- 生产别用 BestEffort:一有资源压力它第一个被驱逐/被 OOM,等于把自己放在祭天名单第一位。
- CPU limit 设太低导致 throttle 抖动:P99 毛刺、偶发超时,
kubectl top看 CPU 用量还没打满 limit 却在抖——查container_cpu_cfs_throttled_periods_total。很多团队干脆不设 CPU limit(只设 requests)来避免 throttle。 - **memory requests ≠ limits(Burstable)**在超卖节点上有被驱逐/OOM 风险;对内存敏感的关键服务可设成 Guaranteed 换稳定。
- JVM/Go 要感知 cgroup:老 JVM 不加
-XX:+UseContainerSupport/-XX:MaxRAMPercentage会按宿主机内存算堆 → 必 OOM;Go 用GOMEMLIMIT提示 GC 上限,避免自杀式 OOM。 - 用 LimitRange 给 namespace 设默认 requests/limits,防止有人漏配变 BestEffort;用 ResourceQuota 防止 namespace 整体超卖。
锚点题 7:一个有状态应用(数据库/中间件)上 K8s,用 StatefulSet + PVC 要考虑哪些完整问题?#
为什么是锚点题:无状态谁都会,有状态才见功力。这题串起 StatefulSet 的三大保证、Headless Service、PVC 与 volumeClaimTemplates、存储拓扑(AZ 绑定)、扩缩容与数据保留策略、Multi-Attach、节点故障下的脑裂防护——是「敢不敢把数据库放 K8s」这类真实决策的知识底座。
会串出的知识点地图:
- StatefulSet 三保证:稳定网络标识(
pod-0固定名 + 固定 DNS)、稳定存储(每 Pod 专属 PVC)、有序部署/扩缩/滚动 - Headless Service(
clusterIP: None):每 Pod DNSpod-0.svc.ns.svc.cluster.local - volumeClaimTemplates:每副本一个 PVC,与序号绑定,重建不删
- 存储拓扑:云盘 zonal 绑 AZ、
volumeBindingMode: WaitForFirstConsumer避免盘与 Pod 不同区 - 数据保留:缩容/删除不删 PVC、
persistentVolumeClaimRetentionPolicy、reclaimPolicy(Retain/Delete) - 更新策略:RollingUpdate +
partition(灰度)、OnDelete - Multi-Attach:RWO 卷同一时刻单节点挂载
- 节点故障:Pod 卡 Terminating 不自动重建(防脑裂)
考官追问链:
- 选型层:为什么这个应用不能用 Deployment?StatefulSet 到底多给了你哪三样东西?
- 网络/存储层:
pod-0的固定 DNS 怎么来的(Headless Service 的作用)?每个副本的盘是怎么和它绑定、且重建后还挂原盘的? - 调度边界层:云盘是绑 AZ 的,如果 Pod 被调度到别的可用区会怎样?
volumeBindingMode的两个取值有什么区别,Immediate会踩什么坑? - 数据安全层:
kubectl scale --replicas=1缩容后,被缩掉副本的 PVC 会被删吗?删 StatefulSet 会删数据吗?这样设计的用意? - 故障层:节点宕机时
mysql-0为什么卡在 Terminating 不在别处重建?强删有什么风险?Multi-Attach error 什么时候出现?
参考答题骨架:
为什么用 StatefulSet(三大保证)
- 稳定网络标识:Pod 名固定有序(
mysql-0、mysql-1),重建后名字不变;配合 Headless Service 每个 Pod 有稳定 DNSmysql-0.mysql.ns.svc.cluster.local——集群成员互相寻址(如主从、Raft peer)靠这个。 - 稳定存储:
volumeClaimTemplates为每个副本生成专属 PVC(data-mysql-0…),PVC 与序号绑定,Pod 重建/迁移后仍挂回原来那块盘。 - 有序:默认按 0→N-1 顺序启动、N-1→0 顺序终止、滚动更新也有序(可用
podManagementPolicy: Parallel改并行)。
关键配置与考量
- Headless Service(
clusterIP: None):DNS 直接返回各 Pod IP,提供每 Pod 稳定域名。 - 存储拓扑(高频坑):云盘通常是 zonal(绑定单可用区)。若 Pod 被调度到与其 PV 不同的 AZ,就挂不上。用 StorageClass 的
volumeBindingMode: WaitForFirstConsumer——先由调度器选好节点,再在同 AZ 创建/绑定盘;用默认的Immediate会先建盘再调度,容易盘和 Pod 不同区导致 Pending。 - 扩缩容与数据保留:缩容 StatefulSet 默认不删 PVC(防误删数据),要手动清理孤儿 PVC;
persistentVolumeClaimRetentionPolicy的whenScaled/whenDeleted可配Retain/Delete。PV 的reclaimPolicy生产用Retain,删 PVC 也不真删底层盘。 - 更新策略:
RollingUpdate+partition: N做灰度(只更新序号 ≥ N 的 Pod,验证无误再降 partition);OnDelete则完全手动。 - Multi-Attach:
ReadWriteOnce卷同一时刻只能被一个节点挂载。Pod 跨节点迁移时,若旧节点还没释放卷,新 Pod 报Multi-Attach error——常见于节点硬宕机。 - 节点故障下的脑裂防护:节点
unreachable时mysql-0进Terminating但不自动重建,因为 K8s 无法确认老实例真死了,贸然在别处拉起同标识 Pod 挂同一逻辑数据可能双写脑裂。需人工确认(fencing)后再--force删除。
踩坑 / 加分点:
- 缩容留下的孤儿 PVC 是隐性成本泄漏,也可能在扩容回来时挂到脏数据;要么配
whenScaled: Delete,要么纳入巡检。 volumeBindingMode: Immediate跨 AZ 踩坑极常见:多 AZ 集群里先建盘后调度,盘在 A 区、Pod 被排到 B 区 → 永久 Pending。- 节点宕机时无脑
kubectl delete pod --force --grace-period=0是数据脑裂高发操作(主库双写);正确做法先物理断电/fencing 再删。 - 备份不能只备 etcd:etcd 只有元数据,业务数据在 PV 里,要针对 PVC 做卷快照 + 应用层逻辑备份(如 mysqldump/物理备份)。
- RWO vs RWX 选型:需要多 Pod 同时读写(如共享文件)要用 RWX(NFS/CephFS/云文件存储),块存储只能 RWO。
- 现实建议加分:数据库能用云托管(RDS)就别自己上 StatefulSet;确要自建,用成熟 Operator(如 mysql-operator、CNPG)比裸 StatefulSet 稳得多,它们把主从切换/备份/fencing 都封装了。
锚点题 8:一个操作报「Forbidden」权限错误,你怎么定位到根因?#
为什么是锚点题:RBAC 是生产权限的地基,CI 机器人、Operator、SA 权限报错是运维日常。这题好在报错信息本身就是完整线索,考的是你懂不懂 RBAC 的主体→绑定→角色→规则四段式,以及那些「apiGroup 写错」「子资源要单独授权」的细节坑。
会串出的知识点地图:
- 报错解析:
User "system:serviceaccount:ns:sa" cannot get resource "pods" in API group "" in namespace "ns"——who / verb / resource / apiGroup / namespace 全在里面 - 定位工具:
kubectl auth can-i <verb> <resource> --as=... -n ns、auth can-i --list - RBAC 四段式:主体(User/Group/ServiceAccount)→(Cluster)RoleBinding →(Cluster)Role → rules(apiGroups + resources + verbs)
- 命名空间 vs 集群级:Role/RoleBinding vs ClusterRole/ClusterRoleBinding;RoleBinding 引用 ClusterRole 只在该 ns 生效
- 常见坑:apiGroup 空串(core)、子资源(pods/log、pods/exec、deployments/scale)需单独授权、verb 粒度(get/list/watch 分开)、SA 没挂到 Pod
考官追问链:
- 读错误层:这条 Forbidden 报错里,能直接读出哪几个关键信息?(who/verb/resource/apiGroup/ns)
- 验证层:不改任何东西,怎么快速验证「这个 SA 到底有没有这个权限」、以及「它一共有哪些权限」?
- 模型层:RBAC 从主体到最终权限是怎么串起来的?Role 和 ClusterRole、RoleBinding 和 ClusterRoleBinding 的区别?
- 细节坑层:为什么「明明给了 deployments 权限还是报错」?(apiGroup 写成 core / 子资源没授权 / verb 缺 list)
- 主体层:Pod 里的进程默认用哪个 ServiceAccount?为什么不写
serviceAccountName常常直接没权限?
参考答题骨架:
第一步 · 读懂报错
Error from server (Forbidden): pods is forbidden:
User "system:serviceaccount:app:ci-bot" cannot list resource "pods"
in API group "" in namespace "app"一句话拆出五要素:主体 ci-bot(app 命名空间的 SA)、verb list、resource pods、apiGroup ""(core 组)、namespace app。
第二步 · 用 can-i 复现 + 列权限
kubectl auth can-i list pods --as=system:serviceaccount:app:ci-bot -n app # 复现:no
kubectl auth can-i --list --as=system:serviceaccount:app:ci-bot -n app # 看它到底有哪些权限第三步 · 顺着四段式找断点 主体 → 绑定 → 角色 → 规则:
kubectl get rolebindings,clusterrolebindings -A -o wide | grep ci-bot # 找绑定
kubectl get role <role> -n app -o yaml # 看引用的 Role 的 rules逐项核对 rules 里的 apiGroups / resources / verbs 是否覆盖了报错要的组合。
RBAC 模型速记
- 主体:User / Group / ServiceAccount。
- Role/RoleBinding:namespace 级;ClusterRole/ClusterRoleBinding:集群级。
- RoleBinding 可以引用 ClusterRole——复用通用角色定义,但权限只在该 RoleBinding 所在的 namespace 生效(高频误解点)。
踩坑 / 加分点:
- apiGroup 写错是头号坑:core 资源(pods、services、configmaps)的 apiGroup 是空串
"";deployments 在apps;ingress 在networking.k8s.io。把 deployments 写进 core 组,规则就是不生效还不报语法错。 - 子资源要单独授权:
pods/log、pods/exec、pods/portforward、deployments/scale都是独立 resource——给了pods不等于能kubectl logs/exec。 - verb 粒度:
get、list、watch是分开的;控制器常还需要create/update/patch/delete。「能看单个但kubectl get pods报错」往往是缺list。 - SA 没挂到 Pod:Pod 不写
spec.serviceAccountName就用 namespace 的defaultSA,而 default 通常没任何权限——「代码里调 API 报 Forbidden」经常是这个。 - RoleBinding 引 ClusterRole 只在本 ns 生效的误解会导致「明明绑了 cluster-admin 还是跨不了 ns」。跨 ns/集群级操作要用 ClusterRoleBinding。
- 加分:优先复用内置 ClusterRole(
view/edit/admin/cluster-admin)+ 用aggregationRule聚合;生产审计要抓通配符*和cluster-admin的滥用;给 CI 机器人按最小权限精确到具体 resource/verb(见5-方案设计型的 CI 机器人 RBAC 设计)。
复习节奏建议:考前把这 8 道锚点题的「考官追问链」当口播稿,每题限时 5
8 分钟一口气讲完;讲不顺的层回到对应的 15 类精修。面试时被问到任一相关点,都可以主动把答案「升维」成这里的一棵树——展现体系感,正是高级岗和普通岗的分水岭。