前言
周四晚经历了一场面试,整体来说答得不是很好,总共就面试了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 | kubectl logs <pod-name> -n <ns> # 当前容器 |
–previous 是排查 CrashLoopBackOff 的关键:容器已经重启了,当前日志是空的,必须看崩溃前的日志。
4. 典型场景
场景 A:CrashLoopBackOff
按优先级排查:
- 应用配置/启动参数错误:日志里通常直接有报错(NPE、配置文件缺失、端口冲突)
- 依赖不可达:数据库、下游服务连不上 → 检查 Service/DNS(nslookup kubernetes.default)、NetworkPolicy
- 探针配置不当:livenessProbe 太严格,应用还没启动完就被杀 → 调大 initialDelaySeconds 或改用 startupProbe
- 重启循环加速排查:可以临时改 command 为 sleep 3600 让容器保持存活,然后 kubectl exec -it … – /bin/sh 进去手动执行启动命令,现场调试
场景 B:OOMKilled
- 确认是哪种 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 记录
- 分析内存去向:
- kubectl top pod –containers 看实时用量趋势
- Java 应用:看堆配置 -Xmx 是否超过了 limit(堆外内存、metaspace、线程栈都要算进去,一般建议 -Xmx 设为 limit 的 60%~75%)
- 接入 Prometheus 的话看 container_memory_working_set_bytes 的曲线,判断是缓慢增长(内存泄漏)还是瞬时尖峰(突发流量)
- 处置:尖峰 → 调大 limit;泄漏 → dump 分析(jmap/jcmd),结合 heap dump 找大对象
5. 环境与资源层排查
如果上面没定位到,往下查:
| 层面 | 命令/手段 |
|---|---|
| 节点资源 | kubectl describe 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 | Pending → Running → Succeeded / Failed |
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 | 1. Pod 标记为 Terminating |
常见踩坑点:
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 | 主动方发 FIN → 收到对方 ACK → 收到对方 FIN → 回 ACK |
设置 2MSL 的目的有两个:
保证最后一个 ACK 丢失时,对方重发 FIN 能收到响应(否则对方会收到 RST)
让本连接的报文在网络中自然消亡,防止新连接复用相同四元组时收到旧连接的延迟报文
所以单机出现大量 TIME_WAIT,本质说明:这台机器在高并发地主动关闭短连接。典型场景:
短连接 HTTP 服务(压测、爬虫、高频 API 调用)
没有启用 keep-alive 的连接复用
服务作为客户端大量调用下游(如网关、BFF 层)
补充:TIME_WAIT 本身不是故障,它是协议正确性的保障。问题在于它占用
ip_local_port_range端口和连接跟踪表,量太大才会出问题。
二、排查思路
1. 确认规模和分布
1 | # 统计各状态连接数 |
重点看 TIME_WAIT 的对端 IP:Port 分布,能直接定位是哪个下游依赖在产生短连接。
2. 判断是否真的造成了影响
1 | # 可用端口范围 |
如果 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 | # 复用 TIME_WAIT 连接(安全,推荐):允许将 TIME_WAIT 的端口用于新的 outbound 连接 |
注意
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)也可能成为瓶颈
四、总结
TIME_WAIT 出在主动关闭方,是协议正常行为,不是 bug
排查三板斧:
ss -s看总量 →ss -ant state time-wait看对端分布 → 对照ip_local_port_range判断是否端口耗尽解决上先治本(连接池复用)再治标(
tcp_tw_reuse+ 扩大端口范围),禁用tcp_tw_recycle真正的报错形态是
Cannot assign requested address,看到这个就优先怀疑端口耗尽
后续
后面还问了SRE agent是怎么做的,有遇到什么问题,这块还好。本来AI就比较新,我自认为没啥问题。
反问
| # | 问题 | 回答 |
|---|---|---|
| 1 | 这个岗位的工作内容和日常是什么?后续需要做什么? | (介绍了岗位职责) |
| 2 | 你们运维基建的建设程度如何?是自建的还是用云上能力? | 以自建为主 |
| 3 | 对多云架构怎么看、怎么用? | 有介绍多云使用策略 |
| 4 | 一朵云故障时,快切时间能做到多久? | 目前做不到快速切换,为手动切换 |
| 5 | 切换手段是什么? | DNS 层面直接切换 |
写在后面
基础知识稍微回炉重造一下。包括基础的运维、还有网络、K8S。
最近架构师考试过了就有点膨胀,我其实之前很长一段时间一直是认为架构师管顶层设计其实就好了,但实际上基础东西还是挺重要的,往往就疏漏在细节上。
持续学习。