
本文基于 [Kubernetes 集群架构](https://kubernetes.io/docs/concepts/architecture/)、[Pod 生命周期](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/)、[调度](https://kubernetes.io/docs/concepts/scheduling-eviction/) 等官方文档，梳理 Pod 从被提交到在节点上运行的整体流程及涉及组件。

## 流程概览

Pod 创建不是单点完成，而是由控制面（control plane，即 API Server、etcd、Scheduler、Controller Manager 等）与节点上的 kubelet 协同完成：用户或控制器提交 Pod 后，请求先到 API Server（kube-apiserver），经认证、鉴权、准入后写入 etcd（集群元数据后端存储）；随后 Scheduler（kube-scheduler）为未绑定的 Pod 选择节点并写入绑定信息；目标节点上的 kubelet 监听到该 Pod 后，通过容器运行时（CRI）、网络插件（CNI）、存储接口（CSI）等拉取镜像、创建容器、配置网络与卷，并持续把 Pod 状态回写到 API Server。下文按阶段说明。

## 阶段一：请求进入与 API Server 处理

用户通过 `kubectl apply` / `kubectl create` 或其它客户端向 Kubernetes API 提交 Pod 规约（PodSpec），请求发往 API Server（kube-apiserver，即控制面暴露 Kubernetes API 的组件）。

1. 认证（Authentication）：API Server 验证请求者身份（证书、Token、ServiceAccount 等）。
2. 鉴权（Authorization）：确认该身份是否有权创建对应命名空间下的 Pod（如 RBAC，Role-Based Access Control，基于角色的访问控制）。
3. 准入控制（Admission Control）：可对创建请求做修改或校验，例如 MutatingAdmissionWebhook、ValidatingAdmissionWebhook、ResourceQuota 等；通过后，API Server 将 Pod 对象写入集群的后端存储。
4. 持久化：若集群使用 etcd 作为后端存储，Pod 的元数据与规约会写入 etcd。此时 Pod 已被集群“接受”，但尚未被调度到任何节点，其阶段（phase）为 Pending。

官方 [Pod 生命周期](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/) 中说明：Pod 从 Pending 开始，表示“已被集群接受，但一个或多个容器尚未就绪”，其中包含“等待被调度”的时间。

## 阶段二：调度（Scheduling）

kube-scheduler（Scheduler）是控制面组件，负责为“未指定运行节点”的 Pod 选择节点。据 [Cluster Architecture](https://kubernetes.io/docs/concepts/architecture/) 与 [Scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/) 文档，Scheduler 监听（watch）未绑定节点的 Pod，并执行：

1. 预选（Filter / Predicate）：根据 Pod 的资源需求、节点亲和/反亲和、污点与容忍、节点状态等，过滤掉不满足条件的节点。
2. 打分（Score / Priority）：在剩余节点上按资源均衡、亲和性、拓扑分布等策略打分，选出最优节点。
3. 绑定（Bind）：将选中的节点名写入 Pod 的 `spec.nodeName`（或通过 Binding 子资源），完成“调度”；此后该 Pod 由该节点上的 kubelet 负责运行。

同一 Pod（同一 UID）在其生命周期内只会被调度一次；若该 Pod 之后被替换（例如被控制器重建），新 Pod 拥有新 UID，会重新走一遍调度流程，且不保证落在同一节点。

## 阶段三：节点上创建与运行（kubelet）

目标节点上的 kubelet 是运行在每个节点上的代理，负责“根据 PodSpec 保证该节点上的 Pod 中容器处于运行且健康”。据 [Cluster Architecture](https://kubernetes.io/docs/concepts/architecture/)，kubelet 接收一组 PodSpec（通过 API Server 等机制获取），并确保其中描述的容器在运行。kubelet 并不管理非由 Kubernetes 创建的容器。

当 Pod 已被调度到本节点（`spec.nodeName` 为本节点）后，kubelet 大致会：

1. 从 API Server 同步该 Pod 的规约与状态（通常通过 watch 或轮询）。
2. 若 Pod 需要卷：通过 CSI（Container Storage Interface，容器存储接口）或内置（in-tree）卷插件挂载 Volume，供容器使用。
3. 通过 CRI（Container Runtime Interface，容器运行时接口）调用本节点的容器运行时（如 containerd、CRI-O）：先创建 Pod 沙箱（例如 pause 容器或等效沙箱），再按 Pod 规约创建并启动各个业务容器；若镜像不存在，先拉取镜像。
4. 网络：通过 CNI（Container Network Interface，容器网络接口）插件为 Pod 配置网络（分配 IP、设置路由等），使 Pod 可与集群内其它 Pod 或 Service（集群内逻辑服务与负载均衡）通信。
5. 执行 init 容器（若有）：按序执行完成后再启动主容器。
6. 启动主容器后，根据配置执行探针（Liveness、Readiness、Startup）并持续把容器与 Pod 状态写回 API Server（如 Running、Ready 等）。

Pod 生命周期文档指出：一旦 Pod 被调度并绑定到节点，kubelet 会通过容器运行时创建容器；容器有三种状态：Waiting、Running、Terminated。Pod 的 phase 会从 Pending 转为 Running（当至少一个主容器已启动），或在后续根据容器退出情况变为 Succeeded / Failed。

## kubelet 日志示例

以下为某节点上 kubelet 处理一个 Pod 创建时的典型日志（敏感信息已替换为占位：`<node-name>` 节点名、`<namespace>` 命名空间、`<pod-name>` Pod 名、`<pod-uid>` Pod UID、`<container-id>` 容器 ID）。可用 `journalctl -u kubelet.service --since="1 hour ago" | grep <pod-name>` 在节点上查看同类输出。

1. SyncLoop ADD：kubelet 从 API 收到该 Pod，加入同步循环。

```
<node-name> kubelet[...]: "SyncLoop ADD" source="api" pods=["<namespace>/<pod-name>"]
```

2. 拓扑与 sandbox：拓扑偏好（如 NUMA）决策后，发现尚无 sandbox，需新建。

```
<node-name> kubelet[...]: "Best TopologyHint" bestHint={"NUMANodeAffinity":3,"Preferred":true} pod="<namespace>/<pod-name>" containerName="nginx"
<node-name> kubelet[...]: "No sandbox for pod can be found. Need to start a new one" pod="<namespace>/<pod-name>"
```

3. 卷挂载：对 ServiceAccount 的 projected 卷执行挂载。

```
<node-name> kubelet[...]: "operationExecutor.VerifyControllerAttachedVolume started for volume \"kube-api-access-xxxx\" (UniqueName: \"kubernetes.io/projected/<pod-uid>-kube-api-access-xxxx\") pod \"<pod-name>\" (UID: \"<pod-uid>\") " pod="<namespace>/<pod-name>"
<node-name> kubelet[...]: "operationExecutor.MountVolume started for volume \"kube-api-access-xxxx\" ..." pod="<namespace>/<pod-name>"
<node-name> kubelet[...]: "MountVolume.SetUp succeeded for volume \"kube-api-access-xxxx\" ..." pod="<namespace>/<pod-name>"
```

4. SyncLoop UPDATE：Pod 规约或状态有更新，再次调和。

```
<node-name> kubelet[...]: "SyncLoop UPDATE" source="api" pods=["<namespace>/<pod-name>"]
```

5. PLEG ContainerStarted：PLEG（Pod Lifecycle Event Generator，Pod 生命周期事件生成器）从容器运行时拿到事件；先上报 sandbox 容器，再上报业务容器启动。

```
<node-name> kubelet[...]: "SyncLoop (PLEG): event for pod" pod="<namespace>/<pod-name>" event={"ID":"<pod-uid>","Type":"ContainerStarted","Data":"<sandbox-container-id>"}
<node-name> kubelet[...]: "SyncLoop (PLEG): event for pod" pod="<namespace>/<pod-name>" event={"ID":"<pod-uid>","Type":"ContainerStarted","Data":"<app-container-id>"}
```

6. 启动耗时统计：从 Pod 进入节点到 Running 的端到端时间及拉镜像区间。

```
<node-name> kubelet[...]: "Observed pod startup duration" pod="<namespace>/<pod-name>" podStartE2EDuration="18.1s" ... firstStartedPulling="..." lastFinishedPulling="..." observedRunningTime="..."
```

上述顺序与阶段三一致：收到 Pod → 卷挂载 → 创建 sandbox → 创建业务容器 → PLEG 上报 ContainerStarted → 更新启动耗时。

参考链接：

- [Cluster Architecture](https://kubernetes.io/docs/concepts/architecture/)
- [Pod lifecycle](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/)
- [Scheduling, Preemption and Eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/)
- [kube-scheduler](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/)
- [kubelet](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)

