← 返回上一页

tech

Kubernetes Worker Node 与资源对象

理解资源对象、控制器、kubelet、容器运行时及 Worker Node 的协作关系

一、资源对象是什么

Kubernetes 中的资源对象本质上是数据结构,不是进程、线程或协程。

例如 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx

它会经历:

YAML / JSON
  ↓
API Server
  ↓
Go 结构体 *v1.Pod
  ↓
序列化
  ↓
etcd 中的字节数据

etcd 中保存的是资源对象的持久化数据:

/registry/pods/default/nginx

资源对象本身不会运行。

二、资源对象和实际进程

Deployment 对象
  ↓
ReplicaSet 对象
  ↓
Pod 对象
  ↓
Container
  ↓
应用进程

例如:

Deployment:
    描述副本数量和发布策略

ReplicaSet:
    维持 Pod 数量

Pod:
    描述一组 Container

Container:
    应用进程的运行环境

应用进程:
    最终运行在 Worker Node 上

因此:

Deployment、Service、PVC、ConfigMap:
    都是数据对象

Pod:
    也是数据对象,不是进程

Container:
    由 kubelet 调用容器运行时创建

应用进程:
    才是真正运行的程序

严格来说,Container 也不是虚拟机,而是由 Linux Namespace、Cgroup、文件系统和网络等机制隔离出的进程环境。

三、资源对象由谁创建

并不是所有对象都由 kube-controller-manager 创建。

Deployment:
    用户、Operator 创建

ReplicaSet:
    Deployment Controller 创建

Pod:
    ReplicaSet、Job、DaemonSet、StatefulSet 等 Controller 创建

Service:
    用户或 Operator 创建

ConfigMap / Secret:
    用户或 Operator 创建

PVC:
    用户创建

PV:
    PV Controller 或外部 Provisioner 创建

Node:
    kubelet 注册或集群工具创建

Pod Status:
    kubelet 更新

Pod.spec.nodeName:
    Scheduler 更新

无论谁创建,通常都要经过:

创建者
  ↓
API Server
  ↓
校验 / Admission
  ↓
etcd

例如:

用户创建 Deployment
  ↓
API Server 保存 Deployment
  ↓
Deployment Controller 创建 ReplicaSet
  ↓
ReplicaSet Controller 创建 Pod
  ↓
Scheduler 绑定 Node
  ↓
kubelet 启动 Container

四、是什么东西读取期望状态

读取资源对象并实现实际效果的是控制循环和节点执行器:

Controller Manager:
    读取 Deployment、ReplicaSet、Job 等对象

Scheduler:
    读取未调度的 Pod

kubelet:
    读取分配到当前节点的 Pod

kube-proxy:
    读取 Service、EndpointSlice

CSI:
    处理存储挂载

CNI:
    配置 Pod 网络

这些组件会比较:

spec 期望状态
  ↓
当前实际状态
  ↓
执行修正动作

例如:

desired := deployment.Spec.Replicas
actual := countPods()

if actual < desired {
    createPods()
}

这就是 Kubernetes 的声明式控制模型。

五、Controller 是进程、线程还是协程

通常关系如下:

kube-controller-manager 进程
  ├── Deployment Controller
  ├── ReplicaSet Controller
  ├── Job Controller
  └── Node Controller

这些 Controller 通常不是独立进程,而是运行在同一个 kube-controller-manager 进程中的 Go 对象和 goroutine。

kube-controller-manager:
    一个独立进程

Deployment Controller:
    进程内的 Controller 实例

Worker:
    处理队列的 goroutine

一个 Controller 的典型流程:

Watch 资源变化
  ↓
事件处理器
  ↓
WorkQueue
  ↓
Worker goroutine
  ↓
重新读取对象
  ↓
比较期望状态和实际状态
  ↓
创建、更新或删除对象

伪代码:

func onEvent(obj Object) {
    queue.Add(obj.Namespace + "/" + obj.Name)
}

func worker() {
    for {
        key := queue.Get()
        reconcile(key)
    }
}

六、Deployment Controller 的例子

Deployment.spec.replicas = 3
  ↓
Deployment Controller 读取
  ↓
发现 ReplicaSet 状态不符合期望
  ↓
创建或更新 ReplicaSet

然后:

ReplicaSet.spec.replicas = 3
  ↓
ReplicaSet Controller 读取
  ↓
发现 Pod 数量不足
  ↓
创建 Pod

相关 Controller 都运行在:

kube-controller-manager

其中 apps.go 主要负责启动:

Deployment Controller
ReplicaSet Controller
StatefulSet Controller
DaemonSet Controller

七、Control Plane 和 Worker Node

Control Plane 和 Worker Node 首先是职责上的逻辑划分。

Control Plane:
    管理集群状态、调度和控制

Worker Node:
    执行 Pod 和容器

Control Plane 通常包括:

kube-apiserver
etcd
kube-scheduler
kube-controller-manager

Worker Node 通常包括:

kubelet
container runtime
kube-proxy
CNI
CSI

生产环境中通常也会把它们部署在不同机器:

Control Plane Node
  ├── API Server
  ├── Scheduler
  ├── Controller Manager
  └── etcd

Worker Node
  ├── kubelet
  ├── containerd / CRI-O
  └── Pod

但开发环境可以把 Control Plane 和 Worker Node 放在同一台机器上。

所以:

逻辑上:
    是职责划分

物理上:
    通常是不同节点,但不是绝对要求

八、资源对象存在于哪里

一个资源对象可能同时存在于多个位置,但含义不同:

etcd:
    持久化对象

API Server:
    对象访问和修改入口

Watch Cache:
    API Server 的内存缓存

Controller:
    本地 Informer Cache 中的副本

Scheduler:
    本地调度缓存中的副本

kubelet:
    本地 Pod Manager 中的副本

Worker Node:
    根据 Pod 对象创建出的 Sandbox、Container 和进程

例如 Pod:

Pod.spec:
    保存在 etcd

Scheduler:
    设置 Pod.spec.nodeName

kubelet:
    读取 Pod.spec

container runtime:
    创建 Sandbox 和 Container

Linux:
    运行应用进程

kubelet:
    读取运行状态并更新 Pod.status

资源对象本身属于整个 Kubernetes 集群,不只属于 Control Plane 或 Worker Node。

九、Worker Node 的核心是 kubelet

Worker Node 上,kubelet 是连接 Control Plane 和本机运行时的主要组件。

API Server
  ⇄ HTTPS REST / Watch
kubelet
  ⇄ CRI gRPC / Unix Socket
container runtime
  ↓
应用进程

kubelet 主要负责:

获取分配给当前节点的 Pod
创建和删除 Pod
调用容器运行时
管理 Volume
更新 Pod Status
更新 Node Status
发送事件和心跳

kubelet 通过字段过滤只监听当前节点的 Pod:

spec.nodeName == 当前节点名

简化流程:

API Server Watch Pod
  ↓
Reflector
  ↓
PodConfig
  ↓
Pod Worker
  ↓
SyncPod
  ↓
CRI
  ↓
container runtime

十、Worker Node 上各组件的通信方式

kubelet 与 API Server

kubelet
  └── HTTPS REST / Watch ──▶ API Server

通常通过 kubeconfig 或 kubelet 配置获取 API Server 地址。

通信内容:

Pod
Node
ConfigMap
Secret
Lease
Pod Status
Node Status
Event

kubelet 与 Container Runtime

不是 HTTP,也不是 kubeconfig。

kubelet
  └── CRI gRPC / Unix Socket ──▶ containerd 或 CRI-O

例如:

/run/containerd/containerd.sock
/var/run/crio/crio.sock

kubelet 通过 CRI 调用:

RunPodSandbox()
CreateContainer()
StartContainer()
StopContainer()
RemoveContainer()

Container Runtime 与 CNI

CNI 通常不是长期运行的服务,而是插件二进制:

/opt/cni/bin/
/etc/cni/net.d/

调用关系:

container runtime
  ↓ 执行 CNI 插件
CNI
  ↓
创建网卡、分配 IP、配置路由

这里通常不是 HTTP,也不使用 kubeconfig。

kube-proxy 与 API Server

kube-proxy
  └── HTTPS REST / Watch ──▶ API Server

kube-proxy Watch:

Service
EndpointSlice
Node

然后修改本机网络规则:

iptables / IPVS / nftables

kubelet 与 CSI

CSI Node Plugin 通常运行在 Worker Node 上的独立容器中。

kubelet
  └── CSI gRPC / Unix Socket ──▶ CSI Node Plugin

CSI 负责:

挂载磁盘
卸载磁盘
创建挂载点
处理存储设备

CSI 插件的外部存储地址和认证方式由驱动自身配置决定,不是 kubeconfig。

十一、组件如何知道对方的位置

kubelet → API Server:
    kubeconfig 或 kubelet 配置

kube-proxy → API Server:
    kube-proxy kubeconfig 或配置文件

kubelet → container runtime:
    Unix Socket 配置

container runtime → CNI:
    本地插件目录和 CNI 配置文件

kubelet → CSI:
    本地 CSI Unix Socket

API Server → etcd:
    --etcd-servers 配置

因此并不是所有组件都通过 HTTP,也不是所有组件都使用 kubeconfig。

十二、Node 服务器与 Node 资源对象

Node 有两个容易混淆的含义:

Node 服务器:
    一台真实的物理机或虚拟机

Node 资源对象:
    API Server 中描述这台服务器的一份数据

Worker Node 通常不是一个 Pod,而是一台可以运行多个 Pod 的机器:

Worker Node
  ├── kubelet
  ├── container runtime
  ├── kube-proxy
  └── 多个 Pod
       ├── Container
       └── Container

一个 Pod 通常不会跨越多个 Node。Pod 内的容器共享网络命名空间、Pod IP 和挂载到 Pod 的卷;不同 Pod 则可以运行在同一台 Node 上。

Node 如何加入集群

服务器需要先由物理设施、虚拟化平台、云平台或基础设施工具创建,再安装并启动 kubelet 和容器运行时。kubelet 启动后,向 API Server 注册或更新对应的 Node 资源对象:

真实服务器
  ↓ 安装并启动 kubelet
kubelet
  ↓
API Server
  ↓
Node 资源对象

一个 Node 对象可能包含以下信息:

apiVersion: v1
kind: Node
metadata:
  name: worker-1
status:
  addresses:
    - type: InternalIP
      address: 10.0.0.21
  capacity:
    cpu: "8"
    memory: 32Gi

这些字段描述节点的名称、地址、容量、操作系统、架构、可调度状态和健康心跳,但不包含服务器本身。

因此,执行:

kubectl apply -f node.yaml

最多只能在 API Server 中创建一份 Node 记录,不会创建虚拟机、安装 kubelet 或启动服务器。如果背后没有真实 kubelet,Pod 即使被调度到该 Node,也没有节点组件负责执行,通常会一直无法正常运行。

服务器的创建和扩容属于外部基础设施流程:

Terraform / Ansible / 云平台 API / Cluster Autoscaler
  ↓
创建或扩容服务器
  ↓
安装并启动 kubelet
  ↓
kubelet 注册 Node 对象

Pod 如何选择 Node

Pod 创建时通常还没有确定运行节点:

spec:
  nodeName: ""

Scheduler 会持续观察没有 spec.nodeName 的 Pod,并执行过滤和打分:

未调度 Pod
  ↓
获取可用 Node
  ↓
过滤不满足条件的 Node
  ↓
为候选 Node 打分
  ↓
选择 worker-1
  ↓
写入 Pod.spec.nodeName

例如,调度完成后:

spec:
  nodeName: worker-1

之后只有 worker-1 上的 kubelet 会处理这个 Pod。

Scheduler 通常会综合以下条件:

CPU 和内存是否足够
nodeSelector
nodeAffinity
taint / toleration
Pod affinity / anti-affinity
拓扑分布约束
端口冲突
卷是否可以使用

例如:

spec:
  nodeSelector:
    disktype: ssd

只有带有对应标签的 Node 才会成为候选节点:

metadata:
  labels:
    disktype: ssd

假设集群有两个 Worker Node:

worker-1: 8 CPU, 32GiB
worker-2: 4 CPU, 16GiB

创建 5 个副本后,Scheduler 会根据资源和其他约束进行分配,例如:

worker-1
  ├── Pod 1
  ├── Pod 2
  └── Pod 3

worker-2
  ├── Pod 4
  └── Pod 5

这里的 3/2 只是一个可能结果,实际分配取决于请求资源、节点状态、亲和性和拓扑约束。Node 不会主动“接收”任意 Pod,而是由 Scheduler 把绑定结果写入 Pod 对象,再由目标节点上的 kubelet 执行。

十三、Pod 从对象变成进程

当 Pod 已经被 Scheduler 绑定到某个 Worker Node:

Pod 对象
  ↓ API Server Watch
kubelet
  ↓
创建 Pod Sandbox
  ↓
配置网络
  ↓
挂载 Volume
  ↓
创建 Container
  ↓
启动 Container
  ↓
应用进程

最终结果:

Deployment:
    仍然是资源对象

Pod:
    仍然是资源对象

Container:
    运行环境

应用进程:
    真正执行的程序

资源对象不会直接变成应用进程,而是由 Controller、Scheduler、kubelet 和容器运行时逐步把声明转换为实际运行状态。

十四、总结

资源对象:
    描述期望状态的数据

etcd:
    保存资源对象

API Server:
    提供统一访问和 Watch

Controller:
    读取资源,维护对象之间的关系

Scheduler:
    为 Pod 选择 Node

kubelet:
    把 Pod 对象转换成运行中的 Container

container runtime:
    创建 Sandbox、Container 和应用进程

kube-proxy:
    配置 Service 转发规则

CNI:
    配置 Pod 网络

CSI:
    配置 Pod 存储

完整链路:

资源对象
  ↓
etcd
  ↓
API Server Watch
  ↓
Controller / Scheduler / kubelet
  ↓
控制器修改对象或节点执行器执行本地操作
  ↓
Container Runtime
  ↓
应用进程

一句话:

Kubernetes 资源对象只是存储在 etcd 中的声明式数据。Controller 和 Scheduler 负责根据这些数据协调集群状态,kubelet 负责把已经调度到节点的 Pod 转换成 Container,最终由容器运行时启动 Worker Node 上真正的应用进程。