引言

大多数 Kubernetes 入门文章把集群组件拆成两类平行介绍:Control Plane 有什么、Node 上有什么。读完能记住"API Server 是入口"“etcd 存数据”“Kubelet 在节点上”——但问起"一个 Pod 从提交 YAML 到真正跑起来,中间经历了什么",往往答不上来。

本文换一个角度:从一次完整的 Pod 创建出发,沿调用链路追踪每个组件的职责与协作方式。每一步都给出具体的数据流向和命令验证方法。读完之后,"K8s 架构"不再是一张静态架构图,而是一条活的通信路径。


一、组件总览与核心职责

在进入调用链路之前,先梳理 Control Plane 和 Node 上各自有哪些组件,以及它们各自的角色。

Node(工作节点)

Control Plane(管控节点)

etcd
状态存储

API Server
(唯一入口)

Controller Manager
(多个控制器)

Scheduler
(调度决策)

Kubelet
(节点代理)

CRI 插件
(容器运行时)

Kube-proxy
(Service 代理)

etcd:整个集群的持久化存储,键值对形式保存所有资源对象。所有组件的状态最终都落在这里。

API Server:集群的唯一入口。所有组件(kubectl、Scheduler、Controller Manager、Kubelet)都只与 API Server 通信,不直接访问 etcd。API Server 本身是无状态的,会话状态存在 etcd 里。

Controller Manager:包含多个内置控制器(Deployment、ReplicaSet、Endpoint、ServiceAccount 等),每个控制器都是一条独立的控制循环,持续对比"期望状态"与"实际状态"。

Scheduler:监听未调度的 Pod,为其选择一个最优节点。它只负责决策,不负责执行——选好节点之后,把 nodeName 字段写回 API Server,剩下的事交给目标节点的 Kubelet。

Kubelet:运行在每个节点上的代理。负责向 API Server 注册节点、管理 Pod 的完整生命周期(从下载镜像到启动容器),以及报告节点和 Pod 状态。

Kube-proxy:负责维护节点上的网络规则(iptables 或 IPVS),实现 Service 的负载均衡。Kubelet 和 Kube-proxy 的职责容易混淆:Kubelet 管理 Pod 本身,Kube-proxy 管理 Pod 之外的 Service 流量。


二、Pod 创建完整时序

下面是最关键的部分。一个 kubectl apply -f pod.yaml 提交之后,集群内部发生了什么?

CRI 插件 Kubelet Scheduler etcd API Server kubectl CRI 插件 Kubelet Scheduler etcd API Server kubectl Pod 已在 etcd 中, 但尚未分配节点 loop [Watch 机制(持续)] Kubelet 通过 Watch 感知到分配给自己的 Pod loop [Watch 机制(持续)] POST /api/v1/namespaces/default/pods 写入 Pod 对象(phase: Pending) 返回写入成功(revision: 1) 201 Created 推送未调度的 Pod 事件 Filter(预选) 过滤不符合条件的节点 Score(优选) 对可用节点打分 PATCH /api/v1/namespaces/default/pods/pod-name spec.nodeName = selected-node 更新 Pod(nodeName 已填充) 更新成功 返回更新成功 推送 Pod 创建事件 读取 Pod Spec 准备创建容器 RunPodSandbox(创建 Pause 容器) PullImage(拉取业务镜像) CreateContainer(创建业务容器) StartContainer(启动容器) PATCH /api/v1/namespaces/default/pods/pod-name/status phase = Running 更新 Pod 状态 写入成功 返回

这张图涵盖了四个核心机制:API Server 的 etcd 写入Scheduler 的 Watch + Filter/Score 决策Kubelet 的 CRI 调用,以及状态回写的完整闭环。下面逐一展开。


三、API Server 的三层入口

API Server 接收请求后,经过认证(Authentication)、授权(Authorization)、准入控制(Admission Control)三层处理:

请求
POST /api/v1/pods

认证
(谁在请求?)

授权
(有权做什么?)

准入控制
(符合集群规范吗?)

etcd
写入

认证层:支持客户端证书(x.509)、Bearer Token、OIDC 等多种方式。kubectl 默认使用 x.509 客户端证书,证书信息(CN=用户名、O=用户组)会在这一步解析出来。

授权层:基于 RBAC(Role-Based Access Control)。如果请求方的 ServiceAccount 没有对应 Role 绑定的权限,直接返回 403 Forbidden

