Kubernetes面试题精选

2026年 阅读约 11 分钟 面试指南 · K8s面试

Kubernetes面试题精选:master与node架构组件、Pod生命周期与三种探针、工作负载区别、Service网络模型、资源requests与limits、滚动更新回滚、常见故障排查,附YAML示例。

一句话总结

Kubernetes 是一个声明式容器编排平台:你用 YAML 声明"期望状态"(要跑几个副本、什么镜像、暴露什么服务),集群里的控制器不断调谐(Reconcile)把实际状态拉回期望状态——这就是"自愈、扩缩容、滚动更新"的统一原理。架构上分控制面(etcd 存状态、api-server 唯一入口、scheduler 调度、controller-manager 执行调谐)和工作节点(kubelet 管 Pod、kube-proxy 管转发、容器运行时跑容器)。面试主线:架构组件 → 工作负载与网络 → 存储与调度 → 排障。

# 一句话串起核心对象: # Pod 是最小调度单位(一组共享网络存储的容器); # Deployment 管无状态副本与滚动更新,StatefulSet 管有稳定身份的有状态服务; # Service 给漂移的 Pod 一个稳定虚拟 IP + DNS 名; # ConfigMap/Secret 管配置,PV/PVC 管存储,RBAC 管权限。

初级理解

集群架构组件

层面组件职责
控制面etcd唯一数据源,存全部集群状态(KV)
控制面apiserver唯一入口:认证授权、准入、REST 语义,其余组件都经它读写
控制面scheduler给未调度 Pod 选节点(过滤 + 打分)
控制面controller-manager一堆控制器(Deployment/ReplicaSet/Node…)执行调谐逻辑
工作节点kubelet按 PodSpec 管理本节点 Pod 生命周期、上报状态
工作节点kube-proxy维护 Service 的转发规则(iptables/IPVS)
工作节点容器运行时containerd / CRI-O,通过 CRI 接口拉起容器

Pod:最小调度单位

# Pod = 一组"绑死"在一起的容器: # - 共享网络命名空间:同 IP、同 localhost、端口不能冲突 # - 共享存储卷:容器间可通过 volume 交换文件 # - 同生共死:一起调度到某节点,一起重启(同一 pause 容器兜底网络) # 为什么不是一个容器包打天下?——亲密性:主容器旁挂日志收集/网格 sidecar, # 它们必须与主容器同机同网络,但生命周期/资源可以独立管理 # Pod 是原子的、可抛弃的:节点挂了 Pod 会在别处重建,IP 会变—— # 所以绝不能直接依赖 Pod IP,这就是 Service 存在的意义

工作负载类型怎么选

类型适用关键特征
Deployment无状态服务(Web/API)通过 ReplicaSet 管副本,支持滚动更新/回滚
StatefulSet有状态(数据库/MQ/Kafka)稳定网络标识 app-0/1/2、稳定存储、有序部署扩缩
DaemonSet每节点一个(日志/监控 agent)新节点加入自动铺一个
CronJob定时任务按 cron 表达式创建 Job
Job一次性任务跑完成功即止,可配 backoffLimit 重试

Service 四种类型

# ClusterIP(默认):集群内虚拟 IP,只能集群内访问 # NodePort:在每节点开一个端口(30000-32767)对外暴露,经节点 IP 转到 Service # LoadBalancer:云厂商 LB + NodePort(要花钱),生产对外服务的标配 # ExternalName:CNAME 指向外部域名,把外部服务"引入"集群 # Service 本质:一组 Pod 的稳定访问入口。kube-proxy 为每个 Service 维护 # iptables/IPVS 规则 + Endpoints 列表(就绪 Pod 的 IP 集合), # 访问 ClusterIP:Port 的流量被 DNAT 转发到某个就绪 Pod(默认随机负载均衡)

中级深入

三种探针与 Pod 生命周期

livenessProbe: # 活着吗?失败 → 重启容器(K8s 自愈的边界在哪靠它) httpGet: { path: /healthz, port: 8080 } periodSeconds: 10 failureThreshold: 3 readinessProbe: # 就绪吗?失败 → 从 Service Endpoints 摘除(不重启,只是不给流量) httpGet: { path: /ready, port: 8080 } periodSeconds: 5 startupProbe: # 启动好了吗?给慢启动应用"护航",成功前不跑 liveness httpGet: { path: /healthz, port: 8080 } failureThreshold: 30 periodSeconds: 10
提示:三者分工是高频考点:liveness 管"要不要重启"、readiness 管"要不要给流量"、startup 管"给慢启动应用缓冲期"。liveness 探得太急(依赖外部 DB)会造成雪崩重启,readiness 不通则流量全打到健康实例上。

优雅终止与资源 QoS

