Zer0e's Blog

2026面试复盘2

字数统计: 4.3k阅读时长: 16 min
2026/08/22 Share

前言

周四晚经历了一场面试,整体来说答得不是很好,总共就面试了20分钟左右。基础知识很多都忘了,写篇文章顺道复习下。
至于为什么这篇是2,1的话由于我没录音所以很难整理,但问的没那么基础,有时间再整理下。

复盘

这次面试的是一个高级运维岗,除了一个项目外,基本都是围绕运维体系提问。

介绍一下 K8s 集群的核心组件

面试的时候回答了一些,比如APIServer、kube-scheduler、coredns、CNI 组件、CSI 插件、kube-proxy(结果名字忘了)、Ingress。但是感觉回答的没有一个层次,正好趁现在把K8S的组件梳理一下。
K8s 集群组件分为几个部分:控制平面(Control Plane)、 工作节点(Worker Node)、核心插件。

控制面组件

kube-apiserver(集群入口)

  • 是整个集群的唯一入口,所有组件(包括 kubectl、kubelet、controller)都通过它通信
  • 提供 RESTful API,负责认证、鉴权、准入控制
  • 是唯一直接读写 etcd 的组件,其他组件都通过它间接访问

etcd(集群存储)

  • 分布式 KV 存储,保存集群的所有期望状态和实际状态(Pod、ConfigMap、Secret 等)
  • 基于 Raft 协议保证一致性,生产环境建议奇数节点(3/5)部署

kube-scheduler(调度器)

  • 负责把未调度的 Pod 分配到合适的 Node 上
  • 调度分两个阶段:过滤(Predicates/Filtering) 筛掉不满足条件的节点,打分(Priorities/Scoring) 给候选节点打分选最优
  • 支持亲和性、污点容忍、资源请求等调度策略

kube-controller-manager(控制器)

  • 包含一系列控制器(Deployment、ReplicaSet、Node、Endpoint 等)
  • 核心职责是调谐循环(Reconcile Loop):不断对比 etcd 中的期望状态和实际状态,有偏差就纠正
  • 比如 ReplicaSet 控制器发现副本数不足就创建新 Pod

工作节点组件

kubelet(节点代理)

  • Node 上的核心代理进程,接收 apiserver 下发的 PodSpec
  • 负责容器的生命周期管理:创建、启动、停止容器,执行探针(liveness/readiness)
  • 定期向 apiserver 上报节点状态

kube-proxy(网络代理)

  • 负责 Service 的网络代理和负载均衡
  • 三种模式:userspace(早期)→ iptables(主流,规则数多时性能下降)→ IPVS(大规模集群推荐,基于哈希表,O(1) 查找)

容器运行时(Container Runtime)

  • 真正运行容器的组件,如 containerd、CRI-O
  • 1.24 之后移除了 dockershim,Docker 需要通过 containerd 间接使用

核心插件

  • CoreDNS:集群内部 DNS 解析
  • CNI 网络插件:Calico / Flannel / Cilium,负责 Pod 网络互通
  • kube-scheduler / controller-manager 之外的可观测组件:metrics-server、监控(Prometheus)、日志(EFK/Loki)

一个例子

kubectl apply 一个 Deployment 后
apiserver 接收并写入 etcd → scheduler 发现新 Pod 未调度,选定 Node 并回写 → 目标节点的 kubelet watch 到任务,调用容器运行时拉起容器 → kube-proxy 更新 Service 转发规则 → controller-manager 持续保证副本数符合期望

感想

还是要整体看下K8S官网。

如果一个 Pod 出现异常(比如 Crash / OOM),你一般怎么排查

这个回答的也不是很好,只说了看状态、还有日志啥的。

一般遵循”先看状态 → 再看事件 → 再看日志 → 最后深入资源”的排查路径,Crash 和 OOM 会作为两类典型情况分别处理。

1. 看 Pod 状态

1
kubectl get pod <pod-name> -n <ns> -o wide

重点关注 STATUS 和 RESTARTS:

  • CrashLoopBackOff:容器反复崩溃,退避重启
  • OOMKilled:上一次退出原因是内存超限
  • Pending:还没调度上去(资源不足/污点)
  • ImagePullBackOff:镜像问题

2. 看详细事件

1
kubectl describe pod <pod-name> -n <ns>

