Kubernetes-04-Kubelet-概览

模块职责

kubelet 是运行在每个节点上的节点代理(Node Agent),负责管理节点上的 Pod 和容器生命周期。它是 Kubernetes 数据平面的核心组件,直接与容器运行时交互,确保 Pod 按照期望状态运行。

核心职责

  1. Pod 生命周期管理

    • 监听 API Server 分配到本节点的 Pod
    • 通过容器运行时(CRI)启动、停止、重启容器
    • 管理 Pod 的各个阶段:Pending → Running → Succeeded/Failed
  2. 容器健康检查

    • 执行 Liveness Probe(存活探针):失败时重启容器
    • 执行 Readiness Probe(就绪探针):失败时从 Service Endpoints 移除
    • 执行 Startup Probe(启动探针):容器启动时的健康检查
  3. 资源管理

    • 通过 cgroups 限制容器的 CPU、内存、磁盘 I/O
    • 节点资源监控(CPU、内存、磁盘使用率)
    • 驱逐低优先级 Pod(节点资源不足时)
  4. 存储卷管理

    • 挂载/卸载 Volume(EmptyDir、HostPath、PV 等)
    • 与 CSI Driver 交互,管理云存储卷
    • 管理 ConfigMap、Secret 挂载
  5. 镜像管理

    • 拉取容器镜像(从镜像仓库)
    • 镜像垃圾回收(删除未使用的镜像)
    • 镜像拉取策略(Always、IfNotPresent、Never)
  6. 节点状态上报

    • 定期向 API Server 上报节点状态(Ready、NotReady)
    • 上报节点资源容量和可分配资源
    • 上报节点信息(OS、Kernel、Container Runtime 版本)

输入/输出