# 删除 Pod 时的流程(优雅终止): # 1. Pod 从 Endpoints 摘除(不再进新流量) # 2. 执行 preStop 钩子(给应用"收尾"时间,比如等在途请求结束) # 3. 发 SIGTERM,应用自行清理退出 # 4. 等 terminationGracePeriodSeconds(默认 30s)耗尽仍未退 → SIGKILL 强杀 # requests/limits 与 QoS 等级(决定节点资源紧张时谁先被驱逐): # requests = 调度依据(scheduler 按它选节点)+ 保底配额 # limits = 上限:CPU 超了被限流(throttle),内存超了 OOMKilled # Guaranteed:requests==limits 全配齐 → 最后才被驱逐 # Burstable :requests < limits → 中等 # BestEffort:都没配 → 内存紧张第一个被驱逐

调度流程与亲和性

# 调度 = 过滤(Filter)→ 打分(Score)→ 绑定 # 过滤:资源不够、端口冲突、nodeSelector/Affinity 不匹配、Taint 不能容忍 → 剔除节点 # 打分:资源均衡度、亲和命中、数据局部性等维度加权选最优 # nodeSelector:最简单的按标签选节点 nodeSelector: { disk: ssd } # Affinity:支持"软偏好(preferred)"与复杂表达式 affinity: podAntiAffinity: # 反亲和:同类 Pod 分散到不同节点/可用区,防单点 preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: topologyKey: topology.kubernetes.io/zone labelSelector: matchLabels: { app: web } # Taint/Toleration:节点"拒绝"某些 Pod(专用节点/预留资源): # kubectl taint nodes n1 dedicated=db:NoSchedule # Pod 需声明 toleration 才能调度上去;NoExecute 影响已在节点上的 Pod

配置与存储

# ConfigMap:非敏感配置(可挂文件或注入环境变量) # Secret:敏感信息(base64 编码,可配合加密静态数据 etcd encryption),类型: # Opaque / kubernetes.io/tls / kubernetes.io/dockerconfigjson # 注意:Secret 也只是 base64,真正安全要靠 RBAC 限读 + 静态加密 + 不进 Git # 存储三件套: # PV(集群资源,运维预供)←PVC(用户申请,"我要 10Gi 的 gp3")→ Pod 挂载 # StorageClass:动态供给——PVC 创建时由 provisioner 自动创建 PV(云盘/NFS), # 省掉运维手工建 PV;accessMode:ReadWriteOnce / ReadOnlyMany / ReadWriteMany

高级进阶

集群网络模型

# K8s 网络三大铁律(CNI 插件必须实现): # 1. 每个 Pod 一个全局唯一 IP,Pod 之间可不经过 NAT 直接通信 # 2. 节点上的 agent(kubelet)可与该节点上所有 Pod 通信 # 3. Pod 看到的自己的 IP = 别人看到的它的 IP # Service 访问链路(iptables 模式): # Client Pod → ClusterIP:Port # → iptables KUBE-SVC 链按概率选中某个 KUBE-SEP(就绪 Pod 的 endpoint) # → DNAT 改写目标 IP 为 Pod IP → 节点路由/CNI 转发到目标 Pod # IPVS 模式:规则在 conntrack/哈希表里,大规模 Service 下性能远优于 iptables 链式规则 # DNS:同 namespace 访问 mysvc,跨 namespace 用 mysvc.namespace.svc.cluster.local # Service 由 kube-dns/CoreDNS 提供 A 记录(指向 ClusterIP),headless(clusterIP: None)返回 Pod IP 列表 # Ingress:七层路由(按域名/路径分发到不同 Service),本身只是规则声明, # 由 ingress-nginx/envoy 等 controller 实现,可配 TLS 终止 —— 对外暴露的主流方案

滚动更新与回滚

strategy: rollingUpdate: maxSurge: 25% # 更新期间最多多出 25% 的 Pod(先起新的) maxUnavailable: 25% # 最多允许 25% 不可用(再删旧的) # Deployment 每次更新产生新的 ReplicaSet,旧 RS 保留(默认留 10 个历史) # 回滚 = rollout undo 指回旧 RS: # kubectl rollout undo deployment/web # kubectl rollout undo deployment/web --to-revision=3 # 配合 readinessProbe:新 Pod 没 ready 前不进流量 → 真正的"零中断"发布 # HPA(水平扩缩):按指标(CPU/内存/自定义/对象)调整 replicas # metrics-server 提供资源指标;缩容有 stabilizationWindow 防抖,扩容立即生效

RBAC 与故障排查方法论