准入控制层:最有意思的部分。内置插件包括:

  • LimitRanger:强制 Pod 必须声明 Resource Request/Limit(未声明的 Pod 默认值为零,生产环境建议配合 LimitRange 设置默认配额)。
  • ResourceQuota:检查命名空间总资源是否超配额。
  • MutatingAdmissionWebhook:在写入 etcd 前修改对象(如自动注入 sidecar 容器)。
  • ValidatingAdmissionWebhook:在写入 etcd 前验证对象(如 OPA/Gatekeeper 的策略检查)。

三层全部通过后,请求才会写入 etcd。任何一个环节拒绝,请求都不会到达 etcd——这是 K8s 安全的纵深防御设计。


四、Scheduler 的调度决策

Scheduler 并不是一次性完成调度的。它通过 Watch 机制持续监听 API Server,收到未调度 Pod 的事件后,进入两阶段决策:Filter(预选)Score(优选)

收到未调度 Pod

Filter(预选)阶段

Score(优选)阶段

选择得分最高节点

资源是否足够?
CPU / 内存 / 存储

端口是否冲突?
HostPort 检查

污点容忍是否匹配?
Taints & Tolerations

节点是否 Ready?

亲和性规则是否满足?

资源倾斜分数
(LeastRequested)

Pod 亲和性分数
(MostRequested)

镜像本地性分数

节点评分总分

全部通过才进入 Score

Filter 阶段会并行执行多个过滤条件(Predicates),任何一个条件不满足,节点直接被淘汰。常见条件包括:

  • PodFitsResources:节点剩余 CPU/内存是否满足 Pod 的 Resource Request
  • PodFitsHostPorts:目标端口是否被占用(hostPort 独占)
  • NoDiskConflict:存储卷是否冲突
  • MatchNodeSelector:节点 Label 是否满足 nodeSelectornodeAffinity
  • NoUnschedulable:节点不能有 node.kubernetes.io/unreachable 标签

Score 阶段对通过 Filter 的节点打分,分数最高的节点胜出。K8s 1.26 之后,Scheduler 架构从旧的 Predicates/Priorities 模型迁移到了 Scheduling Framework,Filter 和 Score 成为标准扩展点:

旧术语新术语说明
PredicateFilter过滤不合格节点
PriorityScore为合格节点打分

默认的 Score 插件包括:

  • LeastRequestedPriority:优先调度到资源使用率低的节点
  • MostRequestedPriority:优先调度到资源使用率高的节点(适合批量任务)
  • ImageLocalityPriority:如果镜像已经在节点上,会给高分(减少拉镜像时间)

K8s 1.26 之后,调度器引入了 Scheduler Framework,支持在 Filter 和 Score 之间插入自定义插件(通过 kube-scheduler--config 指定)。如果默认策略不够用,可以写一个自定义插件注册进去,而不是用旧的 Extender API(后者性能差,因为需要额外的 HTTP 调用)。

验证调度决策

调度完成之后,Pod 的 nodeName 字段被填充。可以用以下命令验证:

# 查看 Pod 当前状态,spec.nodeName 已填充说明调度完成
kubectl get pod nginx -o jsonpath='{.spec.nodeName}'

# 查看 Pod 的调度事件(谁调度了它)
kubectl describe pod nginx | grep -A5 "Events:"

# 查看调度器的调度决策日志(需要调度器开启了 verbose 日志)
kubectl logs -n kube-system kube-scheduler-<node-name> --tail=50 | grep nginx

# 如果 Pod 一直处于 Pending 状态,查看原因
kubectl describe pod nginx | grep -A10 "Conditions:"
# 常见原因:
#   - No nodes available(资源不足)
#   - 0/N nodes are available(Filter 阶段全部淘汰)

五、Controller Manager 的控制器链路

Pod 调度完成之后,Kubelet 还没来得及创建容器,Controller Manager 的控制器们已经开始工作了。其中最核心的是 Endpoints ControllerReplicaSet Controller

协作关系

Controller Manager

API Server

etcd(读取)

ReplicaSet Controller

Endpoints Controller

ServiceAccount Controller

PVC Controller

RS Controller 创建/删除 Pod

Endpoints Controller 监听 Pod
生成 Endpoints 对象

Endpoints 对象触发
Kube-proxy 更新 iptables/IPVS

ReplicaSet Controller:监听 Deployment 的期望副本数,确保集群中始终有指定数量的 Pod 副本运行。当某个 Pod 被删除或节点宕机时,它会自动创建新 Pod 来补齐。