输入:

  • Pod Spec:来自 API Server 的 Pod 定义(通过 List-Watch)
  • 配置文件:Kubelet 配置(--config
  • 静态 Pod:本地 YAML 文件(--pod-manifest-path

输出:

  • 容器操作:通过 CRI 创建/停止/删除容器
  • 节点状态更新:向 API Server 上报节点和 Pod 状态
  • Event 记录:记录 Pod 相关事件(如 Pulled、Created、Started、Failed)

Kubelet 架构图

flowchart TB
    subgraph "Kubelet"
        subgraph "核心组件"
            SYNCLOOP[SyncLoop<br/>主循环]
            PODWORKERS[PodWorkers<br/>Pod 并发管理]
            STATUSMGR[StatusManager<br/>状态上报]
            PROBEMGR[ProbeManager<br/>健康检查]
        end
        
        subgraph "资源管理"
            VOLMGR[VolumeManager<br/>存储卷管理]
            IMGMGR[ImageManager<br/>镜像管理]
            EVICTION[EvictionManager<br/>驱逐管理]
            CGROUPMGR[CgroupManager<br/>资源限制]
        end
        
        subgraph "运行时接口"
            CRI[Container Runtime Interface<br/>CRI]
            CNI[Container Network Interface<br/>CNI]
            CSI[Container Storage Interface<br/>CSI]
        end
    end
    
    subgraph "外部组件"
        API[API Server]
        RUNTIME[Container Runtime<br/>containerd/CRI-O]
        NETWORK[CNI Plugin<br/>Calico/Flannel]
        STORAGE[CSI Driver<br/>云存储]
    end
    
    API -->|List-Watch Pods| SYNCLOOP
    SYNCLOOP --> PODWORKERS
    PODWORKERS --> CRI
    PODWORKERS --> VOLMGR
    PODWORKERS --> PROBEMGR
    STATUSMGR -->|Update Pod Status| API
    PROBEMGR --> STATUSMGR
    
    CRI --> RUNTIME
    CNI --> NETWORK
    CSI --> STORAGE
    VOLMGR --> CSI
    
    style SYNCLOOP fill:#FF6B6B,color:#fff
    style PODWORKERS fill:#4ECDC4,color:#fff
    style CRI fill:#45B7D1,color:#fff

SyncLoop(主循环)

SyncLoop 工作原理

SyncLoop 是 Kubelet 的核心事件循环,持续监听多个事件源,驱动 Pod 状态同步:

// pkg/kubelet/kubelet.go

func (kl *Kubelet) syncLoop(ctx context.Context, updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
    for {
        select {
        // 1. 来自 API Server 的 Pod 更新
        case u := <-updates:
            handler.HandlePodAdditions(u.Pods)
            handler.HandlePodUpdates(u.Pods)
            handler.HandlePodRemoves(u.Pods)
            
        // 2. 定时同步(每秒一次)
        case <-syncTicker.C:
            handler.HandlePodSyncs(kl.getPodsToSync())
            
        // 3. Liveness/Readiness Probe 结果
        case update := <-kl.livenessManager.Updates():
            handler.HandlePodLivenessUpdates(update)
            
        // 4. PLEG(Pod Lifecycle Event Generator)事件
        case e := <-plegCh:
            handler.HandlePLEGEvent(e)
            
        // 5. 清理已终止的 Pod
        case <-housekeepingTicker.C:
            handler.HandlePodCleanups()
        }
    }
}

事件源详解

事件源 触发时机 处理逻辑
API Server Updates Pod 被调度到本节点、Pod 被删除 启动/停止容器
Sync Ticker 每秒触发一次 检查 Pod 实际状态与期望状态是否一致
Probe Updates Liveness/Readiness Probe 失败 重启容器 / 更新 Pod.Status.Conditions
PLEG Events 容器状态变化(启动、退出) 更新 Pod Status,触发重启
Housekeeping 每 2 分钟触发一次 清理已终止的 Pod、垃圾回收镜像

PodWorkers(Pod 并发管理)

架构设计

每个 Pod 有一个独立的 Worker Goroutine,串行处理该 Pod 的所有操作(避免并发冲突):

// pkg/kubelet/pod_workers.go

type podWorkers struct {
    // 每个 Pod 一个 Worker
    podUpdates map[types.UID]chan workUpdate
    
    // Worker 状态
    podSyncStatuses map[types.UID]*podSyncStatus
}

// 启动 Pod Worker
func (p *podWorkers) managePodLoop(podUID types.UID) {
    for update := range p.podUpdates[podUID] {
        // 串行处理 Pod 的所有操作
        p.syncPodFn(update.Pod, update.UpdateType)
    }
}

syncPod(同步 Pod 状态)

// syncPod 同步 Pod 状态(Kubelet 最核心的函数)
func (kl *Kubelet) syncPod(ctx context.Context, pod *v1.Pod, podStatus *kubecontainer.PodStatus) error {
    // 1. 检查 Pod 是否应该运行
    if !kl.podWorkers.IsPodTerminationRequested(pod.UID) {
        // Pod 未被删除,确保运行
        
        // 2. 创建 Pod 的数据目录
        if err := kl.makePodDataDirs(pod); err != nil {
            return err
        }
        
        // 3. 挂载 Volume
        if err := kl.volumeManager.WaitForAttachAndMount(pod); err != nil {
            return err
        }
        
        // 4. 拉取镜像
        if err := kl.imagePuller.EnsureImagesExist(pod); err != nil {
            return err
        }
        
        // 5. 创建/启动容器
        result := kl.containerRuntime.SyncPod(pod, podStatus)
        if result.Error() != nil {
            return result.Error()
        }
        
        // 6. 更新 Pod Status
        kl.statusManager.SetPodStatus(pod, result.PodStatus)
    } else {
        // Pod 被删除,确保清理
        
        // 1. 停止容器
        if err := kl.containerRuntime.KillPod(pod, podStatus); err != nil {
            return err
        }
        
        // 2. 卸载 Volume
        if err := kl.volumeManager.WaitForUnmount(pod); err != nil {
            return err
        }
        
        // 3. 清理 Pod 目录
        if err := kl.cleanupPodDir(pod); err != nil {
            return err
        }
    }
    
    return nil
}

CRI(Container Runtime Interface)

接口定义

CRI 是 Kubelet 与容器运行时的标准接口(gRPC):

// pkg/kubelet/cri/runtime/runtime.go

// RuntimeService CRI 运行时接口
type RuntimeService interface {
    // Pod 操作
    RunPodSandbox(config *PodSandboxConfig) (string, error)
    StopPodSandbox(podSandboxID string) error
    RemovePodSandbox(podSandboxID string) error
    PodSandboxStatus(podSandboxID string) (*PodSandboxStatus, error)
    ListPodSandbox(filter *PodSandboxFilter) ([]*PodSandbox, error)
    
    // Container 操作
    CreateContainer(podSandboxID string, config *ContainerConfig, sandboxConfig *PodSandboxConfig) (string, error)
    StartContainer(containerID string) error
    StopContainer(containerID string, timeout int64) error
    RemoveContainer(containerID string) error
    ListContainers(filter *ContainerFilter) ([]*Container, error)
    ContainerStatus(containerID string) (*ContainerStatus, error)
    
    // 镜像操作
    PullImage(image *ImageSpec, auth *AuthConfig) (string, error)
    RemoveImage(image *ImageSpec) error
    ListImages(filter *ImageFilter) ([]*Image, error)
}

SyncPod 调用 CRI 的流程

sequenceDiagram
    participant KL as Kubelet
    participant CRI as CRI Runtime
    participant CTR as Container Runtime<br/>(containerd)
    
    Note over KL,CTR: 场景:启动一个包含 2 个容器的 Pod
    
    KL->>CRI: RunPodSandbox(podConfig)
    CRI->>CTR: 创建 Pod Sandbox<br/>(配置网络、IPC、UTS Namespace)
    CTR-->>CRI: sandboxID="abc123"
    CRI-->>KL: sandboxID
    
    Note over KL: 串行启动 Init Containers
    
    KL->>CRI: CreateContainer(sandboxID, initContainerConfig)
    CRI->>CTR: 创建 Init Container
    CTR-->>CRI: containerID="init-001"
    CRI-->>KL: containerID
    
    KL->>CRI: StartContainer("init-001")
    CRI->>CTR: 启动 Init Container
    CTR-->>CRI: Success
    CRI-->>KL: Success
    
    KL->>KL: 等待 Init Container 完成
    
    Note over KL: 并行启动 App Containers
    
    par 并行启动
        KL->>CRI: CreateContainer(sandboxID, container1Config)
        CRI->>CTR: 创建 Container-1
        CTR-->>CRI: containerID="app-001"
        KL->>CRI: StartContainer("app-001")
    and
        KL->>CRI: CreateContainer(sandboxID, container2Config)
        CRI->>CTR: 创建 Container-2
        CTR-->>CRI: containerID="app-002"
        KL->>CRI: StartContainer("app-002")
    end

ProbeManager(健康检查管理器)

三种探针类型

// pkg/kubelet/prober/prober.go

type Probe struct {
    // 探针类型
    Type ProbeType  // Liveness / Readiness / Startup
    
    // 探针配置
    Handler v1.ProbeHandler  // Exec / HTTPGet / TCPSocket / gRPC
    
    // 探针参数
    InitialDelaySeconds int32  // 初始延迟
    TimeoutSeconds      int32  // 超时时间
    PeriodSeconds       int32  // 探测周期
    SuccessThreshold    int32  // 成功阈值
    FailureThreshold    int32  // 失败阈值
}

探针类型对比:

探针类型 失败时的行为 适用场景
Liveness Probe 重启容器 检测容器死锁(进程存在但无法响应)
Readiness Probe 从 Service Endpoints 移除 检测容器未就绪(如加载配置、预热缓存)
Startup Probe 重启容器(仅启动阶段) 慢启动容器(如 Java 应用)

探针执行流程

// 探针执行器
func (pb *prober) probe(probeType ProbeType, pod *v1.Pod, container *v1.Container) (Result, error) {
    // 1. 根据 Handler 类型执行探测
    switch container.LivenessProbe.Handler {
    case ExecAction:
        // 在容器内执行命令
        result = pb.exec.Probe(pod, container, cmd)
        
    case HTTPGetAction:
        // 发送 HTTP GET 请求
        result = pb.http.Probe(url, headers, timeout)
        
    case TCPSocketAction:
        // 尝试 TCP 连接
        result = pb.tcp.Probe(host, port, timeout)
        
    case GRPCAction:
        // 调用 gRPC Health Check
        result = pb.grpc.Probe(service, timeout)
    }
    
    // 2. 更新探测结果
    if result == Success {
        pb.successCount++
        if pb.successCount >= SuccessThreshold {
            return Success, nil
        }
    } else {
        pb.failureCount++
        if pb.failureCount >= FailureThreshold {
            return Failure, nil
        }
    }
    
    return Unknown, nil
}

VolumeManager(存储卷管理器)

Volume 类型

Volume 类型 说明 使用场景
EmptyDir 临时目录(Pod 删除时清空) 缓存、临时文件
HostPath 挂载节点目录 访问节点文件系统
PersistentVolumeClaim 持久化存储(PVC → PV) 数据库、文件存储
ConfigMap 配置文件挂载 应用配置
Secret 敏感信息挂载 密码、证书
CSI Volume 云存储(通过 CSI Driver) AWS EBS、Azure Disk、GCE PD

Volume 挂载流程

sequenceDiagram
    participant KL as Kubelet
    participant VM as VolumeManager
    participant CSI as CSI Driver
    participant CLOUD as Cloud Storage
    
    Note over KL,CLOUD: 场景:挂载 AWS EBS Volume
    
    KL->>VM: WaitForAttachAndMount(pod)
    
    Note over VM: 步骤 1Attach(挂载到节点)
    
    VM->>CSI: ControllerPublishVolume(volumeID, nodeID)
    CSI->>CLOUD: AWS API: AttachVolume
    CLOUD-->>CSI: /dev/xvdf
    CSI-->>VM: Success
    
    Note over VM: 步骤 2Mount(挂载到 Pod 目录)
    
    VM->>CSI: NodeStageVolume(volumeID, stagingPath)
    CSI->>CSI: mount /dev/xvdf /var/lib/kubelet/plugins/.../stagingPath
    CSI-->>VM: Success
    
    VM->>CSI: NodePublishVolume(volumeID, targetPath)
    CSI->>CSI: bind mount stagingPath -> /var/lib/kubelet/pods/{pod-uid}/volumes/{volume-name}
    CSI-->>VM: Success
    
    VM-->>KL: Volume 挂载完成

EvictionManager(驱逐管理器)

驱逐信号

当节点资源不足时,Kubelet 会驱逐低优先级 Pod:

驱逐信号 阈值示例 说明
memory.available < 100Mi 可用内存不足
nodefs.available < 10% 根文件系统可用空间不足
nodefs.inodesFree < 5% 根文件系统 inode 不足
imagefs.available < 15% 镜像文件系统可用空间不足

驱逐策略

// pkg/kubelet/eviction/eviction_manager.go

// 驱逐 Pod 的优先级顺序
func (m *managerImpl) rankPodsByPriority(pods []*v1.Pod) []*v1.Pod {
    // 1. BestEffort Pod(无资源请求和限制)
    // 2. Burstable Pod(资源使用超过请求值)
    // 3. Burstable Pod(资源使用未超过请求值)
    // 4. Guaranteed Pod(资源请求等于限制)
    
    return sortedPods
}

关键代码片段

Kubelet 启动流程

// cmd/kubelet/app/server.go

func RunKubelet(kubeServer *options.KubeletServer) error {
    // 1. 创建 Kubelet 实例
    kubelet, err := createAndInitKubelet(kubeServer)
    if err != nil {
        return err
    }
    
    // 2. 启动 Kubelet
    kubelet.Run()
    
    return nil
}

// pkg/kubelet/kubelet.go

func (kl *Kubelet) Run() {
    // 1. 启动 VolumeManager
    go kl.volumeManager.Run(kl.sourcesReady, wait.NeverStop)
    
    // 2. 启动 StatusManager
    kl.statusManager.Start()
    
    // 3. 启动 ProbeManager
    kl.probeManager.Start()
    
    // 4. 启动 PLEG
    kl.pleg.Start()
    
    // 5. 启动 SyncLoop(主循环)
    kl.syncLoop(updates, kl)
}

最佳实践

1. 合理设置资源请求和限制

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "200m"
    memory: "256Mi"

2. 配置健康检查

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

3. 使用 Init Containers

initContainers:
- name: init-db
  image: busybox
  command: ['sh', '-c', 'until nc -z db 5432; do sleep 1; done']

Kubernetes-04-Kubelet-时序图

时序图概述

本文档提供 Kubelet 核心场景的时序图:

  1. Pod 启动完整流程:从接收到 Pod 到容器运行
  2. 容器重启流程:Liveness Probe 失败触发重启
  3. Volume 挂载流程:CSI Volume 的完整挂载过程

场景 1:Pod 启动完整流程

sequenceDiagram
    autonumber
    participant API as API Server
    participant KL as Kubelet
    participant PM as PodManager
    participant PW as PodWorker
    participant VM as VolumeManager
    participant CRI as Container Runtime
    participant CNI as CNI Plugin
    
    Note over API,CNI: 场景:Scheduler  Pod 调度到本节点
    
    API->>KL: Watch Event: Pod/nginx<br/>(Pod.Spec.NodeName = "node-1")
    KL->>PM: AddPod(pod)
    PM->>PM: 更新 Pod 缓存
    
    KL->>PW: dispatchWork(pod, SyncPod)
    PW->>PW: 加入 Pod 的更新队列
    
    Note over PW: PodWorker 处理队列
    
    PW->>PW: syncPod(pod)
    
    Note over PW,VM: 步骤 1:创建 Pod 目录
    
    PW->>PW: makePodDataDirs()<br/>- /var/lib/kubelet/pods/{pod-uid}/<br/>- /var/lib/kubelet/pods/{pod-uid}/volumes/<br/>- /var/lib/kubelet/pods/{pod-uid}/plugins/
    
    Note over PW,VM: 步骤 2:挂载 Volume
    
    PW->>VM: WaitForAttachAndMount(pod)
    VM->>VM: 检查需要挂载的 Volume<br/>(从 Pod.Spec.Volumes 提取)
    
    loop 遍历所有 Volume
        VM->>VM: AttachVolume() - CSI ControllerPublishVolume
        VM->>VM: MountVolume() - CSI NodeStageVolume + NodePublishVolume
    end
    
    VM-->>PW: 所有 Volume 挂载完成
    
    Note over PW,CRI: 步骤 3:拉取镜像
    
    PW->>CRI: PullImage(image="nginx:1.25")
    CRI->>CRI: 从镜像仓库拉取镜像
    CRI-->>PW: Image ID
    
    Note over PW,CNI: 步骤 4:创建 Pod Sandbox
    
    PW->>CRI: RunPodSandbox(podConfig)
    CRI->>CRI: 创建 Pause 容器<br/>(持有网络和 IPC 命名空间)
    CRI-->>PW: Sandbox ID
    
    PW->>CNI: SetUpPod(pod)
    CNI->>CNI: 调用 CNI 插件(ADD 操作)<br/>- 创建 veth pair<br/>- 分配 IP 地址<br/>- 配置路由
    CNI-->>PW: Pod IP = 10.244.1.5
    
    Note over PW,CRI: 步骤 5:启动 Init Containers(串行)
    
    loop 遍历 Init Containers
        PW->>CRI: CreateContainer(sandboxID, initContainerConfig)
        CRI-->>PW: Container ID
        
        PW->>CRI: StartContainer(containerID)
        CRI->>CRI: 启动容器
        CRI-->>PW: Success
        
        PW->>PW: 等待容器完成(退出码 = 0
    end
    
    Note over PW,CRI: 步骤 6:启动 App Containers(并行)
    
    par 并行启动
        PW->>CRI: CreateContainer(sandboxID, container1Config)
        CRI-->>PW: Container-1 ID
        PW->>CRI: StartContainer(container1ID)
    and
        PW->>CRI: CreateContainer(sandboxID, container2Config)
        CRI-->>PW: Container-2 ID
        PW->>CRI: StartContainer(container2ID)
    end
    
    Note over PW: 步骤 7:启动健康检查
    
    PW->>PW: startProbes(pod)<br/>- Liveness Probe<br/>- Readiness Probe<br/>- Startup Probe
    
    Note over PW,API: 步骤 8:更新 Pod Status
    
    PW->>API: PATCH /api/v1/pods/nginx/status<br/>{<br/>  phase: "Running",<br/>  podIP: "10.244.1.5",<br/>  conditions: [{type: "Ready", status: "True"}]<br/>}
    API-->>PW: 200 OK

要点说明

1. Pod 目录结构

/var/lib/kubelet/pods/{pod-uid}/
├── volumes/              # Volume 挂载点
│   ├── kubernetes.io~empty-dir/
│   ├── kubernetes.io~configmap/
│   └── kubernetes.io~csi/
├── plugins/              # 插件目录
└── containers/           # 容器日志

2. Init Containers 特点

  • 串行执行:必须按顺序成功
  • 共享 Volume:与 App Containers 共享 Volume
  • 常见用途:等待依赖服务、初始化数据库

3. 网络配置时机

  • Pod Sandbox 创建后立即配置网络
  • 所有容器共享 Sandbox 的网络命名空间

场景 2:容器重启流程(Liveness Probe 失败)

sequenceDiagram
    autonumber
    participant PROBE as ProbeManager
    participant KL as Kubelet
    participant PW as PodWorker
    participant CRI as Container Runtime
    participant API as API Server
    
    Note over PROBE,API: 场景:容器死锁,Liveness Probe 失败
    
    loop 每 10s 执行一次 Liveness Probe
        PROBE->>PROBE: execProbe(container)<br/>HTTP GET /healthz → 超时
        PROBE->>PROBE: failureCount++
    end
    
    PROBE->>PROBE: failureCount >= FailureThreshold (3)
    PROBE->>PROBE: 标记容器为 Unhealthy
    
    PROBE->>KL: livenessManager.Updates() <- Failure
    KL->>KL: syncLoop 收到 Probe Update 事件
    
    KL->>PW: dispatchWork(pod, SyncPod)
    
    Note over PW,CRI: PodWorker 处理容器重启
    
    PW->>PW: syncPod(pod)
    PW->>PW: 检测到容器 Unhealthy
    
    PW->>CRI: StopContainer(containerID, timeout=30s)
    CRI->>CRI: 1. 发送 SIGTERM 信号<br/>2. 等待 30s<br/>3. 发送 SIGKILL 信号
    CRI-->>PW: Success
    
    PW->>CRI: RemoveContainer(containerID)
    CRI->>CRI: 删除容器
    CRI-->>PW: Success
    
    Note over PW: RestartCount++
    
    PW->>CRI: CreateContainer(sandboxID, containerConfig)
    CRI-->>PW: New Container ID
    
    PW->>CRI: StartContainer(newContainerID)
    CRI->>CRI: 启动新容器
    CRI-->>PW: Success
    
    Note over PW,API: 更新容器状态
    
    PW->>API: PATCH /api/v1/pods/nginx/status<br/>{<br/>  containerStatuses: [{<br/>    restartCount: 1,<br/>    lastState: {terminated: {exitCode: 137, reason: "Error"}},<br/>    state: {running: {startedAt: "2025-10-04T10:00:00Z"}}<br/>  }]<br/>}
    API-->>PW: 200 OK
    
    Note over PROBE: 重新启动 Liveness Probe
    
    PROBE->>PROBE: 重置 successCount 和 failureCount
    PROBE->>PROBE: 等待 initialDelaySeconds 后开始探测

要点说明

1. 容器终止流程

SIGTERM(优雅关闭)
   ↓ 等待 terminationGracePeriodSeconds(默认 30s)
SIGKILL(强制终止)

2. RestartPolicy 策略

  • Always:总是重启(默认)
  • OnFailure:退出码非 0 时重启
  • Never:不重启

3. CrashLoopBackOff

  • 容器启动后 10s 内退出 → 判定为 Crash
  • 退避时间:10s → 20s → 40s → … → 5min(最大)

场景 3:Volume 挂载流程(CSI)

sequenceDiagram
    autonumber
    participant KL as Kubelet
    participant VM as VolumeManager
    participant CSI as CSI Driver
    participant CLOUD as Cloud Storage
    
    Note over KL,CLOUD: 场景:挂载 AWS EBS Volume
    
    KL->>VM: WaitForAttachAndMount(pod)
    VM->>VM: 检查 Pod.Spec.Volumes
    
    Note over VM,CLOUD: 步骤 1AttachController 端)
    
    VM->>CSI: ControllerPublishVolume(volumeID, nodeID)
    CSI->>CLOUD: AWS API: AttachVolume<br/>(VolumeID=vol-123, InstanceID=i-456)
    CLOUD-->>CSI: Device Path = /dev/xvdf
    CSI-->>VM: PublishContext = {devicePath: "/dev/xvdf"}
    
    Note over VM: 等待设备出现
    
    VM->>VM: 轮询检查 /dev/xvdf 是否存在
    
    Note over VM,CSI: 步骤 2StageNode  - 格式化并挂载到临时目录)
    
    VM->>CSI: NodeStageVolume(volumeID, stagingPath)
    CSI->>CSI: 检查文件系统<br/>blkid /dev/xvdf
    
    alt 文件系统不存在
        CSI->>CSI: 格式化<br/>mkfs.ext4 /dev/xvdf
    end
    
    CSI->>CSI: 挂载到临时目录<br/>mount /dev/xvdf /var/lib/kubelet/plugins/.../staging/{volume-id}
    CSI-->>VM: Success
    
    Note over VM,CSI: 步骤 3PublishNode  - Bind Mount  Pod 目录)
    
    VM->>CSI: NodePublishVolume(volumeID, targetPath)
    CSI->>CSI: Bind Mount<br/>mount --bind \<br/>  /var/lib/kubelet/plugins/.../staging/{volume-id} \<br/>  /var/lib/kubelet/pods/{pod-uid}/volumes/{volume-name}
    CSI-->>VM: Success
    
    VM-->>KL: Volume 挂载完成

