【云计算】【Kubernetes】 ② K8S的架构、应用及源码解析 - Pod 生命周期管理与 CRI 集成详解

📖目录

  • [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 execlogsportforward 等操作 快递站的对外服务窗口,客户可通过它查询包裹、要求开箱验货
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 是真正的"执行引擎",它会:

  1. 检查 Pod Sandbox 是否存在
  2. 拉取缺失的镜像
  3. 创建/启动缺失的容器
  4. 执行 post-start hooks
  5. 更新状态

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 的 volumesvolumeMounts 自动生成
  • 环境变量、命令参数等都从 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 容器?

相关推荐
人工智能训练1 小时前
跨架构突围!X86 Ubuntu Dify 无缝迁移 Arm64 openEuler Docker 实战指南
人工智能·ubuntu·docker·容器·架构·arm64·dify
AI云原生1 小时前
Docker部署多个Python项目如何使用Nginx实现负载均衡的解决方案
python·nginx·docker·云原生·容器·kubernetes·负载均衡
Gavin在路上1 小时前
架构设计之COLA架构实战
架构
拾忆,想起1 小时前
Dubbo服务调用失败调试指南:从问题定位到快速修复
前端·微服务·架构·dubbo·safari
..Move...1 小时前
企业级 K8s 中间件部署(Mysql主从)
mysql·中间件·kubernetes
存内计算开发者1 小时前
存算一体架构在空间计算中的应用
人工智能·神经网络·机器学习·计算机视觉·架构·空间计算·存算一体
技术传感器1 小时前
Prompt工程的艺术与科学:从“对话“到“编程“,掌握与大模型高效协作的元技能
人工智能·microsoft·架构·prompt·aigc
Gavin在路上1 小时前
架构设计之COLA架构
java·数据库·架构
碎像1 小时前
阿里云 ARMS 应用实时监控服务
java·阿里云·云计算