Endpoints Controller:这往往是入门文章里最容易被忽略的。Endpoints Controller 监听所有 Pod 的变化(创建/删除/状态变更),实时维护每个 Service 对应的 Endpoints 对象。Endpoints 对象的结构是:Service 名称 → 一组 Pod IP + 端口。

关键点在于:Endpoints 对象是 Kube-proxy 更新网络规则的直接依据。如果 Endpoints Controller 没有及时更新(Pod 卡在 Pending、或者控制器与 API Server 之间 Watch 断开),Service 会突然失去后端 Pod,流量直接打空。

# 查看某 Service 的 Endpoints(即后端 Pod 列表)
kubectl get endpoints nginx-service -o yaml

# 对比 Endpoints 和实际 Pod IP 是否一致
kubectl get pods -l app=nginx -o jsonpath='{range .items[*]}{.status.podIP}{"\n"}{end}'

# 如果 Endpoints 为空但 Pod 正在运行,说明 Endpoints Controller 有问题
# 检查 Controller Manager 日志
kubectl logs -n kube-system kube-controller-manager-<node-name> --tail=20 | grep endpoint

六、Kubelet 与容器运行时

调度完成后,目标节点的 Kubelet 收到了 Watch 推送,开始执行 Pod 的实际创建。整个过程通过 CRI(Container Runtime Interface)接口与容器运行时通信。

通过

不通过

读取 Pod Spec
(来自 API Server)

镜像管理
(CSI 或本地镜像)

Pod Sandbox 创建
(Pause 容器)

Init 容器执行
(按顺序,执行完退出)

业务容器创建
(从 Pod 模板生成)

ReadinessProbe / LivenessProbe
(健康检查)

Pod phase → Running
状态回写 API Server

Probe 是否全部通过

容器对外提供服务

重启容器
或触发 OOMKill

**CRI(Container Runtime Interface)**是 Kubelet 与容器运行时之间的标准化接口。容器运行时负责实际的操作:拉取镜像、创建网络命名空间、启动容器。常见的实现有:

  • containerd:Docker 默认使用的运行时,CNCF 毕业项目
  • cri-o:专门为 K8s 设计的轻量级运行时
  • Docker(dockershim):K8s 1.24 之前内置的 shim,已在 K8s 1.24 中移除

K8s 1.24 移除了内置的 dockershim,这意味着使用 Docker 作为容器运行时的集群需要显式安装 docker-shim 或迁移到 containerd。这是一个经常被忽视的升级陷阱——如果集群使用 kubeadm 搭建且运行时为 Docker,升级到 1.24 之前必须完成迁移。

Pause 容器是每个 Pod 都会首先创建的"基础设施容器"。它的唯一目的是:持有 Pod 的网络命名空间(network namespace),让 Pod 内的所有业务容器共享同一个网络栈,而不需要每个容器都单独配置网络。当业务容器崩溃重启时,Pause 容器保持不变,Pod IP 不会变化——这是 K8s Service 稳定性的底层保障之一。

验证 Kubelet 的 Pod 创建过程

# 查看节点上所有容器(crictl 是 containerd 的 CLI)
crictl ps -a --name=nginx

# 查看 Pod 的详细启动日志(由 Kubelet 写入)
kubectl describe pod nginx | grep -A20 "Events:"

# 查看 Kubelet 日志(排查镜像拉取失败、OOM 等问题)
sudo journalctl -u kubelet --since "10 minutes ago" | grep nginx

# 查看节点磁盘空间(镜像拉取失败常见原因)
df -h /var/lib/docker  # Docker
df -h /var/lib/containerd  # containerd

七、etcd 在整个链路中的位置

虽然 etcd 出现在架构图里,但大多数入门文章只说"etcd 存数据",没有说明它在 Pod 创建链路中扮演的具体角色

控制器协作阶段

Controller Manager
Watch etcd 变更

Endpoints Controller
更新 Endpoints 对象到 etcd

状态上报阶段

Kubelet 定期上报状态
PATCH /status

更新 etcd
phase: Pending → Running

调度阶段

Scheduler 读取 etcd
查询未调度 Pod

PATCH 更新 etcd
spec.nodeName = node-a

Pod 创建阶段

API Server 接收请求
认证 + 授权 + 准入控制

写入 etcd
key: /registry/pods//

返回 revision 号
(乐观锁版本)

API Server

etcd 集群