要点说明

1. Volume 生命周期

Provision(创建 PV)
Attach(挂载到节点)
Stage(格式化并挂载到临时目录)
Publish(Bind Mount 到 Pod 目录)
Unpublish(卸载 Bind Mount)
Unstage(卸载临时目录)
Detach(从节点卸载)
Delete(删除 PV)

2. Stage 和 Publish 的区别

  • Stage:挂载到节点级别的临时目录(一次性,多个 Pod 共享)
  • Publish:Bind Mount 到每个 Pod 的目录(每个 Pod 一次)

3. 为什么需要 Stage?

  • 避免重复格式化(多个 Pod 使用同一 Volume)
  • 提高挂载效率(仅一次 mkfs 和 mount)

性能指标

关键指标

指标 类型 说明
kubelet_pod_start_duration_seconds Histogram Pod 启动延迟(从接收到容器运行)
kubelet_pod_worker_duration_seconds Histogram PodWorker 处理时间
kubelet_pleg_relist_duration_seconds Histogram PLEG 轮询耗时
kubelet_cri_operations_duration_seconds Histogram CRI 操作延迟
kubelet_volume_mount_duration_seconds Histogram Volume 挂载延迟
kubelet_container_restart_total Counter 容器重启次数

Kubernetes-04-Kubelet-数据结构