重点看三块:

  • State / Last State:Exit Code 是核心线索
    • 137(128+9,SIGKILL)→ 大概率 OOMKilled
    • 139(SIGSEGV)→ 段错误
    • 1/2 → 应用自身报错退出
    • 143(SIGTERM)→ 被优雅终止(可能是探针杀的)
  • Events:有没有 OOMKilling、Back-off restarting、Liveness probe failed、Unhealthy 等事件
  • Conditions:Pod 是否就绪

3. 看容器日志

1
2
3
kubectl logs <pod-name> -n <ns>            # 当前容器
kubectl logs <pod-name> -n <ns> --previous # 上一个崩溃实例的日志(排查 Crash 必备)
kubectl logs <pod-name> -c <container> # 多容器时指定容器

–previous 是排查 CrashLoopBackOff 的关键:容器已经重启了,当前日志是空的,必须看崩溃前的日志。

4. 典型场景

场景 A:CrashLoopBackOff
按优先级排查:

  1. 应用配置/启动参数错误:日志里通常直接有报错(NPE、配置文件缺失、端口冲突)
  2. 依赖不可达:数据库、下游服务连不上 → 检查 Service/DNS(nslookup kubernetes.default)、NetworkPolicy
  3. 探针配置不当:livenessProbe 太严格,应用还没启动完就被杀 → 调大 initialDelaySeconds 或改用 startupProbe
  4. 重启循环加速排查:可以临时改 command 为 sleep 3600 让容器保持存活,然后 kubectl exec -it … – /bin/sh 进去手动执行启动命令,现场调试

场景 B:OOMKilled

  1. 确认是哪种 OOM:
    • 容器级 OOM:Events 里有 OOMKilling,Last State: Terminated, Reason: OOMKilled, Exit Code: 137 —— 容器 memory limit 被打满
    • 节点级 OOM:kubelet 按 QoS 等级驱逐 Pod(BestEffort 先死),kubectl describe node 看 MemoryPressure,dmesg 里有 oom-killer 记录
  2. 分析内存去向:
    • kubectl top pod –containers 看实时用量趋势
    • Java 应用:看堆配置 -Xmx 是否超过了 limit(堆外内存、metaspace、线程栈都要算进去,一般建议 -Xmx 设为 limit 的 60%~75%)
    • 接入 Prometheus 的话看 container_memory_working_set_bytes 的曲线,判断是缓慢增长(内存泄漏)还是瞬时尖峰(突发流量)
  3. 处置:尖峰 → 调大 limit;泄漏 → dump 分析(jmap/jcmd),结合 heap dump 找大对象

5. 环境与资源层排查

如果上面没定位到,往下查:

层面 命令/手段
节点资源 kubectl describe node 、kubectl top node
调度问题 Pending 时看 Events 里 Insufficient cpu/memory、污点 Taints
存储 PVC 是否 Bound,挂载失败也会 Crash
节点本身 journalctl -u kubelet、dmesg -T 看内核日志
网络/DNS kubectl run debug –rm -it –image=busybox – sh 临时 Pod 测试

对于进不去的容器,还可以用 kubectl debug 起一个 ephemeral container 共享目标容器的 PID/网络命名空间排查(针对 distroless 镜像特别有用)。

总结

信息收集顺序:get(现象)→ describe(事件+退出码)→ logs –previous(死因)→ 资源/节点层
退出码是第一线索,137 查内存、143 查谁发的信号、1/2 查应用本身
区分瞬时故障和持续故障:偶发 Crash 可能是节点抖动,反复 Crash 一定是确定性问题
长期治理:接入监控告警(Prometheus + AlertManager 覆盖 OOMKilled、CrashLoopBackOff 指标)、合理设置 requests/limits、Java 应用显式配置堆内存,把被动排查变成主动发现

如果一个 Pod 从 Pending 到 Ready 的时间比较长,可能有哪些因素?另外集群对 Pod 的生命周期管理是怎么样的?

这个回答的也不是很好。排查的时候其实能好好排查,但是说起来好像没那么容易。

第一部分:Pending 到 Ready 慢的排查

拆解成几个阶段,慢一定慢在其中某一段:

Pending(调度)→ 节点准备(拉镜像/挂卷)→ 容器启动 → Readiness 探针通过

阶段 1:Pending 时间长(调度慢)

