惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

lznauy的技术小屋

Kubernetes 存储 PV/PVC/CSI 体系 Kubernetes API Server—请求处理链路 Kubernetes 调度器—Pod 如何被分配到节点 Kubernetes Controller 调谐循环的实现原理 Kubernetes 核心资源模型的设计哲学 Gin 路由为什么快:Radix Tree 源码剖析 前路茫茫 NekoCode 深度解析 用AI开发新主题仅需一天 AIAgent开发杂谈 CodeAgent的开发比我想象的难的多 nix语法初入门 记录一次家里老人受骗 nixos美化shell及常用软件安装 皖南川藏线之旅 nixos配置kitty以及桌面美化 nixos安装niri和noctalia nixos-shell及配置优化 nixos-flake和home-manager nixos折腾之旅 claude源码泄漏事件及简单剖析 春季赏花之旅 曲折的离职过程 年味越来越淡了 城市里没有爱情? 日常碎记(三) 利用DeepSeek分析你的隐藏天赋 SELinux技术介绍 我们的认知可能被裹挟了 日常碎记(二)
Kubelet Pod 生命周期管理
lznauy · 2026-07-25 · via lznauy的技术小屋

阅读时间:10 分钟 4999 字 AI 协作

此文章可能有部分 AI 辅助生成内容,如果您介意可以不再继续阅读。

