每逢跳槽季,总有人拿着各种“k8s-经典面试题”合集临时抱佛脚。我在面试别人和被人面的过程中都反复感受到一件事:背熟答案的人很多,能顶着追问聊下去的人很少。比如问到“Pod创建过程中到底发生了什么”,能把组件名字念完的不少,能把“哪一步是谁在等谁、哪个环节失败会怎样”说清楚的,十个里也就一两个。
这篇文章不是让你背答案,而是把这些年我做容器化改造、面试候选人时反复被验证过的经典题目拆开讲——每道题背后的原理是什么,回答时该往哪个方向走,哪些话术会暴露你真的不太懂。准备K8s面试的人、日常维护集群但心里没底的人、以及要帮团队出题的人,都能从中找到对自己有用的部分。
1. 面试官想从“Pod创建流程”里听到什么
1.1 一条Pod从命令到容器的完整路径
这几乎是每场K8s面试的开场题。最浅层的回答是背组件名,像念菜单一样:kubectl、API Server、etcd、Scheduler、kubelet……念完就没了。真正能拉开差距的,是把每一步之间的依赖关系讲清楚,尤其是“谁在等待谁”。
完整路径是这样的。假设你把一份Deployment YAML提交上去,kubectl会把它POST给API Server;API Server先做认证、授权、准入控制,然后校验资源定义是否合法,合法就把对象序列化后写入etcd。注意这第一步里,Pod其实还没出现,出现在etcd里的只是Deployment这个期望状态——一个“3副本”的描述而已。
接着是Controller Manager里的Deployment控制器在干活。它从API Server Watch到新的Deployment,对比当前实例数和期望副本数,发现差3个,于是去创建ReplicaSet,再由ReplicaSet控制器创建3个Pod对象。Pod对象被写进etcd之后,Scheduler通过Watch感知到一批Pending状态的Pod,开始调度。
调度分两步:先筛选出满足条件的节点,再打分选出一个最优节点,把Pod的spec.nodeName字段回写到API Server。这一步很多人会漏掉关键细节——调度器本身不负责建容器,它只是给Pod算分、填字段,真正让容器在节点上跑起来的,是目标节点上的kubelet通过Watch感知到nodeName变化后触发的。
kubelet拿到指派给自己的Pod之后,按顺序做这几件事:创建PodSandbox(在容器运行时层面先准备好隔离环境),拉取镜像,启动业务容器,执行postStart钩子或探针(如果配置了),然后向API Server上报Pod状态。这里又是容易丢分的地方——很多人以为容器启动了Pod就Running了,实际上kubelet把Status回写,API Server更新etcd里的Pod对象,这个状态循环才闭合。
到这一步还没完。容器起来之后,CNI插件负责给Pod分配IP并把网卡接入虚拟网络;kube-proxy在节点上监听Service与Endpoints的变化,更新本机的转发规则。也就是说,你能看到Pod带着IP正常跑,其实是“容器运行 + 网络打通”两个动作都完成之后的结果。
1.2 回答的深度决定你的层级
同样是这题,候选人水平的差异在追问时特别明显。我常追问一句:“如果kubelet底下的容器运行时突然挂了,Controller Manager会不会重新创建一个Pod?”新手会答会,老手会说不会——Controller Manager只看etcd里的Pod对象数量,Pod对象还存在就不会重建;但Pod状态会持续异常,控制器会进入反复调谐的状态,直到节点失联或Pod被驱逐,副本数缺口拉大,Deployment控制器才会重建。
这个追问背后其实是做容器平台最在意的概念:控制面与数据面的解耦。面试时如果能主动点出“API Server和etcd是唯一权威来源,所有控制器都围绕自己的声明去调和现状”,考官基本就能判断你不是靠背的。
常见的丢分姿势也有几种:一是只说组件名不解释关系;二是把“创建Pod”当成一条直线的流水线,忽略Watch与回调机制;三是完全没提status回写和最终一致性。我建议答题时用“提交-存储-调谐-执行-回写”五段式来组织,既完整又不会越讲越乱。
2. 调度器与节点选择:从“怎么选”到“为什么这么选”
2.1 预选筛选与优选打分,调度不是随机挑节点
问过很多人“调度器是怎么选节点的”,得到的答案往往只有一句“哪个空闲选哪个”。这话不算错,但离完整太远。K8s的调度流程分成两个阶段:预选(Filter)和优选(Score)。
预选阶段,调度器把所有节点过一遍,筛掉不满足硬性条件的。常见的过滤器包括:节点资源够不够(Pod的requests与节点可分配量对比)、端口有没有冲突、存储卷能不能挂载上、节点是否被打过污点、Pod是否有节点亲和性约束。这个阶段是“一刀切”,不符合的就排除,不管节点多空闲。
优选阶段,在剩余节点上打分。常见的评分项有:CPU和内存的分配率(越不紧张得分越高)、镜像是否已经在节点上存在(避免拉镜像的等待)、Pod分散性偏好(把同一个ReplicaSet下的Pod尽量打散到不同节点)。最后调度器选总分最高的节点,如果分数相同,就轮询或随机选一个。
这里有个特别容易被忽略的细节:调度只看requests,不看limits,更不看实际运行时的用量。你在YAML里写resources.limits.cpu: 4,那只是运行时的一把锁,调度器不会因为limits大就把你安排到大机器上。我遇到过集群里有Pod疯狂占CPU但requests写得极低,调度器把它当“轻量级应用”继续往同节点塞其他Pod,最后整台节点CPU被打满。这类事在面试里经常以场景题形式出现,如果你能主动说出“调度决策基于requests”,基本就能过。
2.2 污点、容忍与亲和性:场景中才见真章
污点(Taint)和容忍(Toleration)是另一组高频题。概念上很好理解:污点是打在节点上的“排斥标记”,容忍是打在Pod上的“准许标记”,只有当Pod容忍了节点的某个污点,Pod才可能被调度上去。
但面试的深度不止于此。还要理解NoSchedule、NoExecute、PreferNoSchedule三种效果的区别。NoExecute最狠,不仅新Pod不会再上来,已经在节点上运行且不具备对应容忍的Pod还会被驱逐。这正好可以拿来说节点维护的场景:给节点打一个NoExecute污点,就能优雅地把上面的业务Pod赶走,再执行内核升级或硬件更换,比直接删节点规范得多。
亲和性(Affinity)容易和污点混在一起,其实解决的问题不同。亲和性是想让Pod“尽量/必须”到某些节点,污点是节点“拒绝”某些Pod。前者用于把大量读请求的服务调度到内存大的节点,后者用于在集群里隔离出专用计算区。
回答这类问题时最好带一个实际取舍:亲和性规则的表达力越强,调度器的计算开销和维护成本越高。一个生产集群如果到处塞nodeAffinity和podAntiAffinity,后面排障会非常难受。我见过最夸张的一次,某团队为了做容灾,给一个ReplicaSet写了三层反亲和,结果Pod在集群里腾挪不开,副本一直Pending,最后删掉规则才恢复。面试时能把这种反面案例说出来,比背十遍概念更有说服力。
3. 声明式API与控制循环:K8s的“操作系统”底层逻辑
3.1 声明式与命令式:别只停留在概念
这题看似简单,但能说出本质的人不多。命令式是你告诉系统“做什么、怎么做”:用kubectl run直接起一个Pod,然后自己盯着有没有挂掉;声明式是你告诉系统“最终要什么”,剩下的事情由控制面帮你完成。你在YAML里写replicas: 3,提交之后,维护3个副本就是控制器的职责,它不需要你告诉它怎么建,也不用你操心挂掉一个之后该如何应对。
更准确地说,声明式描述的是一个期望状态,系统的核心任务是让当前状态无限逼近这个期望状态。这种设计最大的优势是故障处理路径变得统一:不管是用户改YAML、Pod被删还是节点宕机,控制器看到的都是“当前状态偏离期望状态”,执行的都是同一套“调谐”逻辑,而不是用户手工写一堆脚本来做自愈。
3.2 Controller怎么知道Pod挂了
很多人在控制循环这里能背出公式:期望状态、当前状态、diff、reconcile。但考官会追问:Controller Manager是怎么感知到Pod挂掉的?是不是需要一个心跳机制?答案是Watch。Controller Manager通过API Server的List-Watch机制监听Pod、Deployment等资源对象的变化。当Pod的status.phase变成Failed,或者Pod对象直接消失,ReplicaSet控制器就会收到事件,然后计算副本数缺口并创建新Pod。
还有一题我也常问:某节点宕机了,上面的Pod多久会在其他节点被重建?标准答案跟Pod的容忍设置有关——默认情况下,节点失联后,Pod会在约5分钟后被标记为Terminating并重新调度。这个5分钟不是写死在调度器里的,而是Node控制器分配给Pod的tolerationSeconds默认值是300秒。真正理解这题的候选人会说:如果业务对故障恢复要求高,可以把Pod的tolerationSeconds调小,但不能太激进,因为节点失联也可能是网络抖动,盲目快速驱逐会造成更大范围的连锁重启。
回答这类题最忌讳只报数字。数字背后那个“为什么有这个默认值、改它可以达到什么目的”才是考官真正想听的。我面过一个人,把各类超时参数背得精确到毫秒,但问起“调小这个参数会带来什么风险”就答不上来——说明他只是刷了题,没有真正处理过故障。
4. Service网络与流量转发:面试开口就被追问的链路
4.1 ClusterIP是虚拟IP,Service真正做的是维护Endpoints
Service是K8s里最核心也最容易被误解的抽象之一。很多人背得出ClusterIP、NodePort、LoadBalancer三种类型,却答不出ClusterIP到底是什么。ClusterIP只是一个虚拟IP,它没有对应的网卡或真实设备,它存在的意义是作为一个稳定的流量入口,把请求转发给背后经常变化的Pod。
真正维持Service与Pod关联的机制是Endpoints。当Service创建时,控制面会根据selector筛选出匹配的Pod,把它们对应的IP和端口维护成一个Endpoints对象,由kube-proxy负责把这份数据转成本机上的iptables或IPVS规则。Pod重建后IP变了,Endpoints会跟着更新,转发规则同步刷新,所以客户端始终可以通过Service的ClusterIP访问到健康的Pod。这就是“为什么Pod IP会变但Service地址稳定”的完整答案。
4.2 一条请求从Ingress到Pod的完整路径
把流量链路完整串一遍,是网络题里最能体现功底的。外部请求先到达Ingress Controller——它本身就是一个跑在集群里的Pod或Deployment,通常以NodePort或LoadBalancer的方式暴露。Ingress Controller根据Ingress资源里定义的域名、路径规则,把请求转发到对应的Service。Service再通过ClusterIP和转发规则,把流量送到后端Pod的IP:Port。Pod内部再由监听端口的业务进程接收处理。
有几个容易被追问的点。第一,Ingress和Service不是同一个层面的东西:Ingress管的是外部HTTP路由规则,Service管的是集群内部的负载均衡,离开Service,Ingress转发就没有稳定的后端目标。第二,负载均衡策略:默认的iptables转发是随机等概率规则匹配,而IPVS模式支持更丰富的调度算法,比如轮询、最少连接。如果在面试里能把这两种模式的区别讲清楚,说明你在网络排障上确实有实战经验。
4.3 排障时最容易顺序搞反的问题
Service访问不通,是集群里最常见的故障之一。很多人一上来就查iptables或翻kube-proxy日志,实际上标准排障顺序应该是从应用层往底层走:先看DNS能不能解析出ClusterIP,再看ClusterIP能不能通,然后看Endpoints列表里是不是有可用后端Pod,最后看目标Pod本身的健康状态。多数情况下问题不在网络工具链,而在后端的Pod挂了或selector没匹配上。
这个排障思路我也常拿来考候选人。有个很经典的翻车现场:某服务间歇性超时,查了一圈网络,iptables规则全对,最后定位到两个后端Pod里有一个在频繁重启,流量被随机规则导过去就超时。如果不先把Endpoints和后端健康状态确认清楚,往底层网络里钻很容易绕远路。面试时能把“检查链路”讲成“分层剥洋葱”的人,会比只会背命令的人靠谱得多。
5. 探针、重启与自愈的边界
5.1 startup、liveness、readiness各管一段
自愈是K8s的一个重要卖点,但“自愈”从来不是无条件的。探针这道题的价值,在于考察你对健康检查机制的理解深度。startup探针管的是启动期,用来给慢启动应用争取时间;liveness探针管的是存活,失败会触发容器重启;readiness探针管的是就绪,失败会把Pod从Service的后端列表里摘掉,不让流量进来。
很多面试者知道三种探针的名字,但一进场景题就乱。比如问:一个应用启动需要3分钟,如果只配置了liveness且initialDelaySeconds是10秒,会发生什么?答案是:liveness在启动阶段就开始检查,应用还没就绪就连续失败,达到阈值后容器被反复重启。正确做法是用startup探针先撑住启动窗口,startup成功后再把存活检查交给liveness。
我建议回答时把三种探针理解成三个闸门:startup是入场闸门,liveness是运行期心跳闸门,readiness是流量闸门。它们不是三个独立的可选项,而是配合起来保护应用完整生命周期的组件。再补一句“启动慢的应用必须配startup,否则liveness会误杀”,这一题基本上就稳了。
5.2 CrashLoopBackOff里的退避逻辑
还有一个高频追问是:Pod反复崩溃,为什么不是无限快速重启?这里背后是kubelet的重启退避策略:每次重启失败后,kubelet会加大等待时间,从10秒起步,指数翻倍,封顶300秒,直到容器的健康状态稳定才重置。所以看到CrashLoopBackOff状态不用慌,它是K8s在用自己的方式说“我给你留了排查时间”,同时避免应用崩溃导致节点资源被耗尽。
回答的时候如果能补一句“退避是为了保护节点和容器运行时,而不是惩罚应用”,考官基本会认可。这题背后考的其实是对故障处理机制的理解,而不只是记一个状态名。我见过有人把自愈理解成“只要K8s在,应用就永远不会挂”,这是最大的误解——自愈只在探针配置合理、资源充足、控制器健康的条件下才成立。探针写得不好会误杀正常容器,写得过于宽松则会让故障流量长时间打到坏实例上。
经验之谈:探针配置上线前一定要做混沌验证,比如手动kill一次业务进程、把磁盘写满、模拟慢启动,看K8s是否按预期摘流量、重启、恢复。我在交付某个项目时吃过亏,readiness探针里查的接口本身有缓存,实例实际上已经不可用,探针还是一直返回成功,流量全部打到冷缓存后端,线上体验掉得厉害。后来把探针改为直接检查核心依赖链路的可用性才恢复正常。这类真实案例比任何“标准答案”都值钱。
6. 资源、存储与安全:容易被忽略的深水区
6.1 requests、limits与QoS:调度看request,杀容器看limit
资源题是必考题,先讲一个最多人搞混的点:requests是调度和保留的依据,limits是运行时限制和驱逐的依据。调度器看requests决定节点放不放得下;等到节点内存压力上来,kubelet开始驱逐容器时,靠的是limit与实际用量的对比。所以一个Pod可能requests写得小、limit写得大,调度时被塞进小机器,运行到高峰期又因为limit超限被OOM Kill。面试时能把这两者的分工讲明白,比背一堆参数强。
接着是QoS等级。Guaranteed对应requests等于limits,Burstable是requests与limits不完全相等,BestEffort是不写资源限制。QoS等级决定了节点资源紧张时的驱逐顺序:BestEffort先走,Burstable其次,Guaranteed最后。
这里其实带出一个判断标准:如果你希望核心业务尽量不被驱逐,就不要把requests和limits写得相差悬殊。很多面试者会把QoS等级背得很熟,但追问“为什么Guaranteed最后被杀”就答不上来。答案在于,kubelet在资源压力下会优先驱逐那些资源使用行为不可控的Pod,而Guaranteed的Pod因为用量被限制住了,往往已经是最可控的那类。
6.2 PV/PVC、StorageClass:存储题怎么答才算完整
存储这块很多人只背概念,但我面试时会让人讲一个具体场景:无状态应用随便跑,数据库这类有状态应用怎么拿到持久化存储?完整回答应该覆盖四步:第一步定义PV,描述存储卷的容量、访问模式、回收策略;第二步定义PVC,声明需要的存储规格;第三步PVC和PV通过匹配绑定;再往下,如果集群配了StorageClass,PVC可以不预先建PV,直接触发动态供给,由存储插件按需创建PV。
再深一层,追问“数据库Pod重建之后,再挂载数据还在吗”,这题考察的是PV和Pod的生命周期解耦。PV是独立于Pod的集群资源,Pod可以被删除重建,只要PVC还在、PV没有被回收,数据就还在。这也顺带解释了一个常见运维陷阱:删除PVC之后,如果PV的回收策略是Delete,存储卷会被直接删掉,数据就没了。很多数据丢失事故就是这么来的。
面试时能把这个事故场景主动讲出来,会比单纯背三种访问模式(ReadWriteOnce、ReadOnlyMany、ReadWriteMany)更有说服力,因为说明你踩过坑、知道怎么防坑。
6.3 RBAC最小权限:面试最后一道安全题
安全题里最经典的是RBAC。核心概念不难:User或ServiceAccount是身份,Role或ClusterRole是权限集合,RoleBinding或ClusterRoleBinding把前两者绑定起来。麻烦在于实践,真的去最小权限化一个集群并不轻松。
我常用一个案例来考人:一个后端服务需要读取某个命名空间里的ConfigMap,你会怎么授权?正确回答是:创建一个只包含get、list、watch configmaps权限的Role,再用RoleBinding绑定到一个专用的ServiceAccount,Pod通过这个ServiceAccount运行。很多候选人下意识会创建ClusterRole或者直接绑到default,这在面试里会留下很不好的印象,因为说明他没有权限边界的意识。
我在实际运维中见过不止一次:业务方图省事,把全体Pod挂在默认ServiceAccount上,这个SA又绑了集群管理员权限。等某个Web组件被攻破,攻击者拿到Pod权限就等于拿到了整个集群权限,后果非常严重。安全题往往放在面试最后,不是因为它不重要,而是因为前面答得再好,这里也能看出一个人对生产环境的敬畏程度。
7. 如果只想准备三天,我会把时间花在哪
7.1 用“动一次手”代替“背十条”
刷面试题最常见的误区是背答案而不动手。如果时间紧,我建议你只挑一件事:自己用本地方案把K8s集群搭起来,然后亲手跑一遍Pod创建、修改、删除的完整过程。看一次Pod从Pending到Running再到被重建,比背十遍组件流程都管用,因为只有亲眼看到状态变化,才能理解Watch与回写机制到底是怎么运作的。
另外可以做个刻意练习:在集群里做一次小故障注入,比如删掉某个正在运行的Pod、把节点标记为不可调度,然后观察控制器在多少秒内做了什么事。这一步不需要复杂工具,几个命令就能完成,但它能帮你把“声明式”“控制循环”“调谐”这些抽象概念彻底落地。
7.2 面试答题的框架
我面试别人时最看重的不是标准答案,而是答题结构。好的回答一般分成四步:先说现象或定义,再说背后的机制和原因,然后补一个常见误区和影响,最后必要时给一句实际操作验证。这种结构的好处是,即使你某个细节记不准,考官也能看到你的推导过程是完整的。
比如被问到Service负载均衡原理,你可以先说“ClusterIP是虚拟IP”,再讲Endpoints如何由控制器维护、kube-proxy如何刷新规则,然后补一句“很多人以为ClusterIP能ping通,实际上它没有任何网络设备响应”,最后说“我一般先用nc测试ClusterIP端口,再看iptables规则确认链路”。这样的答案,明显比干巴巴背概念要立体得多。
7.3 我的体会
这篇文章里写的每道题,我都在真实面试中问过或者被问过。说实话,能全部答对的人并不多,大家缺的不是记忆力,而是把一个点讲深讲透的能力。K8s的知识体系特别适合用“系统眼光”来看:网络、存储、调度、安全看起来是四件事,底层其实都围绕着同一个核心——你声明期望状态,控制面负责把它变成现实。抓住这条主线,面试题就不再是零散的考点,而是一个可以推理的完整系统。
最后再分享一个小技巧:面试前把你不熟悉的一个对象,比如ServiceAccount或者StorageClass,用“画链路图”的方式整理一遍,从资源创建、控制器动作、数据流走向三个角度拆开。能画清楚一个,基本就能讲清楚一类。这套方法我自己屡试不爽,也带过几个准备跳槽的朋友按这个思路复习,效果都比埋头背题好得多。