因素 说明
资源不足 最常见,describe pod 事件里有 Insufficient cpu/memory,requests 之和超过节点可分配量
调度约束过严 nodeSelector、亲和性、污点无容忍(node had taint ... that the pod didn't tolerate)
PVC 无法绑定 Pending 事件显示 persistentvolumeclaim is not bound,存储插件未就绪或容量不够
调度器压力/积压 大规模集群批量发布时 scheduler 队列积压,可看 scheduler_pending_pods 指标
拓扑约束 topologySpreadConstraints、PV 有可用区限制,可调度范围太窄

阶段 2:已调度但容器未运行(节点准备慢)

因素 说明
镜像拉取慢 镜像体积大、跨区拉取、registry 限流(DockerHub 429)。优化:用内部镜像仓库、预热镜像(如 Nydus/DADI 按需加载)、imagePullPolicy: IfNotPresent
镜像层太多/太大 解压耗时,建议精简镜像、多阶段构建
volume 挂载慢 CSI driver 慢、网络存储(NFS/云盘 attach)耗时
kubelet / 运行时异常 节点负载高、containerd 卡死,看 journalctl -u kubelet

阶段 3:容器起来了但 Ready 慢

因素 说明
应用本身启动慢 JVM 加载、大量配置/缓存预热、启动时同步加载大数据。优化:懒加载、启动预热
readinessProbe 配置不合理 initialDelaySeconds 过大(白白等待)、periodSeconds 太长导致探测稀疏。推荐用 startupProbe 替代过大的 initialDelay
依赖服务未就绪 应用启动时连数据库/下游服务超时重试,拖慢启动
探针路径错误 一直 404/超时,Pod 永远不 Ready(这属于”不 Ready”而非”慢”,要区分)

排查手段小结:kubectl describe pod 的 Events 是时间戳最全的地方,能清楚看到 Scheduled → Pulling → Pulled → Started 各步骤耗时;配合 metrics-server / Prometheus 看节点和镜像拉取耗时即可定位到具体阶段。

第二部分:K8s 对 Pod 的生命周期管理

Pod 生命周期可以从 Pod 状态、容器状态、探针机制、终止流程 四个维度理解。

1. Pod 的五种状态(Phase)

1
2
3
4
5
Pending → Running → Succeeded / Failed

             ↑

          Unknown(节点失联)
  • Pending:已被系统接受,但未全部容器创建——等待调度或拉镜像

  • Running:已绑定节点,所有容器已创建,至少一个容器在运行

  • Succeeded:所有容器成功终止且不会重启(多见于 Job)

  • Failed:所有容器终止且至少一个失败退出

  • Unknown:通常 kubelet 与 apiserver 通信中断导致

2. 容器生命周期与 Hook

每个容器内部还有 Waiting / Running / Terminated 三种状态,另外支持两个生命周期钩子:

  • postStart:容器创建后立即执行(不保证在 entrypoint 之前),用于初始化注册等

  • preStop:容器终止前执行,优雅退出的关键,比如从注册中心摘除、sleep 等待流量排空

3. 三种探针

探针 作用 失败后果
livenessProbe 判断容器是否存活 杀死容器并按 restartPolicy 重启
readinessProbe 判断是否可接收流量 从 Service Endpoints 摘除,不重启
startupProbe 判断应用是否完成启动 未通过前屏蔽 liveness/readiness,保护慢启动应用

探测方式支持 httpGet / tcpSocket / exec / grpc。

4. 终止流程(优雅下线,面试高频考点)

这是生命周期管理的精华部分,kubectl delete pod 后发生的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
1. Pod 标记为 Terminating

2. 同时执行三件事(并行):

   ├─ Endpoints 中摘除该 Pod(不再接收新流量)

   ├─ 执行 preStop 钩子

   └─ 进入 terminationGracePeriodSeconds 倒计时(默认 30s)

3. preStop 结束后发送 SIGTERM 给容器主进程

4. 宽限期结束仍未退出 → 发送 SIGKILL 强杀

常见踩坑点:

  • Endpoint 摘除是异步的,流量可能还在路上 → 业界做法是 preStop 里 sleep 5~10s 等待连接排空

  • 如果应用不处理 SIGTERM,30s 后会被硬杀,造成请求中断 → 应用需注册 shutdown hook

5. restartPolicy 与 QoS

  • restartPolicy:Always(默认)/ OnFailure / Never,容器异常退出后按此策略重启,CrashLoopBackOff 的退避时间是 10s、20s、40s… 指数增长,上限 5 分钟

  • QoS 等级(由 requests/limits 决定):Guaranteed > Burstable > BestEffort,节点资源紧张时按此顺序驱逐,这也是生命周期管理的一部分

总结

“Pending 到 Ready 慢”本质就是生命周期前半段(调度 → 镜像 → 启动 → 探针)每一段的耗时叠加;K8s 的生命周期管理则通过状态机 + 探针 + Hook + 优雅终止这套机制,保证应用从启动到下线全程可控。优化方向对应起来就是:调度层面保证资源充足、镜像预热、startupProbe 保护慢启动、preStop + 优雅终止保证平滑下线。

一台 Linux 机器上出现大量 TIME_WAIT 状态的连接,是什么原因?怎么排查和解决?

网络知识都忘光了。

分原理 → 排查 → 解决三步来讲。

一、先讲清楚 TIME_WAIT 是怎么产生的

TIME_WAIT 是 TCP 四次挥手中的一个正常状态,只会出现在主动关闭连接的一方(这一点很关键):

1
2
3
4
5
主动方发 FIN → 收到对方 ACK → 收到对方 FIN → 回 ACK

                                          ↓

                            进入 TIME_WAIT,等待 2MSL(Linux 默认 60s)

设置 2MSL 的目的有两个:

  1. 保证最后一个 ACK 丢失时,对方重发 FIN 能收到响应(否则对方会收到 RST)

  2. 让本连接的报文在网络中自然消亡,防止新连接复用相同四元组时收到旧连接的延迟报文

所以单机出现大量 TIME_WAIT,本质说明:这台机器在高并发地主动关闭短连接。典型场景:

  • 短连接 HTTP 服务(压测、爬虫、高频 API 调用)

  • 没有启用 keep-alive 的连接复用

  • 服务作为客户端大量调用下游(如网关、BFF 层)

补充:TIME_WAIT 本身不是故障,它是协议正确性的保障。问题在于它占用 ip_local_port_range 端口和连接跟踪表,量太大才会出问题。

二、排查思路

1. 确认规模和分布

1
2
3
4
5
6
7
8
9
10
11
# 统计各状态连接数

ss -s

ss -ant | awk '{print $1}' | sort | uniq -c



# 看 TIME_WAIT 都连向哪些对端(定位是哪条链路在大量短连接)

ss -ant state time-wait | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn

重点看 TIME_WAIT 的对端 IP:Port 分布,能直接定位是哪个下游依赖在产生短连接。

2. 判断是否真的造成了影响

1
2
3
4
5
6
7
8
9
10
11
# 可用端口范围

cat /proc/sys/net/ipv4/ip_local_port_range     # 默认 32768-60999,约 2.8w 端口



# 端口耗尽的典型报错

dmesg | grep -i "out of socket memory"

# 应用日志:connect() failed: Cannot assign requested address
  • 如果 2.8w 端口 ÷ 60s 的 TIME_WAIT 存活时间 ≈ 单机约 460+ QPS 的主动关闭短连接才会打满端口,没到这个量级其实不用慌

  • 再看 net.ipv4.tcp_max_tw_buckets(默认值较大),超过后内核直接丢弃 TIME_WAIT 并打日志告警

3. 区分正常现象和异常

  • 对端分散、量随流量涨落 → 正常业务行为,优化方向是减少短连接

  • 对端集中 → 针对性治理该依赖

  • TIME_WAIT 只增不减 → 怀疑应用异常退出没走挥手,或内核参数被改

三、解决方案(按优先级)

方案 1:减少短连接(治本,首选)

手段 说明
开启连接池 / keep-alive HTTP client 复用连接(如 Go 的 Transport.MaxIdleConnsPerHost、连接池中间件),这是最有效的办法
下游走长连接 RPC gRPC/Thrift 天然复用
批量请求 把高频小请求合并

方案 2:内核参数调优(治标)

1
2
3
4
5
6
7
8
9
10
11
# 复用 TIME_WAIT 连接(安全,推荐):允许将 TIME_WAIT 的端口用于新的 outbound 连接

net.ipv4.tcp_tw_reuse = 1          # 依赖 tcp_timestamps,需开启

net.ipv4.tcp_timestamps = 1



# 扩大可用端口范围

net.ipv4.ip_local_port_range = 1024 65535

注意 tcp_tw_recycle:4.12 内核已删除该参数,它在 NAT 环境下会导致连接被丢(按时间戳快速回收会误伤 NAT 后多个客户端),生产禁用。

**不建议调小 tcp_fin_timeout**(它管的是 FIN_WAIT_2,不是 TIME_WAIT),这是一个常见误区。

方案 3:架构层面

  • 引入 LB / 网关收敛连接:让 LB 承担对下游的短连接 TIME_WAIT,LB 通常有更大的连接承载能力

  • 客户端分散:多实例分摊端口压力

  • 容器场景:使用 NAT 网络时注意 conntrack 表(nf_conntrack)也可能成为瓶颈

四、总结

  1. TIME_WAIT 出在主动关闭方,是协议正常行为,不是 bug

  2. 排查三板斧:ss -s 看总量 → ss -ant state time-wait 看对端分布 → 对照 ip_local_port_range 判断是否端口耗尽

  3. 解决上先治本(连接池复用)再治标(tcp_tw_reuse + 扩大端口范围),禁用 tcp_tw_recycle

  4. 真正的报错形态是 Cannot assign requested address,看到这个就优先怀疑端口耗尽

后续

后面还问了SRE agent是怎么做的,有遇到什么问题,这块还好。本来AI就比较新,我自认为没啥问题。

反问

# 问题 回答
1 这个岗位的工作内容和日常是什么?后续需要做什么? (介绍了岗位职责)
2 你们运维基建的建设程度如何?是自建的还是用云上能力? 以自建为主
3 对多云架构怎么看、怎么用? 有介绍多云使用策略
4 一朵云故障时,快切时间能做到多久? 目前做不到快速切换,为手动切换
5 切换手段是什么? DNS 层面直接切换

写在后面

基础知识稍微回炉重造一下。包括基础的运维、还有网络、K8S。

最近架构师考试过了就有点膨胀,我其实之前很长一段时间一直是认为架构师管顶层设计其实就好了,但实际上基础东西还是挺重要的,往往就疏漏在细节上。

持续学习。

CATALOG
  1. 1. 前言
  2. 2. 复盘
    1. 2.1. 介绍一下 K8s 集群的核心组件
      1. 2.1.1. 控制面组件
      2. 2.1.2. 工作节点组件
      3. 2.1.3. 核心插件
      4. 2.1.4. 一个例子
      5. 2.1.5. 感想
    2. 2.2. 如果一个 Pod 出现异常(比如 Crash / OOM),你一般怎么排查
      1. 2.2.1. 1. 看 Pod 状态
      2. 2.2.2. 2. 看详细事件
      3. 2.2.3. 3. 看容器日志
      4. 2.2.4. 4. 典型场景
      5. 2.2.5. 5. 环境与资源层排查
      6. 2.2.6. 总结
    3. 2.3. 如果一个 Pod 从 Pending 到 Ready 的时间比较长,可能有哪些因素?另外集群对 Pod 的生命周期管理是怎么样的?
      1. 2.3.1. 第一部分:Pending 到 Ready 慢的排查
        1. 2.3.1.1. 阶段 1:Pending 时间长(调度慢)
        2. 2.3.1.2. 阶段 2:已调度但容器未运行(节点准备慢)
        3. 2.3.1.3. 阶段 3:容器起来了但 Ready 慢
      2. 2.3.2. 第二部分:K8s 对 Pod 的生命周期管理
        1. 2.3.2.1. 1. Pod 的五种状态(Phase)
        2. 2.3.2.2. 2. 容器生命周期与 Hook
        3. 2.3.2.3. 3. 三种探针
        4. 2.3.2.4. 4. 终止流程(优雅下线,面试高频考点)
        5. 2.3.2.5. 5. restartPolicy 与 QoS
      3. 2.3.3. 总结
    4. 2.4. 一台 Linux 机器上出现大量 TIME_WAIT 状态的连接,是什么原因?怎么排查和解决?
      1. 2.4.1. 一、先讲清楚 TIME_WAIT 是怎么产生的
      2. 2.4.2. 二、排查思路
        1. 2.4.2.1. 1. 确认规模和分布
        2. 2.4.2.2. 2. 判断是否真的造成了影响
        3. 2.4.2.3. 3. 区分正常现象和异常
      3. 2.4.3. 三、解决方案(按优先级)
        1. 2.4.3.1. 方案 1:减少短连接(治本,首选)
        2. 2.4.3.2. 方案 2:内核参数调优(治标)
        3. 2.4.3.3. 方案 3:架构层面
      4. 2.4.4. 四、总结
    5. 2.5. 后续
    6. 2.6. 反问
  3. 3. 写在后面