基于 Kubernetes v1.37 源码(pkg/kubelet/

一、Kubelet 的职责 @

Kubelet 是运行在每个节点上的节点代理,其核心职责是管理 Pod 的生命周期

调度器将 Pod 调度到节点
       ↓
kubelet 监听到 Pod 分配
       ↓
创建容器(通过 CRI)
       ↓
监控容器状态
       ↓
上报状态到 API Server
       ↓
处理删除/更新/驱逐

二、Kubelet 架构全景 @

┌────────────────────────────────────────────────────────────┐
│                          Kubelet                           │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                    Pod Sync Loop                     │  │
│  │ LIST/WATCH API Server → 对比期望与实际状态 → 执行     │  │
│  └──────────────────────────────────────────────────────┘  │
│                             ↓                              │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                Container Manager (CM)                │  │
│  │  ┌───────────┐  ┌───────────┐  ┌──────────────────┐  │  │
│  │  │ CRI Client │  │  cgroup   │  │  Device Plugin   │  │  │
│  │  │ 容器运行时  │  │ 资源隔离   │  │   设备管理       │  │  │
│  │  └───────────┘  └───────────┘  └──────────────────┘  │  │
│  └──────────────────────────────────────────────────────┘  │
│                             ↓                              │
│  ┌──────────────────────────────────────────────────────┐  │
│  │          CRI (Container Runtime Interface)           │  │
│  │           containerd / CRI-O / cri-dockerd           │  │
│  └──────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────┘

三、Pod Sync Loop(Pod 同步循环) @

Kubelet 的核心是一个持续运行的 Pod 同步循环,它监听多个通道来触发同步:

// pkg/kubelet/kubelet.go
func (kl *Kubelet) syncLoop(
    ctx context.Context,
    updates <-chan kubetypes.PodUpdate,  // Pod 更新通道
    handler SyncHandler,                  // 同步处理器
) {
    for {
        // 检查运行时错误,如有则指数退避
        if err := kl.runtimeState.runtimeErrors(); err != nil {
            time.Sleep(duration)
            continue
        }
        
        // 监听多个通道,哪个有数据就处理哪个
        kl.syncLoopIteration(ctx, updates, handler,
            syncTicker.C, housekeepingTicker.C, plegCh)
    }
}

3.1 syncLoopIteration @

// pkg/kubelet/kubelet.go
func (kl *Kubelet) syncLoopIteration(
    ctx context.Context,
    configCh <-chan kubetypes.PodUpdate,  // 配置变更通道
    handler SyncHandler,
    syncCh <-chan time.Time,              // 定期同步通道
    housekeepingCh <-chan time.Time,      // 清理通道
    plegCh <-chan *pleg.PodLifecycleEvent, // Pod 生命周期事件通道
) bool {
    select {
    case u, open := <-configCh:
        // 处理 Pod 配置变更(添加/更新/删除/调和)
        // PodUpdate 只有三个字段:Pods []*v1.Pod、Op、Source
        switch u.Op {
        case kubetypes.ADD:
            handler.HandlePodAdditions(ctx, u.Pods)
        case kubetypes.UPDATE:
            handler.HandlePodUpdates(ctx, u.Pods)
        case kubetypes.REMOVE:
            handler.HandlePodRemoves(ctx, u.Pods)
        case kubetypes.RECONCILE:
            handler.HandlePodReconcile(ctx, u.Pods)
        }
    case <-syncCh:
        // 定期同步所有待同步的 Pod
        podsToSync := kl.getPodsToSync()
        handler.HandlePodSyncs(ctx, podsToSync)
    case <-housekeepingCh:
        // 清理已终止的 Pod、孤立的 Volume 和 Pod 目录等
        handler.HandlePodCleanups(ctx)
    case e := <-plegCh:
        // Pod 生命周期事件(容器启动/停止),同步对应的 Pod
        if pod, ok := kl.podManager.GetPodByUID(e.ID); ok {
            handler.HandlePodSyncs(ctx, []*v1.Pod{pod})
        }
    }
}

3.2 Pod Workers(异步执行) @

Kubelet 没有 syncPod 方法,而是通过 podWorkers 异步管理每个 Pod:

// pkg/kubelet/pod_workers.go
type podWorkers struct {
    podLock    sync.Mutex
    podsSynced bool  // 所有工作中的 Pod 至少同步过一次后置为 true

    // 每个 Pod 一个更新通道,向通道发信号通知对应 goroutine 消费更新
    podUpdates map[types.UID]chan struct{}
    // 按 UID 跟踪 Pod 的同步状态(syncing / terminating / terminated / evicted)
    podSyncStatuses map[types.UID]*podSyncStatus

    workQueue queue.WorkQueue
    podSyncer podSyncer  // 实际执行同步的接口,由 Kubelet 实现
}

// 更新 Pod(异步执行,不阻塞调用方)
func (p *podWorkers) UpdatePod(ctx context.Context, options UpdatePodOptions) {
    p.podLock.Lock()
    // 将更新写入该 Pod 的 podSyncStatus.pendingUpdate,
    // 并向 podUpdates[uid] 通道发信号(goroutine 不存在则创建)
    p.podLock.Unlock()
}

// 每个 Pod 的 goroutine 串行消费更新,按 kubetypes.SyncPodType 分类:
//   SyncPodCreate / SyncPodUpdate / SyncPodSync / SyncPodKill
func (p *podWorkers) podWorkerLoop(uid types.UID) {
    for range podUpdatesCh {
        // 根据 podSyncStatus 状态机,调用 podSyncer 对应方法:
        switch {
        case 需要创建或更新:
            p.podSyncer.SyncPod(ctx, updateType, pod, mirrorPod, podStatus)
        case 需要终止:
            p.podSyncer.SyncTerminatingPod(...)  // 停止容器
        case 已终止:
            p.podSyncer.SyncTerminatedPod(...)   // 清理资源
        }
    }
}

四、Pod 生命周期 @

4.1 Pod 状态机 @

Pending → Running → Succeeded/Failed(异常时为 Unknown)
状态 含义
Pending Pod 已被接受,但容器尚未创建
Running 所有容器已创建,至少一个在运行
Succeeded 所有容器成功终止
Failed 所有容器已终止,至少一个失败
Unknown 无法获取 Pod 状态

注意:Scheduled 不是 Pod Phase。Phase 只有上表五种;“已调度到节点"是通过 PodScheduled Condition 表示的(staging/src/k8s.io/api/core/v1/types.go)。

4.2 容器状态机 @

Waiting → Running → Terminated

kubelet 内部运行时层与 API 层对容器状态的表示不同:

// pkg/kubelet/container/runtime.go —— kubelet 内部运行时层
type State string

const (
    ContainerStateCreated State = "created" // 已创建但未启动
    ContainerStateRunning State = "running"
    ContainerStateExited  State = "exited"
    ContainerStateUnknown State = "unknown" // restarting、paused、dead 等
)

// staging/src/k8s.io/api/core/v1/types.go —— API 层
// Waiting/Running/Terminated 是 v1.ContainerState 结构体的字段(互斥指针):
type ContainerState struct {
    Waiting    *ContainerStateWaiting
    Running    *ContainerStateRunning
    Terminated *ContainerStateTerminated
}

五、CRI(容器运行时接口) @

Kubelet 通过 CRI 与容器运行时交互,解耦具体实现:

// k8s.io/cri-api/pkg/apis/runtime/v1/api.proto
service RuntimeService {
    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse);
    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse);
    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse);
    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);
    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse);
    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse);
    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse);
    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse);
    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse);
    rpc Attach(AttachRequest) returns (AttachResponse);
    rpc PortForward(PortForwardRequest) returns (PortForwardResponse);
    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse);
}