数据结构概述

Kubelet 的核心数据结构围绕 Pod 和容器状态管理 设计:

  1. PodStatus:Pod 的状态信息
  2. ContainerStatus:容器的状态信息
  3. PodWorker:Pod 管理的工作单元
  4. VolumeManager:存储卷管理器

核心数据结构 UML 图

classDiagram
    class Kubelet {
        +PodManager podManager
        +StatusManager statusManager
        +VolumeManager volumeManager
        +ProbeManager probeManager
        +PLEG pleg
        +syncLoop()
    }
    
    class PodStatus {
        +PodIP string
        +PodIPs []PodIP
        +Phase PodPhase
        +Conditions []PodCondition
        +ContainerStatuses []ContainerStatus
        +InitContainerStatuses []ContainerStatus
    }
    
    class ContainerStatus {
        +Name string
        +State ContainerState
        +Ready bool
        +RestartCount int32
        +Image string
        +ImageID string
        +ContainerID string
    }
    
    class ContainerState {
        +Waiting *ContainerStateWaiting
        +Running *ContainerStateRunning
        +Terminated *ContainerStateTerminated
    }
    
    class PodWorker {
        +podUID UID
        +workType WorkType
        +syncPodFn syncPodFnType
        +podUpdates chan workUpdate
    }
    
    Kubelet "1" --> "*" PodWorker : manages
    Kubelet "1" --> "1" StatusManager : uses
    PodStatus "1" --> "*" ContainerStatus : contains
    ContainerStatus "1" --> "1" ContainerState : has

