【深度长文】一篇讲清 Kubernetes 控制平面架构,四个组件如何协同
引言很多人第一次接触 Kubernetes记住的是一串组件名称API Server、etcd、Scheduler、Controller Manager、kubelet。再往后一些会知道Deployment管副本Scheduler管调度Operator能做自动化运维。但如果继续追问一步一个Pod为什么会出现在某台节点上一个CRD为什么能像原生资源一样被kubectl get到一个数据库Operator为什么能够自动扩容、备份与故障恢复就需要我们对于K8S的组件工作原理和它们之间的编排协同orchestration有深刻的理解。理解控制平面的价值恰恰在这里。很多看似分散的工程问题例如调度延迟、控制器冲突、对象更新冲突、CRD扩展、Operator编排本质上都属于控制平面的行为问题。Kubernetes 也不是若干二进制程序的简单堆叠而是一个围绕声明式对象、持久化状态、事件分发与控制循环建立起来的自动化控制系统。为了便于阅读可以先记住本文试图回答的三个问题一个 Pod 如何被调度一个 CRD 如何被注册一个 Operator 如何运行一、控制平面总览Kubernetes 在管理什么1.1 控制平面与数据平面从整体视角看Kubernetes 可以分为两部分。第一部分是控制平面。它负责接收用户意图、保存系统对象、分发变化事件、做出调度与编排决策并持续推动整个集群向目标状态收敛。第二部分是数据平面也就是各个工作节点以及其上的运行时环境。它们真正执行容器、挂载卷、配置网络、回报状态。这里需要区分一个常见误解控制平面并不直接“运行业务”。业务容器运行在节点上控制平面的职责是定义、记录、判断、驱动。它更像一个持续运转的控制系统而不是一个直接搬运工作负载的执行系统。1.2 声明式模型中的“期望状态”与“实际状态”Kubernetes 常被称为声明式系统意思不是“可以写 YAML”而是用户提交给系统的不是一步一步的操作命令而是目标状态的描述。例如用户提交一个Deployment说“我希望有 3 个副本镜像版本是某个 tag暴露若干端口”。这不是在告诉系统“先创建 Pod再调度再拉起容器”而是在声明一个结果。至于为了达到这个结果系统要创建哪些中间对象、何时重试、如何补偿失败则由控制平面负责。因此控制平面长期处理的是两类状态之间的关系期望状态对象中由用户或上层控制器声明出来的目标。实际状态节点、容器、网络、存储等运行后真实呈现出来的状态。etcd中保存的是集群对象的持久化表示它是控制平面判断“系统现在应当怎样”的唯一权威来源但它并不等于外部世界本身。真实容器是否已经启动卷是否真的完成挂载探针是否已通过这些仍然发生在节点侧然后再通过状态上报反映回 API 对象中。控制平面的工作就是不断比较这两部分并推动二者靠近。1.3 以 API Server 为中心的星形结构Kubernetes 控制平面的核心结构不是各组件彼此直连而是围绕API Server形成的星形协作模型。用户通过kubectl、控制台、SDK 等入口访问API Server。调度器Scheduler通过API Server读取未调度Pod再把绑定结果写回去。各种控制器(Controller)通过list-watch观察对象变化再把修正结果写回去。kubelet通过API Server获取分配给本节点的对象并上报运行状态。Operator也不是直接操作底层数据库或容器而是围绕 API 对象驱动自动化行为。这意味着 Kubernetes 的协作基础不是组件之间的私有协议而是统一的对象模型与统一的 API 入口。系统中的大多数行为最终都可以归结为四件事存状态、看变化、做决策、推动收敛。K8S组件一览如果只记住一句话那么可以这样概括Kubernetes 控制平面是一个以API Server为中枢、以etcd为持久化状态源、以控制器模型推动系统收敛的分布式控制系统。二、API Server控制平面的统一入口2.1 它不只是一个 REST 服务从表面上看API Server提供了一套 HTTP API资源通过GET、POST、PUT、PATCH、DELETE进行读写似乎只是一个普通的 REST 服务。但在架构上它承担的角色远比“暴露接口”重要得多。API Server是控制平面的前门也是控制平面的总线。所谓前门是指所有合法的状态变更几乎都必须先进入它所谓总线是指不同组件之间并不直接共享内存或直连 RPC而是围绕它提供的对象语义协同。因此API Server至少同时承担四类职责统一入口所有写操作都从这里进入系统。统一语义对象应该长什么样、有哪些字段、哪些字段只读、哪些字段可更新都由它定义和执行。统一安全边界认证、鉴权、准入控制都在这一层完成。统一事件出口对象变化会在这里被观察、缓存和分发。这也是为什么 Kubernetes 的扩展能力如此强。无论是内建资源、CRD、Admission Webhook还是各种控制器本质上都接在这条主线上。2.2 一个请求进入 API Server 后会经历什么以一次创建对象的请求为例API Server的典型处理过程可以概括为以下顺序API server 的请求处理流程认证authentication确认请求是谁发来的例如证书、Token、ServiceAccount等。鉴权authorization确认这个身份是否有权对该资源执行该动作。准入控制(admission)在对象真正持久化之前执行策略检查与修改例如默认值填充、镜像策略检查、配额校验、Webhook 注入等。模式校验(validation)确认对象结构、字段与版本是否合法。持久化(persistence)将对象写入后端存储也就是etcd。事件传播(distribution)对象变更进入缓存与watch链路供控制器、调度器、节点侧组件继续处理。这条链路说明一件重要的事API Server不是简单地把一份 JSON 存起来就结束了。它是 Kubernetes 对“资源对象”这一概念进行制度化管理的地方。2.3list-watch控制器模型的基础设施如果说写路径定义了对象如何进入系统那么list-watch则决定了对象如何在系统内部流动。控制器、调度器、kubelet之所以能够及时响应对象变化并不是因为API Server主动逐一通知每个组件而是因为这些组件会先list当前对象集合再基于某个版本点持续watch后续变化。这样一来它们既能得到一份起始快照又能不断接收增量事件。整个 Kubernetes 控制器模型都建立在这个机制之上。没有watch控制器只能靠高频轮询就既低效又不稳定有了watch系统就能围绕对象变化形成相对统一的事件驱动模式。因此可以说API Server的关键不只在于“接受请求”更在于“向整个控制平面提供统一的事件观察窗口”。三、etcd控制平面的状态存储层3.1etcd存的不是业务数据而是集群控制状态Kubernetes 中几乎所有能被kubectl get到的对象最终都会以某种形式存入etcd。Pod、Deployment、ConfigMap、Secret、Lease、CRD乃至各类状态字段都会成为其中的键值记录。这时很容易产生一个误解既然它存了这么多东西那它是不是就相当于集群的“业务数据库”。严格说并不是。etcd保存的是集群控制状态不是业务系统自己的事务数据。可以把它理解为集群控制状态的核心键值存储。etcd被 Kubernetes 视为核心是因为它保存的是整个控制平面判断与协同所依赖的权威状态。3.2 为什么控制平面需要强一致存储从分布式系统的角度看控制平面最怕的情况不是慢而是状态混乱、对象认知不一致、多个组件对同一事实做出冲突判断。如果一个Pod是否已绑定到某节点不同组件看到的答案不一致如果多个调度器实例对同一个对象做出相互冲突的判断如果控制器以为某个副本不存在而重复创建而另一个组件又以为它已经存在那么系统就会迅速进入不可预测状态。因此Kubernetes 需要一个强一致的状态存储层。对控制平面来说写入成功必须意味着“这个状态已经成为全局上可以被信赖的事实”。这也是etcd被选中的原因之一。它不是为了追求极致吞吐而是为了保证元数据层面的正确性。在这方面控制平面的取舍相当明确对于集群对象这类元状态宁可牺牲部分写入吞吐也要保证一致性。因为一旦控制面失去一致性后面的调度、编排、恢复与扩展都将失去依据。极简版的Raft 协议实现3.3revision、历史版本与对象演化etcd不只是简单地覆盖旧值。它维护的是带版本演化的键值状态每次变更都会产生新的修订版本。Kubernetes 把这一能力映射到对象层形成了对象的resourceVersion、历史变更、冲突检测等一系列上层语义。如果对数据库原理比较熟悉这里可以联想到MVCC。二者并不完全等同但在“为并发读写保留版本演化信息”这一点上确实有相通之处。于是一个对象不再只是“现在长什么样”还隐含着“它是在什么版本基础上长成现在这个样子”。这为乐观并发控制、缓存同步和事件追踪提供了基础。在工程实践中很多控制面问题都可以借助这个视角理解为什么控制器要基于最新对象做reconcile为什么旧缓存有可能导致冲突为什么状态更新最好保持幂等。背后都与对象版本的演化有关。3.4watch的底层支点前面提到控制器依赖list-watch模式而这一机制能够稳定成立底层也离不开etcd的版本化与事件能力。对象变化可以围绕版本点持续输出从而让上层组件获得一个相对连续的变化流。对 Kubernetes 来说这一点意义非常大。因为它使控制平面不必围绕“谁调用了谁”来组织而可以围绕“对象发生了什么变化”来组织。控制器无需关心是谁修改了对象只需要看到对象变化并决定是否采取行动即可。关于etcd背后的分布式一致性、Raft选主与日志复制机制以及分布式算法如何在云原生领域被实现可以参见我之前的科普文章编辑【万字长文】从 Lamport 到 Kubernetes分布式共识算法是怎么主导云原生与AI时代的基础设施28 赞同 · 5 评论 文章四、Scheduler把未落位的 Pod 绑定到合适节点4.1 调度器处理的对象是什么调度器最重要的工作对象是尚未绑定节点的Pod。这类Pod已经是合法对象也已经进入控制平面的状态体系但它们还没有确定应该运行在哪台机器上。这里需要区分“创建 Pod”和“调度 Pod”这两个动作。Pod作为对象往往由更上层的控制器创建例如ReplicaSet控制器。调度器并不负责生成这个对象它负责的是在已有对象基础上做出放置决策。4.2 调度流程过滤、打分、绑定从概念上看调度过程可以分成三个阶段。Scheduler 的调度流程第一阶段是过滤。系统先排除那些明显不合适的节点。例如资源不够、污点不匹配、亲和性不满足、卷约束无法满足、节点不在指定拓扑域中。第二阶段是打分。在剩余候选节点中调度器会根据一系列规则计算优先级例如资源均衡、镜像本地性、拓扑分散、亲和性偏好等。第三阶段是绑定。调度器选择得分最高或最合适的节点将这个结果写回API Server。从语义上说这意味着该Pod的“落位决策”已经形成接下来的执行责任就从调度层转向节点侧。这套流程说明调度器的本质是“选择”而不是“启动”。它通过对象绑定来表达自己的决策而不直接调用容器运行时去创建进程。4.3 调度依据为什么如此复杂很多初学者会把调度理解为“找一台有空闲 CPU 和内存的机器”。这当然是最基础的条件但并不是全部。在真实集群里调度决策往往还要同时考虑资源请求与可分配余量。节点标签与选择器。亲和性与反亲和性。污点与容忍。拓扑分布约束。本地盘、可用区、专用硬件等环境特征。因此调度器并不是一个简单的负载均衡器而是一个把多种约束收束成单次放置决策的组件。它要解决的问题是“什么节点最适合这个Pod”而不是“现在把请求随机发到哪台机器”。4.4 默认调度与可扩展框架Kubernetes 默认提供一套调度框架与插件机制使调度逻辑既有统一骨架又可以按需扩展。这也是为什么 Kubernetes 没有把调度拆成完全去中心化的共识过程。调度决策需要依赖相对全局的资源视图而单次决策通常又足够短因此由一个统一的调度框架完成选择是一种复杂度与收益之间较为平衡的方案。五、Controller Manager让系统持续朝期望状态收敛5.1 什么是控制器如果说API Server提供了统一的状态入口etcd提供了可信的状态存储Scheduler负责解决对象落位问题那么控制器就是推动整个系统持续收敛的执行逻辑。控制器可以理解为一类长期运行的自动化程序。它不断观察某类对象比较“现在是什么样”和“目标应该是什么样”若发现差异就发起修正动作。修正动作本身仍然通过API Server进入系统。这里还需要区分两个层次。平时所说的Controller Manager通常指的是kube-controller-manager这一管理进程而其中真正执行具体自动化逻辑的是一个个面向不同资源的控制器。也就是说Controller Manager更接近控制器的宿主与组织者“控制器”才是实际承担reconcile逻辑的基本单元。5.2 为什么 Kubernetes 里会有很多控制器Kubernetes 没有把所有自动化逻辑塞进一个巨大的“总控制器”里而是把不同职责拆成多个相对独立的控制器。例如Deployment控制器负责把发布层面的目标转成ReplicaSet。ReplicaSet控制器负责维持指定数量的Pod副本。Node控制器负责观察节点状态与心跳。Job控制器负责判断批处理任务是否完成。EndpointSlice控制器负责根据服务后端维护流量目标集合。这种拆分方式的好处在于每个控制器只关注自己那一段因果关系。这样既降低了单个组件的复杂度也让系统更容易扩展。未来新增一种资源或能力时往往只需要新增相应控制器而不必重写整个控制平面。5.3reconcile控制器的通用工作模式尽管控制器种类很多但它们背后有一个高度统一的模式这个模式通常被概括为reconcile。其一般过程可以写成下面这个公式读取对象 - 判断差异 - 更新对象或创建下游对象 - 等待下一次事件Operator的reconcile流程可视化之所以说这是 Kubernetes 最核心的思想之一是因为大量内建功能和生态扩展都遵循这一模型。控制器不假设系统一次动作就能到位也不假设世界在自己操作之后保持静止。它会在对象每次变化后再次判断再次修正直到差异消失或者进入某种稳定状态。5.4 幂等性与持续控制控制器通常不应当写成“一次性脚本”。在分布式环境里请求可能失败缓存可能滞后外部系统可能短暂不可用之前提交的动作也可能部分成功。如果控制器逻辑不是幂等的就很容易在重试过程中制造新的错误。因此成熟的控制器通常具备两个特征持续运行它不是执行一次就结束而是长期存在不断响应对象变化。幂等修正它面对同一个目标状态重复执行时应当尽量得到同样的结果而不是层层叠加副作用。理解这一点对理解Operator尤其重要。因为Operator并不是某种新的魔法而是控制器模式在特定领域中的延伸。六、控制平面的执行终点虽然本文重点是控制平面但节点侧不能完全省略。原因很简单如果没有节点执行层控制平面的所有对象、判断与收敛都无从落地。从职责上看节点侧至少包含三类关键角色kubelet观察分配给本节点的对象把PodSpec转化为具体执行动作并持续汇报状态。容器运行时如containerd、docker负责拉取镜像、创建/删除容器kubelet 通过 CRI 调用它们实现具体运行底层一般由runc或kata执行。DaemonSet确保每个节点都运行某些系统级 Pod例如日志收集如 fluentd、监控如 node-exporter、网络插件如 CNI 各实现或 DNS如 CoreDNS等。以容器启动为例控制平面只会把“这个Pod应当在某节点上运行”的状态表达出来。真正到节点上通常是kubelet依据该对象调用CRI接口请运行时准备镜像、创建容器再由底层机制建立进程隔离与资源限制。这里不再展开到CNI、CSI或内核实现细节因为它们已经超出本文的重点。需要记住的只是控制平面负责定义、分发和纠偏节点侧负责执行和回报。七、三个根本问题之一一个 Pod 是如何被创建并调度的为了把前面的静态组件真正串起来最好的办法是沿着一条完整链路走一遍。这里以更常见的Deployment场景为例因为在真实环境中用户很少直接手写单个Pod作为最终工作负载。7.1 从提交对象开始用户执行kubectl apply -f deployment.yaml时首先进入系统的是一个Deployment对象。这个对象经过认证、鉴权、准入和校验后由API Server写入etcd。到这一步为止系统里还没有真正运行的业务容器。它只是多了一份被持久化的目标描述期望存在一个满足某些规格的发布对象。7.2 上层控制器补齐中间对象Deployment控制器通过watch看到新对象后会判断当前实际状态与期望状态之间的差异。发现还没有对应副本集时它会创建一个ReplicaSet。随后ReplicaSet控制器看到自己的目标副本数尚未满足又会继续创建若干Pod对象。到这时系统中才出现真正代表运行实例的Pod但这些Pod仍然只是 API 对象其中大多还没有绑定到节点。这一阶段非常能体现 Kubernetes 的控制器分层思路。用户并没有直接命令系统“去创建三个 Pod”。用户只声明了发布目标而系统通过多个控制器逐层把目标展开成更具体的对象。7.3 调度器为 Pod 选择节点调度器持续监听未绑定节点的Pod。当新的Pod进入队列后调度器会读取当前节点状态、资源约束与调度规则经过过滤、打分与选择过程最终为该Pod选出一个最合适的节点。接着调度器把绑定结果写回API Server。从对象视角看就是Pod的放置信息发生了变化从系统视角看这代表控制平面已经形成了“这个实例应当在哪台机器上运行”的决策。7.4 节点侧接管执行目标节点上的kubelet同样通过API Server观察分配给自己的对象。一旦发现这个新Pod它就会把对象规格转化为具体执行动作例如准备卷、拉取镜像、通过CRI请求运行时创建容器。如果运行时是containerd那么通常由kubelet调用CRI接口与之交互再由containerd协调底层组件完成容器启动与隔离环境建立。我们可以看到一道清晰职责边界调度器决定放在哪里kubelet负责把这个决定落成真实运行状态。7.5 状态回流与持续收敛容器启动后节点侧会不断把状态回报给控制平面例如Pod的阶段、容器是否就绪、探针是否通过、节点是否健康等。这些状态变化重新进入API Server与存储层进而被各类控制器继续观察。于是控制平面的工作并未在“调度完成”这一刻结束。若节点故障、容器退出、探针失败或副本数不足控制器又会基于新的状态再次采取动作。Kubernetes 的运行逻辑并不是一条单向流程而是一个持续闭环。pod 建立流程的可视化把这条链路概括成一句话就是对象先进入状态系统再由控制器逐层展开由调度器完成落位由节点侧负责执行最后由状态回流推动下一轮收敛。八、三个根本问题之二CRD 是如何在 API Server 中注册的8.1CRD解决的是什么问题Kubernetes 的强大之处不仅在于它内建了一套对象如Pod、Deployment、Service还在于它允许用户把自己的领域概念也纳入同一套 API 体系。CRD即CustomResourceDefinition本质上就是对“新增一种资源类型”这件事的声明。它告诉系统现在除了内建对象之外还应当存在一种新的对象它的名字是什么属于哪个 API 组支持哪些版本字段结构如何哪些字段应该校验。这意味着 Kubernetes 不是一个封闭系统。它的对象模型是可以扩展的而且这种扩展不是绕开原有体系另起炉灶而是接入原有 API 机制。8.2 一个新资源类型如何进入 API 体系从流程上看CRD的注册并不复杂。用户首先提交一个CustomResourceDefinition对象这个对象本身也是通过API Server写入系统的。随后扩展 API 相关的机制会根据这个定义把新的资源类型纳入可发现、可校验、可存储、可访问的 API 范畴。从外部使用体验上看结果就是kubectl api-resources能看到这个新资源。客户端可以通过标准 REST 路径访问它。kubectl get、kubectl describe、watch、RBAC、Admission 等通用机制都可以继续作用于它。CRD的注册流程8.3 Schema、版本与子资源一个成熟的CRD通常不只定义名字还要定义结构与演化方式。Schema 校验确保用户提交的自定义资源至少满足基本结构要求而不是任意字段都能写。版本管理允许资源在v1alpha1、v1beta1、v1等多个阶段演进。子资源划分常见的是spec与status分离前者表达期望后者表达观察结果。这种设计延续了 Kubernetes 内建对象的核心思想。spec与status的区分尤其重要因为它把“用户想要什么”和“系统观察到了什么”分开表达为控制器模式提供了清晰的边界。8.4CRD与 Aggregated API 的边界需要顺手区分的另一点是Kubernetes 的扩展并不只有CRD一种方式。CRD更适合声明式资源扩展也就是“我要新增一种对象类型并让它进入统一 API 体系”。如果需求更接近于新增一套定制 API 服务则还可以走 Aggregated API 等其他路径。但在大多数云原生运维与平台工程实践中CRD已经足以承载大量扩展需求。因为真正关键的是把领域对象纳入 Kubernetes 的状态与控制循环中。九、三个根本问题之三Operator 的运行逻辑是什么9.1 只有CRD还不够有了CRD系统只是学会了“认识一种新对象”但认识对象不等于知道该如何对它采取行动。例如你定义了一个MyDatabase资源Kubernetes 现在可以接受、存储、查询这个对象也可以校验它的字段是否合规。但如果没有后续逻辑系统并不会自动帮你创建 StatefulSet、初始化存储、执行备份、处理主从切换。换言之CRD只提供了资源模型尚未提供控制行为。这时就需要Operator。9.2Operator本质上是一类面向领域的控制器Operator并不是脱离 Kubernetes 的新机制本质上仍然是控制器模型的一种具体化。它通常围绕某个自定义资源长期运行持续观察对象变化并把领域知识编码成自动化动作。它和普通控制器的差别更多体现在处理对象的领域含义上。例如ReplicaSet控制器处理的是“副本数是否满足”而数据库Operator处理的可能是“实例是否已经初始化”“是否需要扩容”“是否应当触发备份”“是否需要执行故障切换”。因此可以把Operator理解为把原本依赖人工判断与人工操作的运维流程改写成一个围绕 API 对象持续运行的控制器。9.3Operator的典型工作循环一个典型Operator的工作过程大致如下通过list-watch观察某类自定义资源对象。读取该对象的spec理解期望配置。检查下游资源或外部系统的实际状态。若发现差异则创建、修改或删除相关对象例如StatefulSet、Service、Secret、备份任务等。在需要清理、善后或级联管理时结合finalizer、所有权关联等机制维护对象生命周期。将观察结果写回对象的status必要时写入条件、阶段、错误信息等。等待下一次事件再重复这一过程。这个过程与 Kubernetes 内建控制器几乎同构。不同之处在于Operator往往携带更强的业务或中间件领域知识。例如数据库主从拓扑如何初始化升级时必须遵循什么顺序哪些状态可以自动恢复哪些状态必须暂停等待人工介入。十、把几条链路收束成一个统一模型写到这里再回头看前面的三条链路Pod的创建与调度展示的是对象如何从高层目标逐步展开成可执行实例。CRD的注册展示的是新的对象类型如何进入 Kubernetes 的统一 API 体系。Operator的运行展示的是新的控制逻辑如何围绕对象持续工作。表面上看这三件事分别属于工作负载编排、API 扩展、自动化运维彼此差异很大但从控制平面角度看它们背后遵循的是同一套结构。第一系统中的核心单位始终是对象。不论是原生对象还是自定义对象最终都要以 API 资源的形式存在。第二系统围绕对象维护状态。用户提交的是期望状态组件观察的是实际状态存储层保存的是可被信赖的持久化状态表示。第三系统通过list-watch获得事件。组件不需要预先知道是谁会修改对象只需要对变化做出响应。第四系统通过控制器或调度器执行控制循环。它们读取状态、判断差异、提交修正再等待新的变化到来。由此可以得到一个相对统一的理解框架Kubernetes 的核心并不只是容器编排而是以API Server为中枢、以对象模型为界面、以状态变化为信号、以控制循环为动力的自动化控制系统。也正因为如此Kubernetes 才能既管理内建工作负载也承载不断扩展的生态。它的关键不在于控制平面里究竟有多少个组件而在于这些组件通过一致的对象模型与事件机制被组织进同一种协作方式中。