需要特别注意的是:etcd 不仅仅是"存放最终状态"的被动存储,它同时是 Controller Manager 的 Watch 数据源。Controller Manager 并不依赖 API Server 推送事件,而是直接 Watch etcd 的变更(API Server 是 etcd 的 Watch 代理)。当 etcd 中某个 key 发生变化时,Watch 链立即触发相关控制器的逻辑。

这带来一个运维层面的重要结论:etcd 的性能直接决定了整个集群的响应速度。etcd 的写入延迟通常要求在 1ms 以内,如果 etcd 所在磁盘发生 I/O 争用(例如与其他进程共享 SSD),API Server 的请求会开始排队,Pod 创建延迟从毫秒级跳升到秒级。

# 查看 etcd 写入延迟(etcd pod 内)
kubectl exec -it -n kube-system etcd-<node-name> -- etcdctl endpoint status --write-out=table

# 查看 etcd 的 wal_fsync_duration_seconds(磁盘同步延迟)
kubectl exec -it -n kube-system etcd-<node-name> -- etcdctl endpoint status | grep "db size"

# 检查 etcd 磁盘健康(etcd 强烈建议使用独立 SSD)
kubectl exec -it -n kube-system etcd-<node-name> -- df -h /var/lib/etcd

八、架构全景:一条请求的完整生命周期

把所有组件串起来,得到一个完整的请求生命周期:

kubectl apply
提交 YAML

API Server
认证 + 授权 + 准入控制

etcd 写入
Pod(Pending)

Watcher 推送
Scheduler

Filter + Score
选择最优节点

etcd 更新
Pod.nodeName

Watcher 推送
目标节点 Kubelet

Kubelet → CRI
创建容器

Kubelet → API Server
PATCH Pod Status = Running

etcd 更新
Pod phase = Running

Endpoints Controller
更新 Endpoints

Kube-proxy
更新网络规则

流量可达
Pod

整个链路在正常情况下耗时约 5~15 秒,主要瓶颈在镜像拉取(首次部署时镜像较大)和容器启动时间。如果镜像已经在节点上(ImageLocality 调度器优先派到已有镜像的节点),整个 Pod 创建过程可以压缩到 2 秒以内


九、快速验证命令清单

以下命令覆盖了 Pod 创建链路每个阶段的排查:

# ① API Server 是否收到请求
kubectl auth can-i create pods --namespace default

# ② Pod 是否已在 etcd 中
kubectl get pod nginx --watch &
kubectl apply -f pod.yaml

# ③ Scheduler 是否分配了节点
kubectl get pod nginx -o wide

# ④ Kubelet 是否开始创建容器(节点侧)
ssh <node> "crictl ps | grep nginx"

# ⑤ Pod 是否达到 Running 状态
kubectl get pod nginx -o jsonpath='{.status.phase}'

# ⑥ Endpoints 是否正确填充
kubectl get endpoints nginx-svc

# ⑦ Kube-proxy 规则是否生效
ssh <node> "iptables -L -t nat | grep nginx | head -20"

# ⑧ 如果 Pod 卡在 Pending,查看详细原因
kubectl describe pod nginx | grep -E "Conditions|Events:"
kubectl get pod nginx -o jsonpath='{.spec.schedulerName}'

# ⑨ 检查 etcd 是否正常(API Server 和 Controller Manager 依赖它)
kubectl get pods -n kube-system | grep etcd

十、总结

K8s 架构的核心不是"有哪些组件",而是组件之间通过 API Server + etcd Watch 机制串联成的协作网络

  1. API Server 是唯一的写入入口,认证、授权、准入控制三层全部通过才会写入 etcd。
  2. etcd 是唯一的真相来源,Controller Manager 和 Scheduler 都通过 Watch etcd 来感知集群状态变化,而不是依赖彼此直接通信。
  3. Scheduler 只做决策,把 nodeName 写回 API Server 就完成任务,不负责后续的 Pod 创建。
  4. Kubelet 只对自己节点的 Pod 负责,通过 CRI 与容器运行时交互,创建出真正的容器。
  5. Pause 容器是 Pod 网络稳定性的保障,理解它就能理解为什么 Pod 重启后 IP 不变。
  6. Endpoints Controller 经常被忽视,但它是 Service 与后端 Pod 之间的关键桥梁。

理解了这六点,K8s 的"架构"就不再是一张静态图片,而是一条随时可以在生产环境中用命令追踪的活路径。


如果觉得这篇文章有帮助,欢迎收藏、转发。


相关阅读

更多推荐