5.1 容器创建流程 @

// pkg/kubelet/kuberuntime/kuberuntime_container.go
// Pod Sandbox(Pause 容器)已在 SyncPod 流程中先行创建,这里不再创建
func (m *kubeGenericRuntimeManager) startContainer(
    ctx context.Context,
    podSandboxID string,
    podSandboxConfig *runtimeapi.PodSandboxConfig,
    spec *startSpec,
    pod *v1.Pod,
    podStatus *kubecontainer.PodStatus,
    pullSecrets []v1.Secret,
    podIP string, podIPs []string,
    imageVolumes kubecontainer.ImageVolumes,
) (string, error) {
    // 1. 确保镜像存在(不存在则拉取)
    imageRef, msg, err := m.imagePuller.EnsureImageExists(
        ctx, ref, pod, container.Image, pullSecrets, podSandboxConfig, ...)
    
    // 2. 生成容器配置并创建容器
    containerConfig, cleanupAction, err := m.generateContainerConfig(...)
    containerID, err := m.runtimeService.CreateContainer(
        ctx, podSandboxID, containerConfig, podSandboxConfig)
    
    // 3. 启动容器
    err = m.runtimeService.StartContainer(ctx, containerID)
    
    // 4. 执行 PostStart 钩子(失败则杀死容器)
    if container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
        m.runner.Run(ctx, kubeContainerID, pod, container, container.Lifecycle.PostStart)
    }
    
    return "", nil
}

5.2 支持的运行时 @

运行时 说明
containerd 默认推荐,CNCF 项目
CRI-O 专为 Kubernetes 设计
Docker 通过 cri-dockerd 适配

六、Container Manager(CM) @

CM 负责节点上所有容器的资源管理和生命周期:

// pkg/kubelet/cm/container_manager.go
type ContainerManager interface {
    Start(context.Context, *v1.Node, ActivePodsFunc, GetNodeFunc,
        config.SourcesReady, status.PodStatusProvider,
        internalapi.RuntimeService, bool) error
    SystemCgroupsLimit() v1.ResourceList
    GetNodeConfig() NodeConfig
    GetResources(ctx context.Context, pod *v1.Pod, container *v1.Container) (*kubecontainer.RunContainerOptions, error)
    UpdateQOSCgroups(logger klog.Logger) error
}

6.1 cgroup 资源隔离 @

// pkg/kubelet/cm/cgroup_manager_linux.go
// 公共部分抽为 cgroupCommon,cgroup v1/v2 各有具体实现
type cgroupCommon struct {
    subsystems *CgroupSubsystems // 节点上已挂载的 cgroup 子系统
    useSystemd bool              // 是否使用 systemd cgroup 驱动
}

var _ CgroupManager = &cgroupV1impl{}
var _ CgroupManager = &cgroupV2impl{}

func (m *cgroupV1impl) Create(logger klog.Logger, config *CgroupConfig) error {
    // 创建 cgroup 目录
    // 设置资源限制:cpu.shares、memory.limit_in_bytes 等
    // (v2 实现对应 cpu.weight、memory.max 等)
    return nil
}

6.2 CPU 管理策略 @

策略 说明
none 默认,共享 CPU
static 独占 CPU 核(Guaranteed QoS)
// pkg/kubelet/cm/cpumanager/policy_static.go
func (p *staticPolicy) Allocate(logger klog.Logger, s state.State,
    pod *v1.Pod, container *v1.Container, operation lifecycle.Operation) error {
    // 为 Guaranteed Pod 的容器分配独占 CPU 核(目前仅支持 Add 操作)
    // 另有 AllocatePod(logger, s, pod, operation) 处理 Pod 级资源分配
    return p.allocateForAdd(logger, s, pod, container)
}

6.3 设备管理 @

