📖目录
- [1. 前言:为什么 kubelet 是 K8s 的"心脏"?](#1. 前言:为什么 kubelet 是 K8s 的“心脏”?)
- [2. kubelet 架构](#2. kubelet 架构)
-
- [2.1 架构全景图:](#2.1 架构全景图:)
- [2.2 架构文字描述](#2.2 架构文字描述)
- [3. Pod 的"出生":从 Watch 到 Create](#3. Pod 的“出生”:从 Watch 到 Create)
-
- [3.1 Pod 来源:三种入口](#3.1 Pod 来源:三种入口)
- [3.2 PodSourceManager:多源聚合器](#3.2 PodSourceManager:多源聚合器)
- [4. Sync Loop:kubelet 的"永动机"](#4. Sync Loop:kubelet 的“永动机”)
-
- [4.1 Sync Loop 主循环(v1.28 源码精简)](#4.1 Sync Loop 主循环(v1.28 源码精简))
- [4.2 SyncPods:全量同步逻辑](#4.2 SyncPods:全量同步逻辑)
- [5. CRI 集成:kubelet 如何与 containerd 对话?](#5. CRI 集成:kubelet 如何与 containerd 对话?)
-
- [5.1 CRI 接口抽象(gRPC 定义)](#5.1 CRI 接口抽象(gRPC 定义))
- [5.2 kubelet 调用 containerd 的完整流程](#5.2 kubelet 调用 containerd 的完整流程)
-
- [5.2.1 步骤1:创建 Pod Sandbox(Pause 容器)](#5.2.1 步骤1:创建 Pod Sandbox(Pause 容器))
- [5.2.2 步骤2:创建业务容器](#5.2.2 步骤2:创建业务容器)
- [6. 探针(Probe)机制:如何判断应用是否"活着"?](#6. 探针(Probe)机制:如何判断应用是否“活着”?)
-
- [6.1 三种探针的作用场景](#6.1 三种探针的作用场景)
- [6.2 探针执行代码(带超时控制)](#6.2 探针执行代码(带超时控制))
- [7. 状态上报:kubelet 如何"汇报工作"?](#7. 状态上报:kubelet 如何“汇报工作”?)
- [8. 结语:kubelet --- 被低估的"节点操作系统"](#8. 结语:kubelet — 被低估的“节点操作系统”)
1. 前言:为什么 kubelet 是 K8s 的"心脏"?
"kubelet 不是 Kubernetes 的'仆人',而是节点上真正的'操作系统内核'。"
------ 《Kubernetes 源码设计哲学》
在 Kubernetes 架构中,kubelet 是运行在每个工作节点(Node)上的核心代理。它不参与调度决策,却负责将调度器分配的 Pod 真正"落地执行"。
-
官方定义 (来自 kubernetes.io):
The kubelet is the primary "node agent" that runs on each node. It ensures that containers are running in a Pod as specified.
-
大白话解释 :
如果把 Kubernetes 比作一家快递公司:
- API Server 是总部订单系统
- Scheduler 是派单员
- kubelet 就是快递小哥本人------他拿到派单后,亲自去仓库(容器运行时)取货(拉镜像)、打包(创建容器)、送货上门(启动 Pod),还要定时回访客户(执行探针)、上报配送状态(上报 Node/Pod 状态)。
没有 kubelet,再完美的调度也只是纸上谈兵。更关键的是,kubelet 是唯一能直接操作宿主机资源的组件------它管理 Cgroups、挂载磁盘、配置网络命名空间,本质上扮演了"云原生操作系统内核"的角色。
2. kubelet 架构
2.1 架构全景图:

2.2 架构文字描述
kubelet 不仅负责维持容器的生命周期,还协同 kube-controller-manager 管理存储卷,并通过 CNI(Container Network Interface)插件配置容器网络。其内部由多个功能模块协同工作,形成一套完整的节点级容器管理平台。
| 模块 | 职责 | 类比 |
|---|---|---|
| Kubelet Server | 对外暴露 HTTP/gRPC API,供 kube-apiserver、metrics-server 等调用;支持 kubectl exec、logs、portforward 等操作 |
快递站的对外服务窗口,客户可通过它查询包裹、要求开箱验货 |
| Pod Manager | 维护本地 Pod 缓存,聚合来自 API Server、静态文件等多源 Pod 配置 | 任务调度板,汇总所有待处理订单 |
| Sync Loop | 核心控制循环,持续比对期望状态与实际状态,驱动容器创建/删除/更新 | 大脑中枢,不断检查"该做的事是否都做了" |
| PLEG (Pod Lifecycle Event Generator) | 监听底层容器运行时事件(如容器退出、OOM),触发状态同步 | 手机震动提醒:"你的快递被签收了!" |
| Generic Runtime Manager | 封装 CRI 客户端逻辑,统一调用不同容器运行时(如 containerd、CRI-O) | 通用遥控器,可控制不同品牌的电视(运行时) |
| Volume Manager | 负责存储卷的准备、挂载、卸载,将宿主机路径映射到容器内 | 安装/拆卸快递车上的货架,确保货物(数据)正确装载 |
| Container Manager | 管理 cgroups 层级、QoS 分类、CPU/Memory 隔离、设备分配等资源策略 | 控制每辆车的油箱大小、优先通行权和载重限制 |
| Eviction Manager | 在内存、磁盘等资源紧张时,按优先级驱逐低 QoS 的 Pod | 仓库爆满时,优先清理"普通包裹",保全"加急件" |
| cAdvisor + Stats Provider | 采集容器 CPU、内存、网络、磁盘 I/O 等指标,通过 /stats/summary 暴露给 metrics-server |
车载 GPS+油耗仪,实时上报位置和能耗数据 |
| Status Manager | 异步将 Pod 和 Node 状态上报至 API Server,保证最终一致性 | 定时向总部发送"任务完成确认"消息 |
✅ 关键点 :kubelet 不直接操作容器引擎 ,而是通过 CRI(Container Runtime Interface) 抽象层与底层运行时通信。这种分层设计使 Kubernetes 能无缝切换运行时,真正实现"一次编排,随处运行"。在 CRI 之下,常见的实现包括:
- containerd :轻量级、符合 OCI 标准的工业级运行时,原生支持 CRI(通过
cri插件)- dockerd :Docker Engine 的守护进程,早期需通过内置的
dockershim适配 CRI(Kubernetes v1.24+ 已移除)
3. Pod 的"出生":从 Watch 到 Create
3.1 Pod 来源:三种入口
kubelet 通过三种方式获知新 Pod 的存在:
go
// pkg/kubelet/config/config.go
type SourceType string
const (
// 来自 API Server(最常见)
sourceApiserver SourceType = "api"
// 来自本地文件(Static Pod)
sourceFile SourceType = "file"
// 来自 HTTP endpoint(较少用)
sourceHTTP SourceType = "http"
)
- API Server 模式 :kubelet 通过 List-Watch 机制监听
/api/v1/pods?fieldSelector=spec.nodeName=<当前节点>。这是动态 Pod 的标准来源。 - Static Pod 模式 :kubelet 监控本地目录(如
/etc/kubernetes/manifests),任何.yaml文件变更都会触发 Pod 创建。常用于部署 kube-apiserver、etcd 等控制平面组件。 - HTTP 模式:通过 HTTP 接口提供 Pod 配置(已基本废弃)。
💡 大白话:就像快递小哥同时看三个消息源------总部APP推送(API Server)、老板微信发的紧急单(Static Pod)、路边贴的"代收包裹"纸条(HTTP)------全部汇总到他的待办列表里。
3.2 PodSourceManager:多源聚合器
所有来源的 Pod 最终被 PodSourceManager 聚合到一个统一 channel:
go
// pkg/kubelet/config/config.go
func (pm *PodSourceManager) mergePods() {
// 创建一个合并后的 channel
mergedCh := make(chan kubetypes.PodUpdate)
// 启动 goroutine 处理每个来源
go func() {
for update := range apiCh {
mergedCh <- update // 来自 API Server
}
}()
go func() {
for update := range fileCh {
mergedCh <- update // 来自本地文件
}
}()
// 返回合并后的 channel
return mergedCh
}
这个设计体现了 "关注点分离" 原则:不同来源只需实现自己的监听逻辑,无需关心如何被消费。
4. Sync Loop:kubelet 的"永动机"
这是 kubelet 最核心的无限循环,每秒检查一次所有 Pod 是否处于期望状态。
4.1 Sync Loop 主循环(v1.28 源码精简)
go
// pkg/kubelet/kubelet.go
func (kl *Kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
// 创建定时器
const syncPeriod = time.Second
timer := time.NewTimer(syncPeriod)
for {
select {
case u := <-updates:
// 处理 Pod 更新事件(新增/删除/修改)
kl.handlePodAdditions(u.Pods)
kl.handlePodDeletions(u.Pods)
kl.handlePodUpdates(u.Pods)
case <-timer.C:
// 每秒主动同步所有 Pod 状态(兜底机制)
handler.SyncPods()
timer.Reset(syncPeriod)
case <-housekeepingTicker.C:
// 执行清理任务(如回收孤儿容器)
kl.housekeeping()
case <-plegCh:
// 处理容器运行时事件(如容器崩溃)
kl.handlePLEGEvents()
}
}
}
🔍 关键洞察:
- 事件驱动:快速响应变化(低延迟)
- 定时轮询 :防止事件丢失(高可靠)
这种"双保险"设计,确保即使容器运行时崩溃重启,kubelet 也能恢复 Pod 状态。
4.2 SyncPods:全量同步逻辑
当定时器触发时,kubelet 会遍历所有已知 Pod 并调用 SyncPod:
go
// pkg/kubelet/kubelet.go
func (kl *Kubelet) SyncPods() {
pods := kl.podManager.GetPods()
for _, pod := range pods {
// 检查 Pod 是否需要更新
if kl.needsReconcile(pod) {
kl.syncPod(pod)
}
}
}
syncPod 是真正的"执行引擎",它会:
- 检查 Pod Sandbox 是否存在
- 拉取缺失的镜像
- 创建/启动缺失的容器
- 执行 post-start hooks
- 更新状态
5. CRI 集成:kubelet 如何与 containerd 对话?
5.1 CRI 接口抽象(gRPC 定义)
CRI 定义了两个核心服务:
protobuf
// cri-api/pkg/apis/runtime/v1/api.proto
service RuntimeService {
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse);
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse);
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
}
service ImageService {
rpc PullImage(PullImageRequest) returns (PullImageResponse);
rpc ListImages(ListImagesRequest) returns (ListImagesResponse);
}
🧠 大白话解释 CRI :
CRI 就像统一的"快递操作协议"。无论仓库用的是顺丰(containerd)、京东物流(CRI-O)还是菜鸟驿站(Docker Shim),只要遵守同一套指令格式(如"请创建一个包裹"、"请启动运输"),kubelet 就能无缝对接。
5.2 kubelet 调用 containerd 的完整流程
5.2.1 步骤1:创建 Pod Sandbox(Pause 容器)
go
// pkg/kubelet/kuberuntime/kuberuntime_sandbox.go
func (ds *dockerService) RunPodSandbox(config *runtimeapi.PodSandboxConfig) (string, error) {
// 1. 构造 Pause 容器配置
spec := ds.makeSandboxDockerSpec(config)
// 2. 调用 containerd 创建容器
containerID, err := ds.client.CreateContainer(ctx, spec)
if err != nil {
return "", err
}
// 3. 启动容器
err = ds.client.StartContainer(ctx, containerID)
return containerID, err
}
⚙️ 技术细节:
- Pause 容器使用极简镜像(如
k8s.gcr.io/pause:3.9)- 它的主要作用是持有 Linux Namespace(网络、IPC、UTS)
- 业务容器通过
--net=container:<pause-id>加入其网络栈
5.2.2 步骤2:创建业务容器
go
// pkg/kubelet/kuberuntime/kuberuntime_container.go
func (m *kubeGenericRuntimeManager) CreateContainer(pod *v1.Pod, container *v1.Container, podSandboxID string) (string, error) {
// 1. 构造容器配置
containerConfig := &runtimeapi.ContainerConfig{
Metadata: &runtimeapi.ContainerMetadata{
Name: container.Name,
},
Image: &runtimeapi.ImageSpec{
Image: container.Image,
},
Command: container.Command,
Args: container.Args,
Env: convertEnvVars(container.Env),
Mounts: makeMounts(pod, container),
}
// 2. 调用 CRI 创建容器
containerID, err := m.runtimeService.CreateContainer(podSandboxID, containerConfig, sandboxConfig)
if err != nil {
return "", err
}
// 3. 启动容器
err = m.runtimeService.StartContainer(containerID)
return containerID, err
}
📌 关键点:
- 容器的
Mounts字段由 kubelet 根据 Pod 的volumes和volumeMounts自动生成- 环境变量、命令参数等都从 Pod Spec 中提取
6. 探针(Probe)机制:如何判断应用是否"活着"?
6.1 三种探针的作用场景
| 探针类型 | 作用时机 | 失败后果 | 典型配置 |
|---|---|---|---|
| livenessProbe | 应用运行中 | 杀死容器并重启 | HTTP GET /healthz |
| readinessProbe | 流量接入前 | 从 Service Endpoints 移除 | TCP Socket 8080 |
| startupProbe | 容器刚启动 | 延迟 liveness/readiness 检查 | Exec command |
🍜 大白话解释(煮方便面版):
- startupProbe:等水烧开(应用初始化完成,比如加载大模型)
- readinessProbe:尝一口汤是否够味(能否对外服务)
- livenessProbe:每隔5分钟尝一口,如果糊了就倒掉重煮(重启容器)
6.2 探针执行代码(带超时控制)
go
// pkg/kubelet/prober/prober.go
func (pb *prober) runProbe(probeType ProbeType, pod *v1.Pod, container v1.Container) Result {
probe := getProbe(probeType, container)
// 设置超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(probe.TimeoutSeconds)*time.Second)
defer cancel()
switch probe.Handler.Type {
case "HTTP":
return pb.probeHTTP(ctx, probe.HTTPGet, pod, container)
case "TCP":
return pb.probeTCP(ctx, probe.TCPSocket, pod, container)
case "Exec":
return pb.probeExec(ctx, probe.Exec, pod, container)
}
return Failure
}
⏱️ 公式理解 (探针超时):
实际超时时间 =
initialDelaySeconds+timeoutSeconds例如:
initialDelaySeconds=30,timeoutSeconds=5→ 第一次检查在第30秒开始,若5秒内无响应则失败。
重要 :periodSeconds控制后续检查间隔,不影响首次检查时间。
7. 状态上报:kubelet 如何"汇报工作"?
kubelet 通过 Status Manager 将 Pod 状态同步到 API Server:
go
// pkg/kubelet/status/status_manager.go
func (m *manager) syncBatch() {
// 1. 收集本地所有 Pod 状态
statuses := m.generateAPIPodStatuses()
// 2. 与 API Server 中的状态对比
for _, status := range statuses {
if !m.isStatusEqual(status, apiServerStatus) {
// 3. 异步 PATCH 更新(避免阻塞主循环)
go m.patchPodStatus(status)
}
}
}
🌐 设计哲学 :
kubelet 不等待 API Server 确认 就继续处理下一个 Pod,保证高吞吐。即使网络中断,状态变更也会在恢复后重试。这种最终一致性模型是分布式系统的基石。
8. 结语:kubelet --- 被低估的"节点操作系统"
kubelet 远不止是一个"容器启动器"。它实现了:
- 声明式 API 的最终一致性
- 多运行时抽象(CRI)
- 健康自愈(探针+重启)
- 资源隔离(Cgroups + QoS)
- 存储网络集成(Volume + CNI)
正如 Linux 内核管理进程、内存、I/O,kubelet 管理着云原生时代的"应用进程"。理解 kubelet,就是理解 Kubernetes 如何在物理世界落地。
下一篇预告:
第三章:深入 containerd ------ CRI 插件如何驱动 OCI 容器?