数据结构详解

1. PodStatus(Pod 状态)

// staging/src/k8s.io/api/core/v1/types.go

type PodStatus struct {
    // Phase Pod 生命周期阶段
    Phase PodPhase  // Pending / Running / Succeeded / Failed / Unknown
    
    // Conditions Pod 状态条件
    Conditions []PodCondition
    
    // Message 人类可读的状态信息
    Message string
    
    // Reason 状态原因(机器可读)
    Reason string
    
    // NominatedNodeName 抢占调度的节点名称
    NominatedNodeName string
    
    // HostIP 节点 IP
    HostIP string
    
    // PodIP Pod IP(IPv4)
    PodIP string
    
    // PodIPs Pod IP 列表(支持 IPv4 + IPv6)
    PodIPs []PodIP
    
    // StartTime Pod 启动时间
    StartTime *metav1.Time
    
    // InitContainerStatuses Init 容器状态列表
    InitContainerStatuses []ContainerStatus
    
    // ContainerStatuses 应用容器状态列表
    ContainerStatuses []ContainerStatus
    
    // QOSClass QoS 等级(Guaranteed / Burstable / BestEffort)
    QOSClass PodQOSClass
    
    // EphemeralContainerStatuses 临时容器状态列表
    EphemeralContainerStatuses []ContainerStatus
}