// pkg/kubelet/cm/devicemanager/types.go
type Manager interface {
    Start(logger klog.Logger, activePods ActivePodsFunc, sourcesReady config.SourcesReady, ...) error
    Allocate(ctx context.Context, pod *v1.Pod, container *v1.Container, operation lifecycle.Operation) error
    UpdatePluginResources(node *schedulerframework.NodeInfo, attrs *lifecycle.PodAdmitAttributes) error
    GetDevices(podUID, containerName string) ResourceDeviceInstances
}

七、Pod 创建完整流程 @

1. 调度器将 Pod 调度到节点
       ↓
2. kubelet 监听到 Pod 分配(Watch API Server)
       ↓
3. Pod Sync Loop 检测到新的 Pod
       ↓
4. 创建 Pod 数据目录
   /var/lib/kubelet/pods/{podUID}/
       ↓
5. 创建 Pod Sandbox(Pause 容器)
   - 创建网络命名空间
   - 配置网络(CNI)
       ↓
6. 拉取镜像
   - 检查本地缓存
   - 从 Registry 拉取
       ↓
7. 创建 Init 容器(如果有)
   - 按顺序执行
   - 必须全部成功
       ↓
8. 创建主容器
   - 创建容器配置
   - 设置资源限制(cgroup)
   - 设置环境变量
   - 挂载 Volume
       ↓
9. 启动容器
   - 执行 PostStart 钩子
   - 启动 Liveness/Readiness 探针
       ↓
10. 更新 Pod 状态
    - 上报到 API Server
    - 触发相关事件

八、健康检查(Probes) @

Kubelet 负责执行三种健康检查:

8.1 探针类型 @

探针 作用 失败后果
LivenessProbe 容器是否存活 重启容器
ReadinessProbe 容器是否就绪 从 Service 摘除
StartupProbe 容器是否启动完成 超过 failureThreshold 后杀死并重启容器;成功之前会禁用 Liveness/Readiness 探针

8.2 探针实现 @

// pkg/kubelet/prober/prober_manager.go(probeType 是小写未导出类型)
type probeType int

const (
    liveness probeType = iota
    readiness
    startup
)

// pkg/kubelet/prober/prober.go
func (pb *prober) probe(ctx context.Context, probeType probeType, pod *v1.Pod,
    status v1.PodStatus, container v1.Container, containerID kubecontainer.ContainerID,
) (results.Result, error) {
    // v1.Probe 内嵌 ProbeHandler(Exec/HTTPGet/TCPSocket/GRPC 指针字段),
    // 没有 Type 字段,通过判断哪个字段非 nil 来选择探测方式
    switch probeSpec := getProbeSpec(container, probeType); {
    case probeSpec.Exec != nil:
        return pb.exec.Probe(...)
    case probeSpec.HTTPGet != nil:
        return pb.http.Probe(...)
    case probeSpec.TCPSocket != nil:
        return pb.tcp.Probe(...)
    case probeSpec.GRPC != nil:
        return pb.grpc.Probe(...)
    }
}

8.3 探针执行循环 @

// pkg/kubelet/prober/worker.go
func (w *worker) run(ctx context.Context) {
    // 用探针配置的 PeriodSeconds 构造 ticker(worker 没有 ProbeInterval 字段)
    probeTicker := time.NewTicker(time.Duration(w.spec.PeriodSeconds) * time.Second)

    for w.doProbe(ctx) {
        select {
        case <-w.stopCh:
            return
        case <-probeTicker.C:
            // 到点继续下一次探测
        }
    }
}

// doProbe 执行一次探测,只把结果写入 resultsManager,从不直接杀死容器
func (w *worker) doProbe(ctx context.Context) (keepGoing bool) {
    result, err := w.probeManager.prober.probe(
        ctx, w.probeType, w.pod, status, w.container, w.containerID)
    w.resultsManager.Set(w.containerID, result, w.pod)
    // 结果由 syncLoopIteration 消费 kl.livenessManager.Updates() 等通道,
    // 触发 handler.HandlePodSyncs,在后续 SyncPod 流程中间接杀死并重建失败的容器
    return true
}

九、Volume 管理 @

9.1 Volume 挂载流程 @

