一、资源对象是什么
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 上真正的应用进程。