字段说明:

字段 类型 说明
Phase PodPhase Pod 生命周期阶段(Pending/Running/Succeeded/Failed)
Conditions []PodCondition Pod 状态条件(PodScheduled、Initialized、Ready)
ContainerStatuses []ContainerStatus 容器状态列表
PodIP string Pod 的 IP 地址
StartTime *Time Pod 启动时间

2. PodCondition(Pod 状态条件)

type PodCondition struct {
    // Type 条件类型
    Type PodConditionType  // PodScheduled / Initialized / Ready / ContainersReady
    
    // Status 条件状态
    Status ConditionStatus  // True / False / Unknown
    
    // LastProbeTime 最后探测时间
    LastProbeTime metav1.Time
    
    // LastTransitionTime 最后状态变化时间
    LastTransitionTime metav1.Time
    
    // Reason 状态原因
    Reason string
    
    // Message 详细信息
    Message string
}

常见 Condition 类型:

Type 说明 True 表示
PodScheduled Pod 是否已调度 已分配到节点
Initialized Init 容器是否完成 所有 Init 容器成功
ContainersReady 容器是否就绪 所有容器 Ready=true
Ready Pod 是否就绪 ContainersReady=true 且 Readiness Gates 通过

3. ContainerStatus(容器状态)

type ContainerStatus struct {
    // Name 容器名称
    Name string
    
    // State 容器状态(Waiting / Running / Terminated)
    State ContainerState
    
    // LastTerminationState 上次终止状态
    LastTerminationState ContainerState
    
    // Ready 容器是否就绪
    Ready bool
    
    // RestartCount 重启次数
    RestartCount int32
    
    // Image 镜像名称
    Image string
    
    // ImageID 镜像 ID(sha256:...)
    ImageID string
    
    // ContainerID 容器 ID(runtime://container-id)
    ContainerID string
    
    // Started 容器是否已启动
    Started *bool
}