// pkg/kubelet/volumemanager/volume_manager.go
func (vm *volumeManager) WaitForAttachAndMount(pod *v1.Pod) error {
    for _, volume := range pod.Spec.Volumes {
        // 1. 等待 Volume 由 Controller 完成 Attach(如 EBS)
        err := volumePlugin.WaitForAttach(volume, pod)
        
        // 2. 挂载 Volume 到节点
        err = volumePlugin.MountDevice(volume, pod)
        
        // 3. 挂载到 Pod 目录
        err = volumePlugin.SetUp(volume, pod)
    }
    return nil
}

9.2 Volume 插件 @

类型 说明
emptyDir 临时目录,Pod 删除后消失
hostPath 主机目录映射
configMap ConfigMap 挂载为文件
secret Secret 挂载为文件
persistentVolumeClaim 持久化存储
csi CSI 驱动(外部存储)
nfs NFS 网络存储

十、Pod 驱逐(Eviction) @

当节点资源不足时,kubelet 会主动驱逐部分 Pod:

10.1 驱逐触发条件 @

// pkg/kubelet/eviction/api/types.go
type Threshold struct {
    Signal      Signal            // memory.available、nodefs.inodesFree 等
    Operator    ThresholdOperator // 目前只有 OpLessThan
    Value       ThresholdValue    // 二选一:Quantity *resource.Quantity 或 Percentage float32
    GracePeriod time.Duration
    MinReclaim  *ThresholdValue   // 触发阈值后至少回收的资源量
}

type Signal string

const (
    SignalMemoryAvailable   Signal = "memory.available"
    SignalNodeFsAvailable   Signal = "nodefs.available"
    SignalNodeFsInodesFree  Signal = "nodefs.inodesFree"
    SignalImageFsAvailable  Signal = "imagefs.available"
    SignalImageFsInodesFree Signal = "imagefs.inodesFree"
    SignalPIDAvailable      Signal = "pid.available"
)

10.2 驱逐流程 @

// pkg/kubelet/eviction/eviction_manager.go
func (m *managerImpl) Start(ctx context.Context, diskInfoProvider DiskInfoProvider,
    podFunc ActivePodsFunc, podCleanedUpFunc PodCleanedUpFunc,
    monitoringInterval time.Duration) {
    go func() {
        for {
            // 1. 核心循环:采集节点摘要、判定阈值、必要时驱逐
            evictedPods, err := m.synchronize(ctx, diskInfoProvider, podFunc)

            if evictedPods != nil && err == nil {
                // 2. 每个监控周期最多驱逐 1 个 Pod,
                //    驱逐后阻塞等待其清理完成再进入下一周期
                m.waitForPodsCleanup(logger, podCleanedUpFunc, evictedPods)
            } else {
                time.Sleep(monitoringInterval)
            }
        }
    }()
}

// synchronize 内部:先尝试回收节点级资源(镜像/容器 GC),
// 压力仍未缓解时按信号对应的排序函数(rankMemoryPressure 等)
// 对活跃 Pod 排序,驱逐排名第一的 Pod 后立即返回

10.3 Pod 驱逐优先级 @

驱逐排序的首要依据是 Pod 资源用量是否超过其 requests,其次是 Pod Priority,最后是相对 requests 的超用量(见 pkg/kubelet/eviction/helpers.gorankMemoryPressure 等排序函数)。QoS 等级本身不是排序键,但与被驱逐风险大致相关:

QoS 等级 被驱逐风险 说明
BestEffort 最高(最易被驱逐) 无 requests,用量必然"超过 requests”
Burstable 中等 超出 requests 的部分越多越危险
Guaranteed 最低(最后被驱逐) requests == limits,且通常 Priority 较高

十一、节点状态上报 @

Kubelet 定期向 API Server 上报节点状态:

// pkg/kubelet/kubelet_node_status.go
// 周期性循环在外层 syncNodeStatus 中,每次调用带重试的 updateNodeStatus
func (kl *Kubelet) updateNodeStatus(ctx context.Context) error {
    for i := 0; i < nodeStatusUpdateRetry; i++ {
        if err := kl.tryUpdateNodeStatus(ctx, i); err != nil {
            // 失败则重试
        } else {
            return nil
        }
    }
    return fmt.Errorf("update node status exceeds retry count")
}

// tryUpdateNodeStatus 内部经 setNodeStatus 填充节点条件:
//   Ready / MemoryPressure / DiskPressure / PIDPressure / NetworkUnavailable
// 其中三种压力条件来自 eviction manager 的
// IsUnderMemoryPressure() / IsUnderDiskPressure() / IsUnderPIDPressure(),
// 最后调用 API Server 的 UpdateStatus 上报