# RBAC:Role/ClusterRole(能做什么)+ RoleBinding/ClusterRoleBinding(绑给谁) # 最小权限原则:应用只拿自己 namespace 的读写;集群级监控才给 ClusterRole # 排查方法论(自上而下): # 1. kubectl get pods -o wide —— 看状态(Running? CrashLoopBackOff? Pending?) # 2. kubectl describe pod x —— 看 Events:调度失败/拉镜像失败/探针失败一目了然 # 3. kubectl logs x [-c 容器名] --previous —— 看崩溃容器日志(--previous 是上次崩溃的) # 4. kubectl top pod —— 看资源实际用量(需 metrics-server) # 常见状态速查: # Pending → 资源不足 / nodeSelector 无匹配节点 / 有 PV 绑不上 # ImagePullBackOff → 镜像名错/私有仓库没配 imagePullSecret/仓库不通 # CrashLoopBackOff → 容器启动即退出:看 logs --previous,多半是启动参数/配置错 # OOMKilled → 内存超 limits:调 limits 或查内存泄漏 # Error → 主进程非 0 退出:看退出码与日志

实战场景

一个生产可用的 Deployment + Service

apiVersion: apps/v1 kind: Deployment metadata: { name: web, namespace: prod } spec: replicas: 3 selector: { matchLabels: { app: web } } template: metadata: { labels: { app: web } } spec: containers: - name: web image: registry.example.com/web:v1.2.3 # 生产必须锁定版本号,忌 latest ports: [{ containerPort: 8080 }] resources: requests: { cpu: "200m", memory: "256Mi" } limits: { cpu: "1", memory: "512Mi" } livenessProbe: { httpGet: { path: /healthz, port: 8080 }, initialDelaySeconds: 10 } readinessProbe: { httpGet: { path: /ready, port: 8080 }, periodSeconds: 5 } envFrom: [{ configMapRef: { name: web-config } }] --- apiVersion: v1 kind: Service metadata: { name: web, namespace: prod } spec: selector: { app: web } # 与 Pod labels 匹配,自动维护 Endpoints ports: [{ port: 80, targetPort: 8080 }]

发布后异常的排查顺序(实战模板)

# 1. kubectl get rs | grep web —— 新 ReplicaSet 的 desired/ready 是否到 replicas # 2. kubectl rollout status deploy/web —— 卡在哪一步(新 Pod 起不来 / 老 Pod 不退) # 3. kubectl describe pod web-xxx —— Events 里找探针失败/调度失败/存储挂载失败 # 4. kubectl logs web-xxx --previous —— 崩溃现场 # 5. 排除项:镜像 tag 是否被覆盖(latest)、configMap 改名没同步、secret 没重建、 # 资源 limits 太小被 OOM、就绪探针路径升级后 404 导致永远不进 Endpoints # 6. 兜底:kubectl rollout undo deploy/web 回滚止血,再复盘
注意:ConfigMap/Secret 以文件挂载时,kubelet 更新后容器内不会瞬时生效(同步有延迟),且应用若不监听文件变化需要主动重载;改配置后确认应用层是否需要重启——"改了配置没生效"是高频事故。

面试模拟

面试官:Pod 到 Service 的请求是怎么到达容器的?

你:Service 只是抽象,背后是 kube-proxy 维护的转发规则。客户端 Pod 访问 Service 的 ClusterIP:Port,出口流量先到节点,被 iptables(或 IPVS)规则 DNAT——按概率选中一个就绪 Pod 的 Endpoint(IP:Port),改写目标后经 CNI 网络转发到目标 Pod 的容器。ClusterIP 本身并不真的"存在"于网卡上,它只是规则里的虚拟地址;负载均衡在 iptables 概率规则或 IPVS 调度算法里完成。同 namespace 可以直接用服务名,DNS(CoreDNS)把名字解析成 ClusterIP。

面试官:liveness 和 readiness 探针的区别?如果 liveness 探测写成查数据库会怎样?

你:liveness 决定容器要不要重启,readiness 决定 Pod 要不要接流量、不触发重启。如果 liveness 去查数据库,DB 抖动时所有实例会同时探活失败→全部重启,而重启并不能修复 DB 问题,只会雪上加霜——数据库依赖应该放在 readiness 或应用内部降级里。liveness 只应探测"进程自身是否健康"这种重启能解决的问题。慢启动应用再加 startupProbe 给缓冲期,避免启动阶段就被 liveness 杀掉。

面试官:Deployment 和 StatefulSet 的区别?什么时候用哪个?

你:Deployment 面向无状态:副本是可互换的,名字随机、存储临时、随便扩缩和替换,更新走滚动替换 ReplicaSet。StatefulSet 面向有状态:每个副本有稳定网络标识(web-0、web-1)和稳定的持久存储(PVC 按序号绑定,重建后还挂原来的盘),部署/扩缩/更新都按序进行——适合数据库、Kafka 这类"节点身份有意义"的系统。判断标准就一条:副本之间是否可互换、是否需要稳定的持久化身份。