4. ContainerState(容器状态)

type ContainerState struct {
    // Waiting 等待状态(容器尚未启动)
    Waiting *ContainerStateWaiting
    
    // Running 运行状态
    Running *ContainerStateRunning
    
    // Terminated 终止状态
    Terminated *ContainerStateTerminated
}

// ContainerStateWaiting 等待状态
type ContainerStateWaiting struct {
    Reason  string  // 等待原因(ContainerCreating / CrashLoopBackOff / ImagePullBackOff)
    Message string  // 详细信息
}

// ContainerStateRunning 运行状态
type ContainerStateRunning struct {
    StartedAt metav1.Time  // 启动时间
}

// ContainerStateTerminated 终止状态
type ContainerStateTerminated struct {
    ExitCode    int32        // 退出码
    Signal      int32        // 信号(如 SIGKILL=9)
    Reason      string       // 终止原因(Completed / Error / OOMKilled)
    Message     string       // 详细信息
    StartedAt   metav1.Time  // 启动时间
    FinishedAt  metav1.Time  // 终止时间
    ContainerID string       // 容器 ID
}

状态转换:

Waiting → Running → Terminated
   ↑                    ↓
   └────────────────────┘
       (容器重启)

5. PodWorker(Pod 工作单元)

// pkg/kubelet/pod_workers.go

