
docker和k8s面试题我自己前前后后准备过三轮也坐在面试官那侧问过别人不少次。最深的感受是大部分面试题表面在考知识点实际在验证你有没有形成体系化的理解。比如问“镜像和容器有什么区别”很多人能背出“镜像只读、容器可写”这八个字但再追问一层“那多个容器为什么能共享同一个镜像而不互相干扰”就卡住了。这类题目刷得再多如果底层逻辑没打通换一个问法照样露馅。这篇内容是我断断续续整理的一份面试题总结按模块拆开每个问题的回答都尽量讲清楚“是什么、为什么、怎么用、踩过什么坑”。目前先把容器和编排最核心的模块理清楚后续还会继续补。适合正在准备容器化/云原生方向面试的中高级开发者也适合运维同学查漏补缺。1. 容器到底是什么绕不开的Docker基础题1.1 镜像与容器的关系面试官想看你会不会区分“模板”与“实例”这道题出现频率极高但很多人只答到了表层。镜像和容器本质上是“模板”和“运行实例”的关系。镜像是一个静态的、分层的文件系统快照里面包含代码、运行时、系统库、配置容器是镜像被加载到内存后真正跑起来的进程集合它拥有一层可写的运行时文件系统。把这个关系说清楚面试官通常会顺着往下问多个容器跑同一个镜像各自修改文件会互相影响吗答案是互不影响。因为容器写入发生在自己的可写层底层的只读镜像层被所有容器共享任何一个容器删除文件或改配置都只在自己的可写层产生变更记录。这也是为什么从同一镜像启动几十个容器宿主机磁盘占用并不会线性翻倍。加分项在于能补充“写时复制”机制。Docker创建容器时并不会复制一整套镜像文件只是把只读层挂载进来再叠加一个可写层。只有当容器内进程真正去修改某个文件时才会把该文件从只读层复制到可写层再改写。这个机制带来的直接收益是容器启动极快、磁盘占用极低秒级甚至毫秒级拉起一个实例不是开玩笑的。1.2 容器与虚拟机到底差在哪别只背“共享内核”这道题的常规答案是“容器共享宿主机内核虚拟机有独立内核”但你要能解释清楚这句结论是怎么推导出来的。虚拟机从硬件层开始虚拟化有Hypervisor做资源调度每个虚拟机内部跑一个完整的Guest OS内核、系统库、应用全套独立所以隔离性强、但是启动慢分钟级、资源开销大每台都要占操作系统层面的内存。容器则完全相反它直接运行在宿主机内核之上通过Linux的namespaces做隔离、cgroups做资源限制。容器里的进程在宿主机看来就是一个普通进程只是看不太到外面的世界。这时候面试官会追加一个问题容器里执行uname -r看到的是谁的版本答案是宿主机内核版本因为容器没有自己的内核。这里有一个很重要的边界既然共享内核那就意味着容器技术本身是挑平台的。Linux容器跑在Windows内核上没有任何意义反之亦然。如果面试场景聊到“Windows能不能直接跑Linux容器”答案是不行除非底层加一层虚拟机级别的Linux内核。把这个边界讲明白比背一句“容器共享内核”高级得多。1.3 镜像分层与写时复制docker pull为什么快docker commit为什么不该用镜像分层的设计是Docker最容易被忽略的核心亮点。一个镜像不是一整块巨大的文件而是由多层只读层叠加而成每一层其实是一个增量变更记录。FROM指定基础镜像RUN执行命令产生新层COPY/ADD添加文件也产生新层。拉取镜像时如果本地已经有部分层Docker只会下载缺失的层这也是docker pull一般比直接下载整个庞大文件要快的原因。面试常问的一个延伸是docker commit能不能用于制作生产镜像强烈不建议。docker commit会把容器当前的完整状态包括可写层的所有变更、临时文件、历史命令打包成一个新镜像这个镜像不可复现、层信息混乱、还容易带入启动过程中产生的临时状态。更重要的是它丢失了Dockerfile的可追溯性后续维护的人拿到一个commit镜像根本不知道里面怎么构建的。生产环境构建镜像的正确姿势始终是写Dockerfile用docker build走缓存、可复现的构建流程。2. Docker进阶题容器生命周期、网络与存储2.1 Dockerfile高频坑CMD与ENTRYPOINT的差异这道题看似基础翻车率却很高。CMD和ENTRYPOINT都可以指定容器启动时的默认命令区别在于指令作用能否被docker run参数覆盖CMD提供默认命令和参数能docker run命令后的参数会整体替换CMDENTRYPOINT定义容器的主进程入口不能直接覆盖docker run参数会作为附加参数拼接在后面实际操作中常见的正确组合方式是ENTRYPOINT写固定的主程序CMD写默认参数。这样做的好处是你既能保证容器启动的进程可控又能通过docker run后面的参数灵活调整行为。比如ENTRYPOINT固定为nginxCMD默认是-g daemon off;一条docker run nginx -t就能直接做配置检查。另外还有一个经常被追问的细节容器里PID 1进程的信号处理问题。如果ENTRYPOINT脚本写得不对信号不会正确传递给真正的业务进程docker stop发出的SIGTERM就会被忽略最后等超时被强制SIGKILL容器退出码变成137。解决思路一般是让主业务进程直接作为PID 1或者在脚本里用exec替换进程确保信号能直达。2.2 容器退出与进程信号0、137、143到底谁发的kill面试里偶尔会给一个场景某个容器总是自动退出退出码是137。这个问题网上答案良莠不齐但只要你理解信号机制就能自己推出来。容器退出码0代表正常退出137代表进程被SIGKILL杀死143代表被SIGTERM杀死。docker stop默认先发SIGTERM给容器进程优雅退出的机会如果超过默认10秒还没退出Docker会发SIGKILL强制执行。所以看到137第一反应是进程没能在优雅时间内关停而不是业务代码直接崩了。常见原因有进程没有监听SIGTERM并做清理、主进程不是PID 1导致信号没传进去、或者应用关闭前的收尾工作超过10秒。还有一种退出场景非常常见容器一启动就退出而且反复重启。根本原因通常是容器里的主进程没有以前台方式运行。Docker要求PID 1必须保持在前台运行容器才会一直是Running状态。如果你在脚本里启动了一个后台进程然后脚本自己结束了容器就会立刻退出。这就是为什么要写nginx -g daemon off;、为什么要用tomcat的catalina.sh run而不是start。2.3 容器网络模型bridge、host、none到底怎么选Docker四种网络模式面试常考的其实是bridge和host还有一个容易忽略的自定义bridge网络的跨容器通信问题。默认bridge模式下容器有独立的网络名字空间需要端口映射才能被外部访问比如-p 8080:80Docker会通过iptables的DNAT规则把宿主机8080端口的流量转发给容器的80端口。host模式没有网络隔离容器直接共享宿主机网络栈。好处是性能好、没有NAT转换损耗坏处是端口冲突风险极高容器要用的端口宿主机不能占用。生产里有些对网络性能敏感的业务会选择host模式但多数情况下不会用因为编排层面会带来大量端口管理的麻烦。面试高频追问同一个宿主机上两个容器怎么互通如果都在默认bridge需要靠IP访问容器重启IP会变很脆弱。正确做法是创建一个自定义bridge网络把两个容器都接入它们之间可以直接用容器名做DNS解析访问。这个机制在Docker层面就是内置DNS服务在做服务编排或者本地联调时特别实用。2.4 数据卷与数据持久化别把数据写在容器可写层容器是短暂的但数据不能丢。这个问题在面试里一般会包装成“MySQL容器重启后数据还在吗”来问。如果MySQL数据目录直接写在容器可写层容器被rm掉或者重新调度数据就全没了。正确方案是挂载数据卷让数据落到宿主机或外部存储。数据卷主要有两种volume和bind mount。volume由Docker管理存放在Docker数据目录下推荐用于生产便于备份和迁移bind mount是把宿主机某个目录直接挂载进容器适合开发调试场景。面试官追问“为什么不用bind mount生产”你可以从权限混乱、宿主机目录与容器内目录结构强耦合、多机调度不方便等角度回答核心还是volume的生命周期由平台管理更利于抽象和迁移。3. K8s入门必答Pod、Deployment与Service的核心逻辑3.1 Pod为什么是调度的最小单位进程组视角而非单个进程视角K8s里最小的调度单位不是容器而是Pod这是很多新手最容易绕晕的点。用传统运维思维理解你会觉得容器已经够小了为什么外面还要包一层Pod答案是K8s借鉴了Linux进程组的思想——有些进程天生需要“抱团”共享同样的生命周期、网络环境和存储。一个Pod可以包含一个或多个容器这些容器共享同一个网络名字空间也就是说它们共享IP、共享localhost可以通过localhost直接互相访问。同时它们可以共享同一个数据卷彼此交换文件非常方便。最典型的例子是sidecar模式主容器跑业务应用伴生容器做日志采集或者流量代理两个容器在网络和存储上是连通的。实现这种共享的关键是pause容器。每个Pod里K8s会先启动一个pause容器它创建并持有整个Pod的网络和存储上下文其他业务容器加入这个上下文。这就是为什么你在节点上看到Pod里的容器数量总是比你定义的多多出来的通常就是那只看不见的pause容器。面试里如果能主动提到pause容器往往会让对方觉得你确实研究过底层。3.2 Deployment与StatefulSet无状态和有状态怎么选K8s控制器这么多面试考得最多的是这两个。Deployment面向无状态应用它的核心假设是Pod之间没有身份差异、没有固定序号任何一个Pod挂掉用同样的镜像新起一个顶替就行。这种场景下Pod名字随机、IP随机数据一般放在共享存储上比如多个Pod挂同一个PVC。StatefulSet面向有状态应用比如数据库、消息队列。它给每个Pod一个稳定且有序的标识比如web-0、web-1每个Pod有独立的、稳定绑定的存储卷Pod重建后名称、网络标识、存储都保持不变。这种设计是为了让有状态应用能在分布式环境下维持身份和数据的确定性。面试官经常会问一个实际决策题生产里能不能用K8s跑数据库直接答“能”或“不能”都不好。合理回答是技术上可行StatefulSet配合Operator可以解决存储、扩缩容、备份恢复等问题但运维复杂度远高于在虚拟机里跑数据库。很多团队把数据库留在虚拟机或云托管服务上把无状态应用跑在K8s里这种混部模式在成本与运维复杂度之间比较平衡。3.3 Service核心原理ClusterIP到底是怎么转发流量的K8s中Pod的IP是不稳定的频繁重建、扩缩容都会让IP变化。如果让应用直接访问Pod IP依赖关系会变得极为脆弱。Service就是为了解决这个问题而生它在Pod外面提供一个稳定的访问入口。Service本身不处理流量转发真正的数据路径是Service通过标签选择器找到一组后端Pod生成Endpoints新版本叫EndpointSlicekube-proxy组件监听这些变化并维护节点上的转发规则。你访问Service的ClusterIP流量会被规则转发到某个后端Pod上天然带了负载均衡效果。kube-proxy的三种实现模式里生产用得比较多的是iptables和IPVS。iptables模式通过一系列规则做DNAT转发稳定但不适合超大规模集群因为规则链很长匹配效率随规则数下降。IPVS模式基于内核的IPVS模块支持更多调度算法、扩展性更好适合Pod规模上千的集群。userspace模式最早但性能差基本只存在于历史文档里。面试追问的一个点是ClusterIP是集群内虚拟IP外部完全访问不到那你靠什么暴露服务答案是NodePort、LoadBalancer和Ingress。NodePort在每个节点上开一个端口LoadBalancer依赖云厂商的负载均衡器把流量引流进来Ingress则是L7层做域名和路径路由背后还要有一款Ingress Controller实际干活。4. K8s调度、网络与监控面试高频的“进阶三连”4.1 调度器在干嘛从Pod分配到健康检查K8s调度器kube-scheduler负责把新创建的Pod分配到合适的节点上。很多人以为调度就是随机分发实际上它经历了两个阶段过滤和打分。过滤阶段先排除掉那些不满足条件的节点——比如资源不足、有污点且Pod不能容忍、端口冲突打分阶段对剩余节点按照资源均衡度、亲和性规则等项目打分选分最高的节点完成绑定。面试如果聊到调度值得提一下扩展机制。调度器架构里有丰富的扩展点比如在过滤前、过滤后、打分前都能挂自定义逻辑。生产环境里基于这些扩展点可以实现很多特殊调度策略比如让Pod优先调度到节点上有某块GPU、或者根据业务域隔离的规则。这样就不是只背概念而是能展示你将调度器当成平台能力在理解。常见的追问如果一个Pod一直Pending可能是什么原因回答出来调度器筛选不出可用节点就行了最常见的情况是节点资源不够或者有污点没有被容忍。这句答案本身就是面试官在验证你有没有真正理解调度器存在的意义。4.2 探针三兄弟Liveness、Readiness、Startup怎么配才合理K8s探针是面试必考因为它最直接地体现了K8s对应用健康状态的理解。三个探针分工非常明确探针类型作用失败后的行为LivenessProbe判断容器是否还活着失败则重启容器ReadinessProbe判断是否准备好接收流量失败则从Service后端摘除不重启StartupProbe判断启动是否完成保护慢启动应用失败则按策略重启成功后交由Liveness接管绝大多数问题了核心就是三句话Liveness管“死没死”Readiness管“能不能接客”Startup管“慢启动保护”。回答时能举个例子更好一个JVM应用启动需要40秒如果只配LivenessProbe且initialDelaySeconds设置成10秒很可能启动过程中就被探针误杀反复崩溃循环。正确做法是配一个StartupProbe给足启动时间成功后再让Liveness正常接手。实际配置经验还有一个关键探针的threshold不能一味调小。过于灵敏的探针会在应用短暂抖动时造成不必要的重启或摘流反而加剧服务不稳定。生产上我一般会把failureThreshold调到3以上周期控制在10秒左右给业务留出恢复的容错空间。4.3 Pod异常状态ImagePullBackOff与CrashLoopBackOff的定位思路遇到Pod起不来快速看状态是基本功。ImagePullBackOff表示镜像拉取失败常见原因有镜像名写错、tag不存在、私有仓库没有配置镜像拉取secret、仓库需要认证而节点上没有凭证还有可能是镜像仓库网络不通。CrashLoopBackOff表示容器启动后很快崩溃被K8s反复重启而且重启间隔指数递增BackOff。这时候很多人一上来就想看日志但更规范的排查顺序应该是kubectl describe pod name先看Events特别关注调度失败、镜像拉取错误、探针失败等信息。kubectl logs name看应用启动日志判断是程序异常退出还是进程被信号杀掉。如果Pod有多个容器记得用-c container指定容器名否则默认只输出第一个容器的日志。必要时kubectl exec进容器手动执行启动命令复现错误。这个排查顺序在面试中讲一遍最能体现你的实战经验因为很多人只会停留在K8s状态层面不会往下追到应用层。4.4 网络方案Flannel与Calico的选型逻辑K8s不直接提供Pod之间跨节点通信的实现而是通过CNI插件来打通。这个设计很多人不理解“难道Pod之间还能不通吗”事实是没有CNI插件集群里的Pod网络根本不会自动互联。CNI要做的事是让每个Pod拿到集群内唯一的IP并保证任意两个节点上的Pod都能直接访问。Flannel主流模式是VXLAN走overlay网络通过隧道封装实现跨节点通信。优点是部署简单、不依赖底层网络设备缺点是封包解包有性能损耗适合中小规模集群。Calico则用BGP路由方案每个节点充当一个路由器把Pod IP路由信息同步给整个网络性能更优适合大规模集群但要求底层网络支持BGP配置复杂度略高。选型思路其实没那么玄节点几十台以内用Flannel足够了省心规模上几百台、对网络性能敏感就认真考虑Calico。面试里能说出这套取舍逻辑说明你真的为环境选过型而不是背了两个名词。5. 实操向高频问题排查与经典误区速查5.1 从面试题到现场排查一条完整的Pod故障定位链路说一个真实工作里排查过的例子某服务新版本上线后有几台Pod一直处于Pending状态。第一反应是kubectl get pods -o wide看分布发现全部挤在同一个节点其他节点空闲。这就很有问题了。kubectl describe pod一看Events提示“节点端口已分配”也就是说该Pod定义了hostPort并指定了端口而其他节点的这个端口都被占用了。这就是典型的调度条件不满足跟资源充足与否没有直接关系。排查链路里最容易犯的错是一上来就查应用日志其实Pod还停留在调度阶段时日志压根还没生成。正确的路径永远是先看Events定位阶段再决定下一步往哪个环节钻。这个习惯带到面试中非常受益因为面试官问“Pod起不来怎么排查”时真正期待的不是背命令而是你能按“调度 - 拉镜像 - 启动 - 探针 - 运行时”的链路去定位问题到底出在哪一环。5.2 常见误区速查表误区表述正解docker exec和docker attach差不多都能进容器attach是连接到容器主进程的输入输出exec是新起一个进程进入容器做操作生产调试优先用exec容器和虚拟机最主要的区别是性能核心区别是隔离边界性能差异只是表象之一镜像体积越小越好小镜像确实拉取快、攻击面小但一个小到连时区和证书都没有的镜像可能让业务跑出诡异故障CPU limit越高应用跑越快limit是上限不是分配量设置过高不会提升性能反而可能让调度器认为资源充足而过度打包触发Node压力容器里服务挂了容器退出码一定是非0如果PID 1是脚本且脚本捕获了退出状态自行退出可能出现容器退出但状态码为0的假象要结合日志判断Pause容器是多余的开销Pause容器负责持有Pod网络和存储上下文是Pod概念落地的基础整理这些误区不是为了应付面试是因为它们真的会让生产环境出事故。比如CPU limit那条很多人以为调大limit能让容器跑得更快结果Pod数量和单节点资源配额不匹配节点被打满业务反而被限流。5.3 面试题的准备心得准备了这么多轮我自己的体会是容器和K8s的面试题其实不只是技术题更是逻辑题。一个真正理解Docker和K8s的人面对没见过的问题也能从“这个组件为什么存在、没有它会怎样”推一个八九不离十的答案。反过来只背题不追底层的人遇到组合式追问立刻就会露怯。所以我的建议是刷题之前先搭框架容器解决的是打包和隔离的问题K8s解决的是编排和规模的问题。然后再把每个具体的题挂到框架下面去理解而不是孤立地去记。比如Service这题先想清楚“Pod IP会变”这个痛点再想Service如何通过标签和代理解决它答案自然就出来了。这份总结先整理到这。目前覆盖了镜像与容器、生命周期与信号、网络与存储、Pod与控制器、Service与网络方案、故障排查几个大块后面我会继续补调度策略细节、持久化存储方案、HPA自动扩缩容、集群升级思路和更多真实案例把这个系列慢慢填满。