2026/10/21 17:02:40

从容器到编排:一张图理清 Kubernetes 核心组件与运行机制

从容器到编排:一张图理清 Kubernetes 核心组件与运行机制 选 K8S 当话题基本就告别“一招鲜”了。很多人学 Kubernetes 卡住不是理解不了 Pod 和 Deployment而是被一堆抽象概念组合在一起时的连锁反应搞懵调度、网络、存储、声明式状态、控制循环……每样单独看都能看懂放进一个大集群里就不知道谁先谁后、谁依赖谁。这篇文章换个讲法先理清它到底解决什么问题再把组件、对象、运行机制串成一条线讲。不保证看完全部精通但看完你至少能搞明白K8S 里一个 Pod 是怎么跑起来的出问题了该往哪个方向查。1. 先搞清楚 K8S 为什么存在容器编排解决的真实痛点1.1 从 Docker 到 K8S中间到底差了什么Docker 解决了“打包”的问题应用连同依赖、配置、运行环境一起打进镜像装个容器引擎就能跑。这在单机上体验很好一条docker run就能起服务。但当你跑到几十台机器、上百个服务实例时情况就不一样了。容器挂在哪台机器上需要人肉分配机器资源利用率东一块西一块。流量进来需要一个入口统一转发给散落在各机器的容器还要能动态感知哪个容器活着、哪个挂了。服务发布新版本最好能滚动更新、不中断失败了能回滚。早上流量大晚上流量小希望实例数量跟着变而不是半夜爬起来手动扩容。某台机器宕机了跑在上面的容器得自动换到别的机器而不是等人发现。这些问题 Docker 本身解决不了需要一层“编排系统”来管这就是 Kubernetes 的定位。它本质上是一个分布式系统的调度和管理平台对外提供一组 API让你以声明的方式描述集群应该长什么样它负责把现实改造成你想要的样子。类比一下Docker 是砖块K8S 是施工队。砖块决定了房子用什么材料施工队负责照着图纸把房子盖起来、漏水了修补、入住率高了加盖楼层。没有施工队你只能自己搬砖。1.2 声明式 API 与控制循环K8S 的运作哲学K8S 最核心的设计思想是“声明式”。你告诉它“我要跑 3 个副本的 Nginx”它自己决定怎么安排、怎么保证一直是 3 个。它内部有一个不断运行的“控制循环”Control Loop大致流程如下持续从 API Server 读取集群期望状态比如副本数3。持续从各节点采集实际状态比如当前只有 2 个副本在运行。对比两者差异想办法让实际状态向期望状态收敛补齐那 1 个副本。这个循环不是跑一次就结束而是始终在跑所以哪怕你手动删了一个 Pod它也会在几秒内重新拉起一个——不是你删不掉是它觉得副本数不对自动帮你修回来了。理解了这一点后续看 Deployment、ReplicaSet、节点自愈思路都会顺很多。2. 控制平面与工作节点一张图拆解 K8S 组件职责2.1 API Server、etcd、Scheduler、Controller Manager 各管什么K8S 集群从物理角色上分为两大部分控制平面和节点。控制平面是集群的“大脑”负责决策节点是“手脚”负责跑容器。控制平面有四个核心组件API Server整个集群的大门所有组件、kubectl、外部系统操作集群都通过它。它负责认证、鉴权、校验请求然后把状态写入存储。没有它集群内其他组件互相都联系不上。etcd一个高可用的键值存储保存集群所有状态数据。比如“有哪些节点”“有哪些 Pod”“期望副本数是多少”全在这里。API Server 是唯一读写 etcd 的组件其他组件不直接碰它。Scheduler负责决定新建的 Pod 跑在哪个节点上。它通过一系列规则做筛选和打分比如节点资源够不够、有没有满足节点亲和性、数据卷能不能挂上等。Controller Manager一个组件集合里面跑着各种各样的控制器。前面说的控制循环实际就是由这些控制器分别实现的比如 Deployment 控制器管副本数、Node 控制器管节点状态、Namespace 控制器管命名空间清理。一个经验排障时先看 API Server 能不能访问、etcd 是否健康。控制平面任何一个环节出问题整个集群都会表现为“看起来所有节点正常但创建 Pod 永远卡在 Pending”。2.2 kubelet 与 kube-proxy节点上的两个关键角色每个工作节点上至少有两个常驻进程kubelet节点的“大管家”负责与 API Server 通信接收分配给本节点的 Pod 任务调用容器运行时如 containerd、Docker把 Pod 拉起来持续上报节点状态和 Pod 运行状态定期对容器做健康检查。节点上几乎所有关于容器生命周期的事情都由它负责。kube-proxy处理服务发现和负载均衡。它会监听 API Server 中 Service 和 Endpoint 的变化在节点上更新 iptables 或 IPVS 规则把访问 Service 的流量转发到对应的 Pod。它不负责 DNSDNS 是 CoreDNS 单独提供。2.3 一次 Pod 创建请求的完整链路kubectl 执行kubectl apply之后到底发生了什么把链路走一遍你对整个系统的协作方式会有直观感受kubectl 将 Deployment 的定义YAML发给 API Server。API Server 校验通过后把 Deployment 存入 etcd。Deployment 控制器检测到“期望副本数”没有对应的 Pod就创建一组 ReplicaSetReplicaSet 控制器再创建具体的 Pod 对象。Scheduler 观察到有新的未分配节点的 Pod开始调度选择一个合适的节点写入 Pod 的nodeName字段。目标节点上的 kubelet 发现 Pod 被分配给自己调用容器运行时拉取镜像、启动容器。容器启动完成后kubelet 上报 Pod 状态为 Running同时持续上报资源使用量。如果 Pod 设置了健康检查kubelet 按配置执行探针失败到一定次数会触发重启或重新调度。这里面有一个初学者常忽略的点Pod 的调度节点一确定除非节点故障或 Pod 被删除否则 Scheduler 不会再参与。也就是说Pod 一旦绑定了节点就成了“该节点的事”调度器只管“最初的那一下”。3. 核心对象逐个说透Pod、Deployment、Service、Ingress 的角色分工3.1 Pod最小调度单元但里面可以有多个容器Pod 是 K8S 中最小、最基本的调度单位也是运行应用的载体。很多人误以为 Pod 约等于容器其实不是。一个 Pod 可以包含一个或多个容器Pod 内的容器共享网络命名空间和存储卷通过 localhost 就能互相访问。为什么需要多容器 Pod典型场景是“边车模式”比如主容器是一个 Web 应用旁边挂一个日志采集容器它俩共享同一个网络栈和磁盘卷日志采集容器不需要暴露端口只需要把本地日志文件同步到日志平台。好处是主容器保持单一职责辅助功能全部独立成容器。实际使用中我建议除非你有明确的边车需求否则一个 Pod 只放一个主容器复杂度越低越好。把两个业务不相干的进程塞进同一个 Pod将来滚动更新、日志排查、资源统计都会非常难受。3.2 Deployment 与 ReplicaSet声明式管理实例数量的核心手段Deployment 是你平时最常用的控制器它描述应用的期望状态包括镜像版本、副本数、更新策略等。它自己不直接管理 Pod而是管理 ReplicaSet由 ReplicaSet 负责维持 Pod 副本数。为什么中间多一层 ReplicaSet因为滚动更新需要版本对比和回滚Deployment 每次更新都会创建一个新的 ReplicaSet逐渐将流量从旧 ReplicaSet 切到新 ReplicaSet。回滚本质是调整两个 ReplicaSet 的副本数比例——把新的缩到 0把旧的扩回来。滚动更新有几个关键参数strategy.type: RollingUpdate默认策略先起新 Pod等它 Ready 再杀掉旧的。maxUnavailable更新过程中允许最多多少个旧副本不可用建议不超过总副本数的 25%。maxSurge更新过程中允许最多超出期望副本数的数量相当于多准备几个临时实例。一个我踩过的坑把maxSurge设得过大小规格集群在更新大应用时会因为资源不足导致新 Pod 一直 Pending更新卡死。稳妥的做法是maxUnavailable: 25%、maxSurge: 25%先在测试环境验证一次再上生产。3.3 Service集群内的流量入口与负载均衡Deployment 保证 Pod 数量但 Pod 的 IP 是不稳定的重建就变这时候就需要 Service 来提供一个稳定访问入口。Service 通过标签选择器匹配一组 Pod创建一个稳定的 ClusterIPkube-proxy 在节点上维护相应转发规则把访问 ClusterIP 的流量负载均衡到后端 Pod。Service 的类型有几种常用场景ClusterIP只能在集群内访问适合服务间调用、数据库、内部 API。NodePort在每个节点上开一个端口外部通过“节点IP:端口”访问适合测试和小流量场景。LoadBalancer云环境下的负载均衡器实现把外部流量接入集群适合生产环境的入口。Headless Service不分配 ClusterIP配合 StatefulSet 使用给每个 Pod 一个稳定的 DNS 名称。Service 定位后端的依赖是 Endpoint 或 EndpointSlice。诊断“服务通了但 Pod 没收到流量”时先看 Endpoint 里有没有正确的 Pod IP。3.4 Ingress外部流量的统一网关Service 负责集群内部的事情Ingress 则是不折不扣的外部网关。它工作在第七层HTTP/HTTPS通过规则把外部 HTTP 请求转发到集群内部的 Service。比如你有一个域名、两个服务可以通过 Ingress 配置按路径分流/api打到 A 服务/web打到 B 服务。Ingress 在 K8S 里只是一个 API 对象真正干活的是 Ingress Controller常见的有 Nginx Ingress、Traefik。装完 Controller 后写 Ingress 规则才会生效。大多数云厂商提供的 K8S 集群默认就带了一个用之前先确认一下你的集群里装的是哪个。4. 调度、弹性与自愈K8S 凭什么让应用“自我运行”4.1 Scheduler 调度 Pod 的筛选与打分机制Scheduler 的工作可以拆成两个阶段过滤和打分。过滤阶段剔除不满足硬性条件的节点比如 CPU 或内存不够、端口冲突、违反节点亲和性、数据卷无法挂载。这一阶段保证了“能不能跑”。打分阶段对剩余节点按规则排序比如资源剩余越多分越高、Pod 分散度越高分越高、有本地数据的节点加分。这一阶段保证了“跑得好”。想影响调度结果有几种常用方式nodeSelector最简单的节点选择方式按节点 label 匹配。nodeAffinity更灵活的节点亲和性支持硬性要求和软性偏好。podAffinity / podAntiAffinity让某些 Pod 尽量靠在一起或者尽量分散开。resource requests / limits调度依据的核心requests 用于选节点limits 用于运行时限流。toleration容忍节点上的污点允许 Pod 调度到带有特定污点的节点。实际排障时Pod 一直 Pending优先看两件事节点是否满足 requests 要求是否被污点挡住。kubectl describe pod里通常能看到 Scheduler 留下的具体警示信息。4.2 HPA 弹性伸缩容量跟着流量走HPAHorizontalPodAutoscaler是 K8S 提供的自动扩缩容机制。你设置一个目标指标比如 CPU 利用率 70%HPA 会定期计算当前实际利用率与目标的差距调整 Deployment 的副本数。计算逻辑大致是期望副本数 ceil(当前副本数 × (当前指标值 / 目标指标值))比如当前 4 个副本CPU 平均利用率到了 90%目标 70%那么4 × (90/70) ≈ 5.14向上取整变成 6 个副本。缩容同理但会有默认的冷却时间防止指标抖动频繁伸缩。HPA 依赖 Metrics Server 提供指标数据很多离线或测试环境没装这个组件写 HPA 也不会生效。初次使用先用 CPU 做指标跑稳了再考虑自定义指标循序渐进。4.3 故障自愈容器重启、节点驱逐与控制器的“修复”逻辑自愈是 K8S 最吸引人的特性之一它的自愈分成几个层次容器崩溃kubelet 检测到容器退出按重启策略决定是否在本机重启。默认RestartPolicy: Always所以 Pod 不会因为容器崩溃而消失而是反复重启这就是 CrashLoopBackOff 的由来。节点宕机Node 控制器通过心跳超时判定节点 NotReady一段时间后驱逐节点上的 Pod在其他节点重建前提是这些 Pod 由 Deployment 这类控制器管理。Pod 所在节点失联但还没判定死亡Pod 会处于 Terminating 状态这时候需要强制删除或者等待节点恢复不要急着直接把整个命名空间删掉。重要经验K8S 的自愈不等于数据不丢。如果容器里有未持久化的日志或临时文件Pod 一删就没了。无状态应用随便自愈有状态应用必须结合持久化存储规划好。5. 网络与存储两个理论基础也是排障的高频盲点5.1 扁平网络模型为什么 Pod 之间可以直接互通 IPK8S 网络模型要求每个 Pod 拥有独立的 IP所有 Pod 之间可以直接互通不需要 NAT这看起来简单实际是通过容器网络插件CNI在背后实现的。常见的插件有 Calico、Flannel、Cilium它们负责为每个节点分配一个网段为 Pod 分配 IP并维护跨节点的路由规则。理解这个模型的关键是Pod 的 IP 在整个集群范围内是唯一的不管它调度到哪个节点其他 Pod 访问这个 IP 都应该能通。跨节点通信靠的就是 CNI 插件在宿主机之间建立的路由或隧道规则。排障思路访问一个 Pod IP 不通从 Pod 内部 ping 自身 IP 开始测再逐段排查同节点 Pod、跨节点 Pod可以快速定位是 CNI 配置问题还是网络策略问题避免一上来就怀疑应用的问题。5.2 PV、PVC、StorageClass存储抽象的三层设计Pod 里的容器一重建宿主机上的临时文件就没了。K8S 通过卷Volume机制解决持久化问题核心是三个对象PV持久化卷由管理员预先创建代表一段真实的存储资源比如一块云硬盘、一个 NFS 目录。PVC持久化卷声明描述应用需要什么样的存储比如“我要 5Gi读写模式 ReadWriteOnce”。StorageClass动态供给模板定义了如何按需创建 PV。用户写 PVC 时指定存储类名系统自动调用存储插件创建对应的 PV 并绑定。类比PV 是仓库里已租好的库位PVC 是你要租库位的申请单StorageClass 是根据申请单自动帮你找库位并签约的中介。没有 StorageClass管理员就得手工逐个预创建 PV量大时非常累。生产环境建议优先用动态供给除非有强管控要求。绑定是很死板的PV 和 PVC 必须匹配存储大小、访问模式、存储类三个条件否则永远处于 Pending 状态。5.3 ConfigMap 与 Secret应用配置的保存与注入配置管理也是 K8S 的常见场景。ConfigMap 存非敏感配置Secret 存敏感信息它们都可以通过环境变量或挂载文件的方式注入到 Pod 中。比如把配置文件内容放在 ConfigMap 里Pod 启动时挂载为一个文件多个副本共享一份配置。却有个容易踩的坑修改 ConfigMap 不会自动重启应用。环境变量是在容器启动时注入的改完即便 Pod 还在配置也不会更新。需要再滚动一次 Deployment或者用支持热更新配置的框架去监听文件变化。否则你会看到配置改了、文件也变了应用还是旧值半天查不出原因。6. 常见问题与排查技巧实录一套实战排障路线6.1 镜像拉取失败现象Pod 状态为 ImagePullBackOff 或 ErrImagePull。查kubectl describe pod看事件信息。多数情况是镜像名拼错、标签不存在、私有仓库认证缺失、网络无法访问镜像仓库。如果是私有仓库需要提前创建imagePullSecrets并绑定到 Deployment。6.2 CrashLoopBackOff容器反复启动又退出这个状态说明容器启动后立即退出。排查顺序kubectl logs pod --previous看上一次容器的日志很多应用启动失败时日志会直接说明原因。检查启动命令和参数是否写对镜像的 entrypoint 是否被覆盖。检查依赖是否就绪比如数据库连接串、ConfigMap、Secret。6.3 服务无法访问分三种情况排查集群内访问 Service 不通看 Service 的 Endpoint 是否有正确的后端 IP看kubectl describe service的标签选择器是否匹配到 Pod。外部访问 NodePort 不通确认安全组或防火墙放行确认 kube-proxy 正常工作iptables 规则是否被意外覆盖。Ingress 接入不通看 Ingress 是否绑定正确的 Service 和端口再看 Ingress Controller 日志。6.4 节点状态 NotReady节点宕机、kubelet 异常、网络分区都可能导致 NotReady。先登录节点看 kubelet 状态和日志确认容器运行时是否正常再确认 CNI 插件是否健康。节点恢复后之前被驱逐的 Pod 不会自动回来控制器会重新创建但需要时间。最后分享一个小技巧遇到任何 K8S 问题先执行kubectl get events --sort-by.lastTimestamp事件的顺序往往已经帮你把因果链摆好了。我排查过的不少问题上手查日志怎么也查不出来翻到事件列表瞬间就明白了。把事件、日志、配置三项串起来看是排障最快的路径。