type podWorkers struct {
    // podUpdates 每个 Pod 一个更新通道
    podUpdates map[types.UID]chan workUpdate
    
    // podSyncStatuses Pod 同步状态
    podSyncStatuses map[types.UID]*podSyncStatus
    
    // 工作函数
    syncPodFn       syncPodFnType
    syncTerminatingPodFn syncTerminatingPodFnType
}

type workUpdate struct {
    WorkType   WorkType      // SyncPod / TerminatingPod / TerminatedPod
    Options    UpdateOptions // Pod 信息、更新类型
}

type podSyncStatus struct {
    syncedAt    time.Time   // 最后同步时间
    working     bool        // 是否正在处理
    terminating bool        // 是否正在终止
    terminated  bool        // 是否已终止
}

6. VolumeManager(存储卷管理器)

// pkg/kubelet/volumemanager/volume_manager.go

type volumeManager struct {
    // desiredStateOfWorld 期望状态(应该挂载的 Volume)
    desiredStateOfWorld cache.DesiredStateOfWorld
    
    // actualStateOfWorld 实际状态(已挂载的 Volume)
    actualStateOfWorld cache.ActualStateOfWorld
    
    // operationExecutor Volume 操作执行器
    operationExecutor operationexecutor.OperationExecutor
    
    // reconciler 协调器(将实际状态协调为期望状态)
    reconciler reconciler.Reconciler
}

type AttachedVolume struct {
    VolumeName          v1.UniqueVolumeName  // Volume 名称
    VolumeSpec          *volume.Spec         // Volume 规格
    NodeName            types.NodeName       // 节点名称
    MountedByNode       bool                 // 是否已挂载到节点
    DevicePath          string               // 设备路径(如 /dev/xvdf)
    DeviceMountPath     string               // 挂载路径
}

关键算法与机制

1. PLEG(Pod Lifecycle Event Generator)

作用: 定期轮询容器运行时,检测容器状态变化

// pkg/kubelet/pleg/generic.go

type GenericPLEG struct {
    runtime       kubecontainer.Runtime
    eventChannel  chan *PodLifecycleEvent
    relistPeriod  time.Duration  // 轮询周期(默认 1s)
}

type PodLifecycleEvent struct {
    ID   types.UID                  // Pod UID
    Type PodLifeCycleEventType      // ContainerStarted / ContainerDied / ContainerRemoved
    Data interface{}                // 事件数据(如容器 ID)
}

// relist 轮询容器运行时
func (g *GenericPLEG) relist() {
    // 1. 调用 CRI ListPodSandbox + ListContainers
    podList, err := g.runtime.GetPods(true)
    
    // 2. 比较当前状态与上次状态
    events := computeEvents(g.podRecords, podList)
    
    // 3. 发送事件到 eventChannel
    for _, e := range events {
        g.eventChannel <- e
    }
    
    // 4. 更新缓存
    g.podRecords = podList
}

2. Probe 探针状态机

// pkg/kubelet/prober/prober.go

type probeResultManager struct {
    // 探针结果缓存
    cache map[kubecontainer.ContainerID]proberesults.Result
    
    // 更新通道
    updates chan proberesults.Update
}

type Result int

const (
    Unknown Result = iota  // 未知
    Success                // 成功
    Failure                // 失败
)

// 状态转换逻辑
func (prober *prober) runProbe(probeType, pod, container) {
    result := prober.exec(probe)
    
    if result == Success {
        prober.successCount++
        if prober.successCount >= probe.SuccessThreshold {
            prober.resultManager.Set(containerID, Success)
        }
    } else {
        prober.failureCount++
        if prober.failureCount >= probe.FailureThreshold {
            prober.resultManager.Set(containerID, Failure)
        }
    }
}

性能与容量考虑

1. PodWorker 并发

并发模型:

  • 每个 Pod 一个 Worker Goroutine(串行处理)
  • 不同 Pod 间并行处理

内存占用:

  • 每个 PodWorker:约 10 KB(Goroutine + channel)
  • 100 个 Pod ≈ 1 MB

2. PLEG 轮询开销

CPU 占用:

  • 轮询周期:默认 1s
  • 每次轮询:调用 CRI ListPodSandbox + ListContainers(约 10-50ms)

优化建议:

  • 使用 Evented PLEG(基于 CRI Event 流,无需轮询)
  • 适用于 containerd 1.7+

3. StatusManager 上报频率

默认配置:

  • 每 10s 上报一次节点状态
  • Pod 状态变化时立即上报

优化建议:

  • 减少上报频率(适用于大规模集群)
  • 批量上报 Pod 状态