11.1 节点条件 @

条件 含义
Ready 节点健康,可接收 Pod
MemoryPressure 内存压力大
DiskPressure 磁盘压力大
PIDPressure PID 压力大
NetworkUnavailable 网络未就绪

十二、Pod 垃圾回收 @

Kubelet 负责清理已终止的 Pod。Pod 级清理由 syncLoopIteration 的 housekeeping 分支定期触发 HandlePodCleanups

// pkg/kubelet/kubelet_pods.go
func (kl *Kubelet) HandlePodCleanups(ctx context.Context) error {
    // 1. 对比期望与实际集合,停止已终止 Pod 的 worker
    workingPods := kl.podWorkers.SyncKnownPods(logger, allPods)

    // 2. 停止不再运行的 Pod 的探针
    kl.probeManager.CleanupPods(possiblyRunningPods)

    // 3. 清理孤立的 Pod 状态
    kl.removeOrphanedPodStatuses(logger, allPods, mirrorPods)

    // 4. 清理孤立的 Pod 数据目录和 Volume
    kl.cleanupOrphanedPodDirs(logger, allPods, runningRuntimePods)

    // 5. 删除孤立的 mirror Pod、终止残留的孤儿容器等
}

容器级 GC 由 containerGC 独立执行,按 GCPolicy 策略删除已退出的容器:

// pkg/kubelet/container/container_gc.go
type GCPolicy struct {
    MinAge             time.Duration // 容器可被回收的最小存在时间
    MaxPerPodContainer int           // 每个 Pod(UID+容器名)最多保留的已退出容器数
    MaxContainers      int           // 全节点最多保留的已退出容器总数
}

十三、镜像管理 @

13.1 镜像拉取 @

// pkg/kubelet/images/image_manager.go
func (m *imageManager) EnsureImageExists(
    ctx context.Context,
    objRef *v1.ObjectReference,
    pod *v1.Pod,
    requestedImage string,
    pullSecrets []v1.Secret,
    podSandboxConfig *runtimeapi.PodSandboxConfig,
    podRuntimeHandler string,
    pullPolicy v1.PullPolicy,
) (imageRef, message string, err error) {
    // 1. 通过 CRI ImageStatus 检查镜像是否已存在(不存在本地 imageCache)
    // 2. 按 pullPolicy(Always/IfNotPresent/Never)决定是否需要拉取
    // 3. 需要拉取时解析凭据并调用 CRI PullImage
    return imageRef, message, nil
}

13.2 镜像垃圾回收 @

// pkg/kubelet/images/image_gc_manager.go
func (im *realImageGCManager) GarbageCollect(ctx context.Context, beganGC time.Time) error {
    // 1. 按最近使用时间对镜像排序(最久未用的在前)
    images, err := im.imagesInEvictionOrder(ctx, freeTime)

    // 2. 获取镜像文件系统的磁盘使用率
    fsStats, _, err := im.statsProvider.ImageFsStats(ctx)

    // 3. 使用率高于 HighThresholdPercent 时,从旧到新删除未使用的镜像,
    //    直到使用率降到 LowThresholdPercent
    if usagePercent >= im.policy.HighThresholdPercent {
        im.freeSpace(ctx, amountToFree, freeTime, images)
    }
    return nil
}

十四、设计亮点总结 @

  • 异步处理:Pod Sync Loop 与容器启动异步执行,提高吞吐量。
  • 分层管理:Pod 层管理 Pod 生命周期,容器层通过 CRI 与运行时交互,资源层通过 cgroup 实现资源隔离。
  • 健康检查:三种探针(Liveness/Readiness/Startup)保证容器健康。
  • 驱逐机制:基于资源压力信号的驱逐策略(按用量是否超过 requests 及 Pod Priority 排序),保证高优先级 Pod 的运行。
  • 状态上报:定期上报节点状态,为调度器提供决策依据。

十五、总结 @

Kubelet 是 Kubernetes 节点上的"执行者":

  • 监听:Watch API Server 获取 Pod 分配
  • 创建:通过 CRI 创建容器
  • 监控:执行健康检查,上报状态
  • 清理:驱逐低优先级 Pod,回收资源

理解 Kubelet,就掌握了 Pod 从"调度结果"到"运行实体"的完整链路。