【kubernetes v1.21】(kubelet 3)PLEG、健康检查、Eviction 与状态管理

Kubelet 深度分析 Part 3:PLEG、健康检查、Eviction 与状态管理


一、模块定位

1.1 PLEG(Pod Lifecycle Event Generator)

业务职责 :PLEG 是 Kubelet 的"感知引擎",负责发现容器运行时中 Pod/Container 状态的变化,并将其转化为 Pod 生命周期事件(PodLifecycleEvent),供 Kubelet 的主同步循环(syncLoop)消费。PLEG 弥补了容器运行时与 Kubelet 之间的状态鸿沟------容器可能因各种原因(OOM、外部 kill、运行时内部错误等)状态发生变化,Kubelet 无法被动感知,必须通过 PLEG 主动轮询或事件驱动来发现这些变化。

在 Kubelet 中的位置 :PLEG 是 Kubelet 主循环 syncLoop 的上游数据源之一。Kubelet 的 syncLoop 从 PLEG 的 eventChannel 读取事件,触发对应 Pod 的同步操作。如果 PLEG 异常(如 relist 超时),Kubelet 会将 Node 标记为 NotReady。

1.2 Prober(健康检查)

业务职责:Prober 负责对运行中的容器执行三种探针------Liveness、Readiness、Startup,根据探针结果决定容器的存活状态、就绪状态和启动状态。Liveness 探针失败会导致容器被重启;Readiness 探针失败会将 Pod 从 Service 的 Endpoints 中移除;Startup 探针用于标识容器是否已完成初始化。

在 Kubelet 中的位置 :Prober Manager 是 Kubelet 中独立的探针管理模块,它为每个定义了探针的容器启动独立的 worker goroutine,定期执行探针并通过 results.Manager 将结果传播到 Status Manager,最终反映到 Pod 的 ContainerStatus 中。

1.3 Eviction(驱逐)

业务职责:Eviction Manager 监控节点的资源使用情况(内存、磁盘、PID),当资源使用达到配置的驱逐阈值时,按照 QoS 等级和优先级选择 Pod 进行驱逐,以保证节点稳定运行。驱逐分为硬驱逐(立即执行)和软驱逐(宽限期后执行),并支持 LocalStorage 限额驱逐。

在 Kubelet 中的位置 :Eviction Manager 以独立 goroutine 运行,周期性调用 synchronize() 方法。它同时实现了 PodAdmitHandler 接口,在资源压力下拒绝新 Pod 的准入。驱逐动作最终调用 Kubelet 的 killPodFunc

1.4 Status Manager(状态管理)

业务职责:Status Manager 是 Pod 状态的"真相之源"(Source of Truth),负责缓存 Pod 的最新状态,并将状态变更同步到 API Server。它是 Kubelet 内部各模块(PLEG、Prober、Eviction 等)状态变更的汇聚点和出口。

在 Kubelet 中的位置 :Status Manager 通过 podStatusChannel 接收状态更新请求,以独立 goroutine 周期性批量同步到 API Server。它确保 Pod 状态的单调性演进,防止非法状态转换。

1.5 NodeStatus(节点状态上报)

业务职责:NodeStatus 模块负责构造和上报节点的各种状态信息,包括节点地址、容量(Capacity)、可分配量(Allocatable)、条件(Conditions:Ready/MemoryPressure/DiskPressure/PIDPressure)、镜像列表、版本信息等。

在 Kubelet 中的位置 :NodeStatus 的 Setter 函数被 Kubelet 的 updateNodeStatus 流程调用,通过函数式管道(一系列 Setter)逐步修改 Node 对象,最终通过 API Client 上报。

1.6 Lifecycle(生命周期钩子与准入)

业务职责:Lifecycle 模块定义了 Pod 生命周期的三大扩展点------Admit(准入)、SyncLoop(同步循环检查)、Sync(同步操作检查),以及容器生命周期钩子(PostStart/PreStop)的执行器。它还包含了 Predicate 准入处理器,用于在 Kubelet 本地执行调度谓词检查。

在 Kubelet 中的位置 :Lifecycle 的 PodAdmitHandler 链是 Pod 准入的拦截器链,Eviction Manager、Shutdown Manager、Predicate Handler 等都作为 Handler 加入链中。Handler Runner 负责执行容器生命周期钩子。

1.7 OOM Watcher

业务职责:OOM Watcher 通过 cAdvisor 的 OOM Parser 监控内核的 OOM 事件,当检测到系统级 OOM 时记录事件。

在 Kubelet 中的位置:OOM Watcher 以独立 goroutine 运行,仅在 Linux 平台生效。它主要用于监控和告警,不直接参与驱逐决策。

1.8 Node Shutdown Manager

业务职责:通过 systemd/logind 的 D-Bus 接口监听节点关闭事件,在节点关闭前优雅地终止 Pod。它使用 systemd 的 inhibit lock 机制延迟节点关闭,为 Pod 争取优雅终止时间。

在 Kubelet 中的位置 :Shutdown Manager 同时实现了 PodAdmitHandler 接口,在节点关闭过程中拒绝新 Pod 准入。它通过 GracefulNodeShutdown Feature Gate 控制。

1.9 Preemption(抢占)

业务职责 :CriticalPodAdmissionHandler 是一个 AdmissionFailureHandler,当 Critical Pod 因资源不足无法准入时,通过驱逐低优先级 Pod 来释放资源。驱逐顺序为 BestEffort → Burstable → Guaranteed。

在 Kubelet 中的位置:Preemption 作为 Predicate 准入链的失败处理器,仅在 Critical Pod 准入失败且唯一原因是资源不足时触发。

1.10 QoS(服务质量等级)

业务职责:QoS 模块根据 Pod 的资源请求和限制,为容器计算 OOM Score Adjustment,确保不同 QoS 等级的容器在系统 OOM 时有不同的被杀概率:Guaranteed 最不容易被杀,BestEffort 最容易被杀。

在 Kubelet 中的位置 :QoS 的 GetContainerOOMScoreAdjust 函数在容器创建时被调用,设置 /proc/<pid>/oom_score_adj


二、模块整体结构

2.1 PLEG 类结构与接口

go 复制代码
// 核心接口定义(pleg.go)
type PodLifecycleEventGenerator interface {
    Start()                              // 启动 PLEG 循环
    Watch() chan *PodLifecycleEvent      // 返回事件通道
    Healthy() (bool, error)              // 健康检查
}

// 事件类型
type PodLifeCycleEventType string  // ContainerStarted/ContainerDied/ContainerRemoved/PodSync/ContainerChanged

// 事件结构
type PodLifecycleEvent struct {
    ID   types.UID
    Type PodLifeCycleEventType
    Data interface{}  // 容器名称
}

// 核心实现(generic.go)
type GenericPLEG struct {
    relistPeriod    time.Duration                     // relist 周期(默认 1s)
    runtime         kubecontainer.Runtime             // 容器运行时接口
    eventChannel    chan *PodLifecycleEvent            // 事件输出通道
    podRecords      podRecords                         // Pod 状态记录(old/current)
    relistTime      atomic.Value                       // 上次 relist 时间
    cache           kubecontainer.Cache                // Pod 状态缓存
    clock           clock.Clock
    podsToReinspect map[types.UID]*kubecontainer.Pod  // 需要重新检查的 Pod
}

type podRecord struct {
    old     *kubecontainer.Pod
    current *kubecontainer.Pod
}
type podRecords map[types.UID]*podRecord

依赖注入关系

  • GenericPLEG 依赖 kubecontainer.Runtime(获取 Pod 列表和状态)、kubecontainer.Cache(缓存 Pod 状态)
  • Kubelet 创建 GenericPLEG 时注入 runtime 和 cache

2.2 Prober 类结构与接口

go 复制代码
// 探针管理器接口(prober_manager.go)
type Manager interface {
    AddPod(pod *v1.Pod)
    RemovePod(pod *v1.Pod)
    CleanupPods(desiredPods map[types.UID]sets.Empty)
    UpdatePodStatus(podUID types.UID, podStatus *v1.PodStatus)
}

// 探针管理器实现
type manager struct {
    workers          map[probeKey]*worker    // 活跃的探针 worker
    workerLock       sync.RWMutex
    statusManager    status.Manager          // 用于获取 Pod 状态
    readinessManager results.Manager         // Readiness 探针结果
    livenessManager  results.Manager         // Liveness 探针结果
    startupManager   results.Manager         // Startup 探针结果
    prober           *prober                 // 探针执行器
}

// 探针 Key
type probeKey struct {
    podUID        types.UID
    containerName string
    probeType     probeType   // liveness/readiness/startup
}

// 探针 Worker
type worker struct {
    stopCh          chan struct{}
    manualTriggerCh chan struct{}
    pod             *v1.Pod
    container       v1.Container
    spec            *v1.Probe
    probeType       probeType
    initialValue    results.Result
    resultsManager  results.Manager
    probeManager    *manager
    containerID     kubecontainer.ContainerID
    lastResult      results.Result
    resultRun       int
    onHold          bool
}

// 探针执行器
type prober struct {
    exec          execprobe.Prober
    readinessHTTP httpprobe.Prober   // 独立 HTTP 探针实例
    livenessHTTP  httpprobe.Prober   // 独立 HTTP 探针实例
    startupHTTP   httpprobe.Prober   // 独立 HTTP 探针实例
    tcp           tcpprobe.Prober
    runner        kubecontainer.CommandRunner
    recorder      record.EventRecorder
}

// 探针结果管理器(results/results_manager.go)
type Manager interface {
    Get(kubecontainer.ContainerID) (Result, bool)
    Set(kubecontainer.ContainerID, Result, *v1.Pod)
    Remove(kubecontainer.ContainerID)
    Updates() <-chan Update
}

type manager struct {
    sync.RWMutex
    cache   map[kubecontainer.ContainerID]Result
    updates chan Update   // 缓冲 20 的更新通道
}

2.3 Eviction 类结构与接口

go 复制代码
// 驱逐管理器接口(types.go)
type Manager interface {
    Start(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc,
           podCleanedUpFunc PodCleanedUpFunc, monitoringInterval time.Duration)
    IsUnderMemoryPressure() bool
    IsUnderDiskPressure() bool
    IsUnderPIDPressure() bool
}

// 驱逐管理器实现(eviction_manager.go)
type managerImpl struct {
    clock                        clock.Clock
    config                       Config
    killPodFunc                  KillPodFunc
    mirrorPodFunc                MirrorPodFunc
    imageGC                      ImageGC
    containerGC                  ContainerGC
    sync.RWMutex
    nodeConditions               []v1.NodeConditionType
    nodeConditionsLastObservedAt nodeConditionsObservedAt
    nodeRef                      *v1.ObjectReference
    recorder                     record.EventRecorder
    summaryProvider              stats.SummaryProvider
    thresholdsFirstObservedAt    thresholdsObservedAt
    thresholdsMet                []evictionapi.Threshold
    signalToRankFunc             map[evictionapi.Signal]rankFunc
    signalToNodeReclaimFuncs     map[evictionapi.Signal]nodeReclaimFuncs
    lastObservations             signalObservations
    dedicatedImageFs             *bool
    thresholdNotifiers           []ThresholdNotifier
    thresholdsLastUpdated        time.Time
}

// 驱逐信号(api/types.go)
type Signal string  // memory.available/nodefs.available/imagefs.available/nodefs.inodesFree/imagefs.inodesFree/allocatableMemory.available/pid.available

// 驱逐阈值
type Threshold struct {
    Signal     Signal
    Operator   ThresholdOperator  // 仅 OpLessThan
    Value      ThresholdValue     // Quantity 或 Percentage
    GracePeriod time.Duration
    MinReclaim  *ThresholdValue
}

// 内存阈值通知器
type memoryThresholdNotifier struct {
    threshold  evictionapi.Threshold
    cgroupPath string
    events     chan struct{}
    factory    NotifierFactory
    handler    func(string)
    notifier   CgroupNotifier
}

// Linux cgroup 通知器
type linuxCgroupNotifier struct {
    eventfd  int
    epfd     int
    stop     chan struct{}
    stopLock sync.Mutex
}

2.4 Status Manager 类结构

go 复制代码
type manager struct {
    kubeClient        clientset.Interface
    podManager        kubepod.Manager
    podStatuses       map[types.UID]versionedPodStatus  // Pod 状态缓存
    podStatusesLock   sync.RWMutex
    podStatusChannel  chan podStatusSyncRequest          // 缓冲 1000 的更新通道
    apiStatusVersions map[kubetypes.MirrorPodUID]uint64  // API Server 已知的版本号
    podDeletionSafety PodDeletionSafetyProvider
}

type versionedPodStatus struct {
    status       v1.PodStatus
    version      uint64
    podName      string
    podNamespace string
}

type podStatusSyncRequest struct {
    podUID types.UID
    status versionedPodStatus
}

三、核心业务逻辑深度解析

3.1 PLEG 事件生成与处理完整流程

3.1.1 PLEG 架构总览

#mermaid-svg-epFanOb7kChaOaiw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-epFanOb7kChaOaiw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-epFanOb7kChaOaiw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-epFanOb7kChaOaiw .error-icon{fill:#552222;}#mermaid-svg-epFanOb7kChaOaiw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-epFanOb7kChaOaiw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-epFanOb7kChaOaiw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-epFanOb7kChaOaiw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-epFanOb7kChaOaiw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-epFanOb7kChaOaiw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-epFanOb7kChaOaiw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-epFanOb7kChaOaiw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-epFanOb7kChaOaiw .marker.cross{stroke:#333333;}#mermaid-svg-epFanOb7kChaOaiw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-epFanOb7kChaOaiw p{margin:0;}#mermaid-svg-epFanOb7kChaOaiw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-epFanOb7kChaOaiw .cluster-label text{fill:#333;}#mermaid-svg-epFanOb7kChaOaiw .cluster-label span{color:#333;}#mermaid-svg-epFanOb7kChaOaiw .cluster-label span p{background-color:transparent;}#mermaid-svg-epFanOb7kChaOaiw .label text,#mermaid-svg-epFanOb7kChaOaiw span{fill:#333;color:#333;}#mermaid-svg-epFanOb7kChaOaiw .node rect,#mermaid-svg-epFanOb7kChaOaiw .node circle,#mermaid-svg-epFanOb7kChaOaiw .node ellipse,#mermaid-svg-epFanOb7kChaOaiw .node polygon,#mermaid-svg-epFanOb7kChaOaiw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-epFanOb7kChaOaiw .rough-node .label text,#mermaid-svg-epFanOb7kChaOaiw .node .label text,#mermaid-svg-epFanOb7kChaOaiw .image-shape .label,#mermaid-svg-epFanOb7kChaOaiw .icon-shape .label{text-anchor:middle;}#mermaid-svg-epFanOb7kChaOaiw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-epFanOb7kChaOaiw .rough-node .label,#mermaid-svg-epFanOb7kChaOaiw .node .label,#mermaid-svg-epFanOb7kChaOaiw .image-shape .label,#mermaid-svg-epFanOb7kChaOaiw .icon-shape .label{text-align:center;}#mermaid-svg-epFanOb7kChaOaiw .node.clickable{cursor:pointer;}#mermaid-svg-epFanOb7kChaOaiw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-epFanOb7kChaOaiw .arrowheadPath{fill:#333333;}#mermaid-svg-epFanOb7kChaOaiw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-epFanOb7kChaOaiw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-epFanOb7kChaOaiw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-epFanOb7kChaOaiw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-epFanOb7kChaOaiw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-epFanOb7kChaOaiw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-epFanOb7kChaOaiw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-epFanOb7kChaOaiw .cluster text{fill:#333;}#mermaid-svg-epFanOb7kChaOaiw .cluster span{color:#333;}#mermaid-svg-epFanOb7kChaOaiw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-epFanOb7kChaOaiw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-epFanOb7kChaOaiw rect.text{fill:none;stroke-width:0;}#mermaid-svg-epFanOb7kChaOaiw .icon-shape,#mermaid-svg-epFanOb7kChaOaiw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-epFanOb7kChaOaiw .icon-shape p,#mermaid-svg-epFanOb7kChaOaiw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-epFanOb7kChaOaiw .icon-shape .label rect,#mermaid-svg-epFanOb7kChaOaiw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-epFanOb7kChaOaiw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-epFanOb7kChaOaiw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-epFanOb7kChaOaiw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 容器运行时
PLEG 模块
Kubelet 主循环
Start(): go wait.Until(relist, period)
GetPods(true)
podList
setCurrent(pods)
computeEvents(old, current)
Watch()
updateCache()
UpdateTime()
syncLoop
GenericPLEG
eventChannel
podRecords
kubecontainer.Cache
kubecontainer.Runtime
relist
PodLifecycleEvent

3.1.2 PLEG Relist 完整流程图

#mermaid-svg-NdGf3825JCtWPuOT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NdGf3825JCtWPuOT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NdGf3825JCtWPuOT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NdGf3825JCtWPuOT .error-icon{fill:#552222;}#mermaid-svg-NdGf3825JCtWPuOT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NdGf3825JCtWPuOT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NdGf3825JCtWPuOT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NdGf3825JCtWPuOT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NdGf3825JCtWPuOT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NdGf3825JCtWPuOT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NdGf3825JCtWPuOT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NdGf3825JCtWPuOT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NdGf3825JCtWPuOT .marker.cross{stroke:#333333;}#mermaid-svg-NdGf3825JCtWPuOT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NdGf3825JCtWPuOT p{margin:0;}#mermaid-svg-NdGf3825JCtWPuOT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NdGf3825JCtWPuOT .cluster-label text{fill:#333;}#mermaid-svg-NdGf3825JCtWPuOT .cluster-label span{color:#333;}#mermaid-svg-NdGf3825JCtWPuOT .cluster-label span p{background-color:transparent;}#mermaid-svg-NdGf3825JCtWPuOT .label text,#mermaid-svg-NdGf3825JCtWPuOT span{fill:#333;color:#333;}#mermaid-svg-NdGf3825JCtWPuOT .node rect,#mermaid-svg-NdGf3825JCtWPuOT .node circle,#mermaid-svg-NdGf3825JCtWPuOT .node ellipse,#mermaid-svg-NdGf3825JCtWPuOT .node polygon,#mermaid-svg-NdGf3825JCtWPuOT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NdGf3825JCtWPuOT .rough-node .label text,#mermaid-svg-NdGf3825JCtWPuOT .node .label text,#mermaid-svg-NdGf3825JCtWPuOT .image-shape .label,#mermaid-svg-NdGf3825JCtWPuOT .icon-shape .label{text-anchor:middle;}#mermaid-svg-NdGf3825JCtWPuOT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NdGf3825JCtWPuOT .rough-node .label,#mermaid-svg-NdGf3825JCtWPuOT .node .label,#mermaid-svg-NdGf3825JCtWPuOT .image-shape .label,#mermaid-svg-NdGf3825JCtWPuOT .icon-shape .label{text-align:center;}#mermaid-svg-NdGf3825JCtWPuOT .node.clickable{cursor:pointer;}#mermaid-svg-NdGf3825JCtWPuOT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NdGf3825JCtWPuOT .arrowheadPath{fill:#333333;}#mermaid-svg-NdGf3825JCtWPuOT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NdGf3825JCtWPuOT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NdGf3825JCtWPuOT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NdGf3825JCtWPuOT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NdGf3825JCtWPuOT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NdGf3825JCtWPuOT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NdGf3825JCtWPuOT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NdGf3825JCtWPuOT .cluster text{fill:#333;}#mermaid-svg-NdGf3825JCtWPuOT .cluster span{color:#333;}#mermaid-svg-NdGf3825JCtWPuOT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NdGf3825JCtWPuOT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NdGf3825JCtWPuOT rect.text{fill:none;stroke-width:0;}#mermaid-svg-NdGf3825JCtWPuOT .icon-shape,#mermaid-svg-NdGf3825JCtWPuOT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NdGf3825JCtWPuOT .icon-shape p,#mermaid-svg-NdGf3825JCtWPuOT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NdGf3825JCtWPuOT .icon-shape .label rect,#mermaid-svg-NdGf3825JCtWPuOT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NdGf3825JCtWPuOT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NdGf3825JCtWPuOT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NdGf3825JCtWPuOT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 成功
失败



失败
成功



relist 开始
记录 relist 间隔指标
runtime.GetPods(true)
updateRelistTime(timestamp)
直接返回
更新 RunningPod/Container 指标
podRecords.setCurrent(pods)
遍历所有 podRecords
getContainersFromPods(oldPod, pod)
computeEvents(oldPod, pod, &container.ID)
eventsByPodID >= 10?
跳出循环
处理有事件的 Pod
cache 启用?
updateCache(pod, pid)
needsReinspectionpid = pod
delete(podsToReinspect, pid)
podRecords.update(pid)
发送事件到 eventChannel
过滤 ContainerChanged 事件
通道满?
丢弃事件,PLEGDiscardEvents++
发送事件
重新检查上次失败的 Pod
cache.UpdateTime(timestamp)
podsToReinspect = needsReinspection
relist 完成

3.1.3 事件生成逻辑(generateEvents 逐行解析)

generateEvents 是 PLEG 的核心状态机,它根据容器状态转换生成不同类型的事件:

go 复制代码
func generateEvents(podID types.UID, cid string, oldState, newState plegContainerState) []*PodLifecycleEvent {
    if newState == oldState {
        return nil  // 状态未变化,不生成事件
    }
    switch newState {
    case plegContainerRunning:
        // 新状态为运行中 → ContainerStarted 事件
        return []*PodLifecycleEvent{{ID: podID, Type: ContainerStarted, Data: cid}}
    case plegContainerExited:
        // 新状态为已退出 → ContainerDied 事件
        return []*PodLifecycleEvent{{ID: podID, Type: ContainerDied, Data: cid}}
    case plegContainerUnknown:
        // 新状态为未知 → ContainerChanged 事件
        // 注意:ContainerChanged 事件在发送时被过滤掉
        return []*PodLifecycleEvent{{ID: podID, Type: ContainerChanged, Data: cid}}
    case plegContainerNonExistent:
        switch oldState {
        case plegContainerExited:
            // 之前已报告死亡,现在容器被移除 → ContainerRemoved
            return []*PodLifecycleEvent{{ID: podID, Type: ContainerRemoved, Data: cid}}
        default:
            // 容器从存在直接消失(非正常路径)→ ContainerDied + ContainerRemoved
            return []*PodLifecycleEvent{
                {ID: podID, Type: ContainerDied, Data: cid},
                {ID: podID, Type: ContainerRemoved, Data: cid},
            }
        }
    }
}

状态转换矩阵

oldState \ newState Running Exited Unknown NonExistent
Running - ContainerDied ContainerChanged ContainerDied+Removed
Exited ContainerStarted - ContainerChanged ContainerRemoved
Unknown ContainerStarted ContainerDied - ContainerDied+Removed
NonExistent ContainerStarted ContainerDied ContainerChanged -
3.1.4 GenericPLEG 类图

渲染错误: Mermaid 渲染失败: Parse error on line 30: ... +Data interface{} } class -----------------------^ Expecting 'STRUCT_STOP', 'MEMBER', got 'OPEN_IN_STRUCT'

3.1.5 PLEG 健康检查

Healthy() 方法检查 relist 是否正常执行:如果距上次成功 relist 超过 3 分钟(relistThreshold),则返回不健康。Kubelet 在 NodeStatus 上报时使用此方法判断 PLEG 状态。

go 复制代码
func (g *GenericPLEG) Healthy() (bool, error) {
    relistTime := g.getRelistTime()
    if relistTime.IsZero() {
        return false, fmt.Errorf("pleg has yet to be successful")
    }
    metrics.PLEGLastSeen.Set(float64(relistTime.Unix()))
    elapsed := g.clock.Since(relistTime)
    if elapsed > relistThreshold {  // 3 * time.Minute
        return false, fmt.Errorf("pleg was last seen active %v ago; threshold is %v", elapsed, relistThreshold)
    }
    return true, nil
}
3.1.6 Pod IP 保留机制

getPodIPs 方法确保 Pod 在销毁后仍保留其 IP 地址。当新获取的 PodStatus 中 IP 为空(网络已拆除),但 Sandbox 已退出时,使用缓存中的旧 IP:

go 复制代码
func (g *GenericPLEG) getPodIPs(pid types.UID, status *kubecontainer.PodStatus) []string {
    if len(status.IPs) != 0 {
        return status.IPs  // 新状态有 IP,直接使用
    }
    oldStatus, err := g.cache.Get(pid)
    if err != nil || len(oldStatus.IPs) == 0 {
        return nil
    }
    for _, sandboxStatus := range status.SandboxStatuses {
        if sandboxStatus.State == runtimeapi.PodSandboxState_SANDBOX_READY {
            return status.IPs  // 还有就绪的 Sandbox,不使用旧 IP
        }
    }
    return oldStatus.IPs  // 使用旧 IP
}

3.2 三种 Probe 执行逻辑

3.2.1 Probe 管理器架构

#mermaid-svg-Y33RMUceoW8Xom54{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Y33RMUceoW8Xom54 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Y33RMUceoW8Xom54 .error-icon{fill:#552222;}#mermaid-svg-Y33RMUceoW8Xom54 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Y33RMUceoW8Xom54 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Y33RMUceoW8Xom54 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Y33RMUceoW8Xom54 .marker.cross{stroke:#333333;}#mermaid-svg-Y33RMUceoW8Xom54 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Y33RMUceoW8Xom54 p{margin:0;}#mermaid-svg-Y33RMUceoW8Xom54 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Y33RMUceoW8Xom54 .cluster-label text{fill:#333;}#mermaid-svg-Y33RMUceoW8Xom54 .cluster-label span{color:#333;}#mermaid-svg-Y33RMUceoW8Xom54 .cluster-label span p{background-color:transparent;}#mermaid-svg-Y33RMUceoW8Xom54 .label text,#mermaid-svg-Y33RMUceoW8Xom54 span{fill:#333;color:#333;}#mermaid-svg-Y33RMUceoW8Xom54 .node rect,#mermaid-svg-Y33RMUceoW8Xom54 .node circle,#mermaid-svg-Y33RMUceoW8Xom54 .node ellipse,#mermaid-svg-Y33RMUceoW8Xom54 .node polygon,#mermaid-svg-Y33RMUceoW8Xom54 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y33RMUceoW8Xom54 .rough-node .label text,#mermaid-svg-Y33RMUceoW8Xom54 .node .label text,#mermaid-svg-Y33RMUceoW8Xom54 .image-shape .label,#mermaid-svg-Y33RMUceoW8Xom54 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Y33RMUceoW8Xom54 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Y33RMUceoW8Xom54 .rough-node .label,#mermaid-svg-Y33RMUceoW8Xom54 .node .label,#mermaid-svg-Y33RMUceoW8Xom54 .image-shape .label,#mermaid-svg-Y33RMUceoW8Xom54 .icon-shape .label{text-align:center;}#mermaid-svg-Y33RMUceoW8Xom54 .node.clickable{cursor:pointer;}#mermaid-svg-Y33RMUceoW8Xom54 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Y33RMUceoW8Xom54 .arrowheadPath{fill:#333333;}#mermaid-svg-Y33RMUceoW8Xom54 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Y33RMUceoW8Xom54 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Y33RMUceoW8Xom54 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y33RMUceoW8Xom54 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Y33RMUceoW8Xom54 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y33RMUceoW8Xom54 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Y33RMUceoW8Xom54 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Y33RMUceoW8Xom54 .cluster text{fill:#333;}#mermaid-svg-Y33RMUceoW8Xom54 .cluster span{color:#333;}#mermaid-svg-Y33RMUceoW8Xom54 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Y33RMUceoW8Xom54 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Y33RMUceoW8Xom54 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Y33RMUceoW8Xom54 .icon-shape,#mermaid-svg-Y33RMUceoW8Xom54 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y33RMUceoW8Xom54 .icon-shape p,#mermaid-svg-Y33RMUceoW8Xom54 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Y33RMUceoW8Xom54 .icon-shape .label rect,#mermaid-svg-Y33RMUceoW8Xom54 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y33RMUceoW8Xom54 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Y33RMUceoW8Xom54 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Y33RMUceoW8Xom54 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 容器运行时
Prober 执行器
Probe Workers
Prober Manager
AddPod()
AddPod()
AddPod()
doProbe()
doProbe()
doProbe()
prober.probe()
RunInContainer
resultsManager.Set()
resultsManager.Set()
resultsManager.Set()
Updates()
Updates()
manager
readinessManager
livenessManager
startupManager
workers map
worker: startup
worker: readiness
worker: liveness
prober
exec.Prober
readinessHTTP
livenessHTTP
startupHTTP
tcp.Prober
CommandRunner
Status Manager
Kubelet 主循环

3.2.2 Worker 探针循环详解

Worker 的 run() 方法实现了探针的主循环:

go 复制代码
func (w *worker) run() {
    // 1. 如果 Kubelet 刚重启,随机等待一个周期,避免所有探针同时启动
    probeTickerPeriod := time.Duration(w.spec.PeriodSeconds) * time.Second
    if probeTickerPeriod > time.Since(w.probeManager.start) {
        time.Sleep(time.Duration(rand.Float64() * float64(probeTickerPeriod)))
    }

    probeTicker := time.NewTicker(probeTickerPeriod)
    defer func() {
        probeTicker.Stop()
        if !w.containerID.IsEmpty() {
            w.resultsManager.Remove(w.containerID)  // 清理结果
        }
        w.probeManager.removeWorker(...)  // 从管理器移除
        ProberResults.Delete(...)  // 清理指标
    }()

probeLoop:
    for w.doProbe() {
        select {
        case <-w.stopCh:          // 停止信号
            break probeLoop
        case <-probeTicker.C:     // 定时触发
        case <-w.manualTriggerCh: // 手动触发(用于 Readiness)
        }
    }
}

doProbe() 方法的逐行解析:

go 复制代码
func (w *worker) doProbe() (keepGoing bool) {
    // 1. 获取 Pod 状态
    status, ok := w.probeManager.statusManager.GetPodStatus(w.pod.UID)
    if !ok {
        return true  // Pod 尚未创建或已删除,继续等待
    }

    // 2. 终止 Pod 的检查
    if status.Phase == v1.PodFailed || status.Phase == v1.PodSucceeded {
        return false  // Pod 已终止,停止探针
    }

    // 3. 查找容器状态
    c, ok := podutil.GetContainerStatus(status.ContainerStatuses, w.container.Name)
    if !ok || len(c.ContainerID) == 0 {
        return true  // 容器尚未创建,继续等待
    }

    // 4. 容器 ID 变更处理
    if w.containerID.String() != c.ContainerID {
        if !w.containerID.IsEmpty() {
            w.resultsManager.Remove(w.containerID)  // 移除旧容器的结果
        }
        w.containerID = kubecontainer.ParseContainerID(c.ContainerID)
        w.resultsManager.Set(w.containerID, w.initialValue, w.pod)
        w.onHold = false  // 恢复探针
    }

    // 5. onHold 状态:等待新容器
    if w.onHold {
        return true
    }

    // 6. 非运行状态处理
    if c.State.Running == nil {
        if !w.containerID.IsEmpty() {
            w.resultsManager.Set(w.containerID, results.Failure, w.pod)
        }
        return c.State.Terminated == nil || w.pod.Spec.RestartPolicy != v1.RestartPolicyNever
    }

    // 7. 优雅关闭处理
    if w.pod.ObjectMeta.DeletionTimestamp != nil && 
       (w.probeType == liveness || w.probeType == startup) {
        w.resultsManager.Set(w.containerID, results.Success, w.pod)
        return false  // 停止探针
    }

    // 8. InitialDelaySeconds 检查
    if int32(time.Since(c.State.Running.StartedAt.Time).Seconds()) < w.spec.InitialDelaySeconds {
        return true  // 在初始延迟内,跳过探针
    }

    // 9. Startup 探针与 Liveness/Readiness 的互斥逻辑
    if c.Started != nil && *c.Started {
        if w.probeType == startup {
            return true  // Startup 成功后不再探测,但保持 worker 运行
        }
    } else {
        if w.probeType != startup {
            return true  // Startup 未成功前,不执行其他探针
        }
    }

    // 10. 执行探针
    result, err := w.probeManager.prober.probe(w.probeType, w.pod, status, w.container, w.containerID)

    // 11. 阈值处理(FailureThreshold / SuccessThreshold)
    if w.lastResult == result {
        w.resultRun++
    } else {
        w.lastResult = result
        w.resultRun = 1
    }
    if (result == results.Failure && w.resultRun < int(w.spec.FailureThreshold)) ||
       (result == results.Success && w.resultRun < int(w.spec.SuccessThreshold)) {
        return true  // 未达到阈值,不更新结果
    }

    // 12. 更新结果
    w.resultsManager.Set(w.containerID, result, w.pod)

    // 13. Liveness/Startup 失败时挂起探针
    if (w.probeType == liveness || w.probeType == startup) && result == results.Failure {
        w.onHold = true   // 等待新容器
        w.resultRun = 0
    }

    return true
}
3.2.3 Exec/HTTP/TCP Probe 执行流程

#mermaid-svg-WWQMAPu5Ee8Flr4u{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WWQMAPu5Ee8Flr4u .error-icon{fill:#552222;}#mermaid-svg-WWQMAPu5Ee8Flr4u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WWQMAPu5Ee8Flr4u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .marker.cross{stroke:#333333;}#mermaid-svg-WWQMAPu5Ee8Flr4u svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WWQMAPu5Ee8Flr4u p{margin:0;}#mermaid-svg-WWQMAPu5Ee8Flr4u .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster-label text{fill:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster-label span{color:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster-label span p{background-color:transparent;}#mermaid-svg-WWQMAPu5Ee8Flr4u .label text,#mermaid-svg-WWQMAPu5Ee8Flr4u span{fill:#333;color:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .node rect,#mermaid-svg-WWQMAPu5Ee8Flr4u .node circle,#mermaid-svg-WWQMAPu5Ee8Flr4u .node ellipse,#mermaid-svg-WWQMAPu5Ee8Flr4u .node polygon,#mermaid-svg-WWQMAPu5Ee8Flr4u .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .rough-node .label text,#mermaid-svg-WWQMAPu5Ee8Flr4u .node .label text,#mermaid-svg-WWQMAPu5Ee8Flr4u .image-shape .label,#mermaid-svg-WWQMAPu5Ee8Flr4u .icon-shape .label{text-anchor:middle;}#mermaid-svg-WWQMAPu5Ee8Flr4u .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .rough-node .label,#mermaid-svg-WWQMAPu5Ee8Flr4u .node .label,#mermaid-svg-WWQMAPu5Ee8Flr4u .image-shape .label,#mermaid-svg-WWQMAPu5Ee8Flr4u .icon-shape .label{text-align:center;}#mermaid-svg-WWQMAPu5Ee8Flr4u .node.clickable{cursor:pointer;}#mermaid-svg-WWQMAPu5Ee8Flr4u .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .arrowheadPath{fill:#333333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WWQMAPu5Ee8Flr4u .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WWQMAPu5Ee8Flr4u .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WWQMAPu5Ee8Flr4u .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster text{fill:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u .cluster span{color:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WWQMAPu5Ee8Flr4u .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WWQMAPu5Ee8Flr4u rect.text{fill:none;stroke-width:0;}#mermaid-svg-WWQMAPu5Ee8Flr4u .icon-shape,#mermaid-svg-WWQMAPu5Ee8Flr4u .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WWQMAPu5Ee8Flr4u .icon-shape p,#mermaid-svg-WWQMAPu5Ee8Flr4u .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WWQMAPu5Ee8Flr4u .icon-shape .label rect,#mermaid-svg-WWQMAPu5Ee8Flr4u .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WWQMAPu5Ee8Flr4u .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WWQMAPu5Ee8Flr4u .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WWQMAPu5Ee8Flr4u :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Exec
HTTPGet
TCPSocket
无 Handler
runner.RunInContainer(containerID, cmd, timeout)
readiness
liveness
startup
是且出错

runProbe 开始
探测类型
exec.Probe(newExecInContainer)
构建 URL + Headers
构建 Host:Port
返回 Unknown 错误
execInContainer.Run()
容器运行时
探针类型
readinessHTTP.Probe(url, headers, timeout)
livenessHTTP.Probe(url, headers, timeout)
startupHTTP.Probe(url, headers, timeout)
tcp.Probe(host, port, timeout)
返回 probe.Result
重试 < maxProbeRetries?
返回 result, output, err

Exec 探针 :通过 kubecontainer.CommandRunner.RunInContainer() 在容器内执行命令。execInContainer 适配器将 Kubelet 的 RunInContainer 接口转换为 exec.Cmd 接口,供 exec.Probe 使用。

HTTP 探针 :构造 URL(scheme://host:port/path),使用独立的 HTTP 客户端实例(避免连接池冲突 #49740),支持自定义 Headers。三种探针使用不同的 HTTP 实例。

TCP 探针 :在指定 host:port 上建立 TCP 连接,检查是否可连通。

3.2.4 Probe 结果与 Pod 状态联动

#mermaid-svg-7GhquPxYdUWmp8kd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7GhquPxYdUWmp8kd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7GhquPxYdUWmp8kd .error-icon{fill:#552222;}#mermaid-svg-7GhquPxYdUWmp8kd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7GhquPxYdUWmp8kd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7GhquPxYdUWmp8kd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7GhquPxYdUWmp8kd .marker.cross{stroke:#333333;}#mermaid-svg-7GhquPxYdUWmp8kd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7GhquPxYdUWmp8kd p{margin:0;}#mermaid-svg-7GhquPxYdUWmp8kd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7GhquPxYdUWmp8kd .cluster-label text{fill:#333;}#mermaid-svg-7GhquPxYdUWmp8kd .cluster-label span{color:#333;}#mermaid-svg-7GhquPxYdUWmp8kd .cluster-label span p{background-color:transparent;}#mermaid-svg-7GhquPxYdUWmp8kd .label text,#mermaid-svg-7GhquPxYdUWmp8kd span{fill:#333;color:#333;}#mermaid-svg-7GhquPxYdUWmp8kd .node rect,#mermaid-svg-7GhquPxYdUWmp8kd .node circle,#mermaid-svg-7GhquPxYdUWmp8kd .node ellipse,#mermaid-svg-7GhquPxYdUWmp8kd .node polygon,#mermaid-svg-7GhquPxYdUWmp8kd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7GhquPxYdUWmp8kd .rough-node .label text,#mermaid-svg-7GhquPxYdUWmp8kd .node .label text,#mermaid-svg-7GhquPxYdUWmp8kd .image-shape .label,#mermaid-svg-7GhquPxYdUWmp8kd .icon-shape .label{text-anchor:middle;}#mermaid-svg-7GhquPxYdUWmp8kd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7GhquPxYdUWmp8kd .rough-node .label,#mermaid-svg-7GhquPxYdUWmp8kd .node .label,#mermaid-svg-7GhquPxYdUWmp8kd .image-shape .label,#mermaid-svg-7GhquPxYdUWmp8kd .icon-shape .label{text-align:center;}#mermaid-svg-7GhquPxYdUWmp8kd .node.clickable{cursor:pointer;}#mermaid-svg-7GhquPxYdUWmp8kd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7GhquPxYdUWmp8kd .arrowheadPath{fill:#333333;}#mermaid-svg-7GhquPxYdUWmp8kd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7GhquPxYdUWmp8kd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7GhquPxYdUWmp8kd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7GhquPxYdUWmp8kd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7GhquPxYdUWmp8kd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7GhquPxYdUWmp8kd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7GhquPxYdUWmp8kd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7GhquPxYdUWmp8kd .cluster text{fill:#333;}#mermaid-svg-7GhquPxYdUWmp8kd .cluster span{color:#333;}#mermaid-svg-7GhquPxYdUWmp8kd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7GhquPxYdUWmp8kd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7GhquPxYdUWmp8kd rect.text{fill:none;stroke-width:0;}#mermaid-svg-7GhquPxYdUWmp8kd .icon-shape,#mermaid-svg-7GhquPxYdUWmp8kd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7GhquPxYdUWmp8kd .icon-shape p,#mermaid-svg-7GhquPxYdUWmp8kd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7GhquPxYdUWmp8kd .icon-shape .label rect,#mermaid-svg-7GhquPxYdUWmp8kd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7GhquPxYdUWmp8kd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7GhquPxYdUWmp8kd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7GhquPxYdUWmp8kd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} resultsManager.Set(containerID, result, pod)
cacheid = result
updates <- Update
Liveness 结果
检测到 Liveness Failure
Readiness 结果
SetContainerReadiness()
syncPod()
Success
Failure
不存在
Success
Failure
不存在 worker
存在 worker 但未运行
Worker doProbe
results.Manager
结果缓存
Updates Channel
Kubelet syncLoop
杀死并重启容器
Status Manager
更新 PodStatus
API Server
manager.UpdatePodStatus()
Startup 探针结果
Started = true
Started = false
Started = nil
Readiness 探针结果
Ready = true
Ready = false
Ready = true (无探针则默认就绪)
触发 manualTriggerCh 立即探测

3.3 Eviction 信号采集与决策算法

3.3.1 Eviction 架构总览

#mermaid-svg-3M04ht1Th0DW72ov{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3M04ht1Th0DW72ov .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3M04ht1Th0DW72ov .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3M04ht1Th0DW72ov .error-icon{fill:#552222;}#mermaid-svg-3M04ht1Th0DW72ov .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3M04ht1Th0DW72ov .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3M04ht1Th0DW72ov .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3M04ht1Th0DW72ov .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3M04ht1Th0DW72ov .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3M04ht1Th0DW72ov .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3M04ht1Th0DW72ov .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3M04ht1Th0DW72ov .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3M04ht1Th0DW72ov .marker.cross{stroke:#333333;}#mermaid-svg-3M04ht1Th0DW72ov svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3M04ht1Th0DW72ov p{margin:0;}#mermaid-svg-3M04ht1Th0DW72ov .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3M04ht1Th0DW72ov .cluster-label text{fill:#333;}#mermaid-svg-3M04ht1Th0DW72ov .cluster-label span{color:#333;}#mermaid-svg-3M04ht1Th0DW72ov .cluster-label span p{background-color:transparent;}#mermaid-svg-3M04ht1Th0DW72ov .label text,#mermaid-svg-3M04ht1Th0DW72ov span{fill:#333;color:#333;}#mermaid-svg-3M04ht1Th0DW72ov .node rect,#mermaid-svg-3M04ht1Th0DW72ov .node circle,#mermaid-svg-3M04ht1Th0DW72ov .node ellipse,#mermaid-svg-3M04ht1Th0DW72ov .node polygon,#mermaid-svg-3M04ht1Th0DW72ov .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3M04ht1Th0DW72ov .rough-node .label text,#mermaid-svg-3M04ht1Th0DW72ov .node .label text,#mermaid-svg-3M04ht1Th0DW72ov .image-shape .label,#mermaid-svg-3M04ht1Th0DW72ov .icon-shape .label{text-anchor:middle;}#mermaid-svg-3M04ht1Th0DW72ov .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3M04ht1Th0DW72ov .rough-node .label,#mermaid-svg-3M04ht1Th0DW72ov .node .label,#mermaid-svg-3M04ht1Th0DW72ov .image-shape .label,#mermaid-svg-3M04ht1Th0DW72ov .icon-shape .label{text-align:center;}#mermaid-svg-3M04ht1Th0DW72ov .node.clickable{cursor:pointer;}#mermaid-svg-3M04ht1Th0DW72ov .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3M04ht1Th0DW72ov .arrowheadPath{fill:#333333;}#mermaid-svg-3M04ht1Th0DW72ov .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3M04ht1Th0DW72ov .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3M04ht1Th0DW72ov .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3M04ht1Th0DW72ov .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3M04ht1Th0DW72ov .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3M04ht1Th0DW72ov .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3M04ht1Th0DW72ov .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3M04ht1Th0DW72ov .cluster text{fill:#333;}#mermaid-svg-3M04ht1Th0DW72ov .cluster span{color:#333;}#mermaid-svg-3M04ht1Th0DW72ov div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3M04ht1Th0DW72ov .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3M04ht1Th0DW72ov rect.text{fill:none;stroke-width:0;}#mermaid-svg-3M04ht1Th0DW72ov .icon-shape,#mermaid-svg-3M04ht1Th0DW72ov .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3M04ht1Th0DW72ov .icon-shape p,#mermaid-svg-3M04ht1Th0DW72ov .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3M04ht1Th0DW72ov .icon-shape .label rect,#mermaid-svg-3M04ht1Th0DW72ov .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3M04ht1Th0DW72ov .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3M04ht1Th0DW72ov .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3M04ht1Th0DW72ov :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存阈值通知
驱逐决策
阈值评估
信号采集
Eviction Manager
KernelMemcgNotification
eventfd/epoll
managerImpl
synchronize
Admit: PodAdmitHandler
IsUnderPressure
SummaryProvider
statsapi.Summary
memory.available
allocatableMemory.available
nodefs.available
imagefs.available
nodefs.inodesFree
imagefs.inodesFree
pid.available
thresholdsMet
thresholdsFirstObservedAt
thresholdsMetGracePeriod
thresholdsUpdatedStats
nodeConditions
rankFunc
reclaimNodeLevelResources
evictPod
memoryThresholdNotifier
linuxCgroupNotifier
Linux Kernel memcg

复制代码
#### 3.3.3 驱逐排序算法

**内存压力排序**(`rankMemoryPressure`):

排序优先级:

  1. exceedMemoryRequests --- 使用量超过请求的 Pod 优先驱逐

  2. priority --- 低优先级 Pod 优先驱逐

  3. memory(usage - request) --- 内存使用量(减去请求值)大的优先驱逐

    PID 压力排序rankPIDPressure):

排序优先级:

  1. priority --- 低优先级 Pod 优先驱逐

  2. process --- 进程数多的 Pod 优先驱逐

    磁盘压力排序rankDiskPressureFunc):

排序优先级:

  1. exceedDiskRequests --- 使用量超过请求的 Pod 优先驱逐

  2. priority --- 低优先级 Pod 优先驱逐

  3. disk(usage - request) --- 磁盘使用量(减去请求值)大的优先驱逐

    阈值优先级排序byEvictionPriority):
    内存信号优先于所有其他信号,无法回收资源的阈值排最后。

    3.3.4 LocalStorage 驱逐

    LocalStorage 驱逐独立于常规 Eviction 流程,检查三个维度:

    1. emptyDir 限额驱逐emptyDirLimitEviction):检查每个 EmptyDir 卷的使用量是否超过 sizeLimit
    2. Pod 级临时存储限额驱逐podEphemeralStorageLimitEviction):检查 Pod 的总临时存储使用量是否超过 Pod 级限制。
    3. 容器级临时存储限额驱逐containerEphemeralStorageLimitEviction):检查每个容器的临时存储使用量(logs + rootfs)是否超过容器级限制。

    3.3.5 驱逐准入控制

    Eviction Manager 实现了 PodAdmitHandler,在资源压力下拒绝新 Pod 准入:

    go 复制代码
    func (m *managerImpl) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
        if len(m.nodeConditions) == 0 {
            return PodAdmitResult{Admit: true}  // 无压力,允许
        }
        if kubelettypes.IsCriticalPod(attrs.Pod) {
            return PodAdmitResult{Admit: true}  // Critical Pod 总是允许
        }
        // 仅内存压力时,允许非 BestEffort Pod
        if nodeOnlyHasMemoryPressureCondition {
            if v1.PodQOSBestEffort != v1qos.GetPodQOS(attrs.Pod) {
                return PodAdmitResult{Admit: true}
            }
            // BestEffort Pod 容忍内存压力污点时允许
            if v1helper.TolerationsTolerateTaint(...) {
                return PodAdmitResult{Admit: true}
            }
        }
        return PodAdmitResult{Admit: false, ...}  // 拒绝
    }

3.4 Status Manager 与 NodeStatus 上报机制

NodeStatus 上报时序图
API Server Eviction Manager NodeStatus Setters Kubelet API Server Eviction Manager NodeStatus Setters Kubelet #mermaid-svg-VtIchF7MXBkUwvVH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VtIchF7MXBkUwvVH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VtIchF7MXBkUwvVH .error-icon{fill:#552222;}#mermaid-svg-VtIchF7MXBkUwvVH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VtIchF7MXBkUwvVH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VtIchF7MXBkUwvVH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VtIchF7MXBkUwvVH .marker.cross{stroke:#333333;}#mermaid-svg-VtIchF7MXBkUwvVH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VtIchF7MXBkUwvVH p{margin:0;}#mermaid-svg-VtIchF7MXBkUwvVH .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VtIchF7MXBkUwvVH text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-VtIchF7MXBkUwvVH .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-VtIchF7MXBkUwvVH .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-VtIchF7MXBkUwvVH .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-VtIchF7MXBkUwvVH .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-VtIchF7MXBkUwvVH #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-VtIchF7MXBkUwvVH .sequenceNumber{fill:white;}#mermaid-svg-VtIchF7MXBkUwvVH #sequencenumber{fill:#333;}#mermaid-svg-VtIchF7MXBkUwvVH #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-VtIchF7MXBkUwvVH .messageText{fill:#333;stroke:none;}#mermaid-svg-VtIchF7MXBkUwvVH .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VtIchF7MXBkUwvVH .labelText,#mermaid-svg-VtIchF7MXBkUwvVH .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-VtIchF7MXBkUwvVH .loopText,#mermaid-svg-VtIchF7MXBkUwvVH .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-VtIchF7MXBkUwvVH .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-VtIchF7MXBkUwvVH .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-VtIchF7MXBkUwvVH .noteText,#mermaid-svg-VtIchF7MXBkUwvVH .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-VtIchF7MXBkUwvVH .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VtIchF7MXBkUwvVH .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VtIchF7MXBkUwvVH .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-VtIchF7MXBkUwvVH .actorPopupMenu{position:absolute;}#mermaid-svg-VtIchF7MXBkUwvVH .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-VtIchF7MXBkUwvVH .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-VtIchF7MXBkUwvVH .actor-man circle,#mermaid-svg-VtIchF7MXBkUwvVH line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-VtIchF7MXBkUwvVH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 检查 runtime/network/storage/shutdown 错误 loop每个 syncNodeStatus 周期 tryUpdateNodeStatus()Get(node)current nodeNodeAddress()更新 AddressesMachineInfo()更新 Capacity/AllocatableVersionInfo()更新 NodeInfoDaemonEndpoints()更新 DaemonEndpointsImages()更新 Images 列表GoRuntime()更新 OS/ArchReadyCondition()更新 NodeReady ConditionIsUnderMemoryPressure()boolMemoryPressureCondition()更新 MemoryPressure ConditionIsUnderDiskPressure()boolDiskPressureCondition()更新 DiskPressure ConditionIsUnderPIDPressure()boolPIDPressureCondition()更新 PIDPressure ConditionVolumesInUse()更新 VolumesInUseVolumeLimits()更新 VolumeLimitsUpdate(node)更新结果

相关推荐
该昵称用户已存在1 小时前
2026 能源中台架构实录:MyEMS 百万级测点场景下的时序数据库选型与微服务拆分策略
架构·能源·时序数据库
秋漓2 小时前
Kubernetes了解与应用
云原生·容器·kubernetes
IT策士2 小时前
第28篇 k8s之Service:为 Pod 提供稳定的访问入口
云原生·容器·kubernetes
星光不负赶路人772 小时前
深度解析:Claude Code 为什么把多 Agent 编排写进可执行代码
架构
张忠琳2 小时前
【kubernetes v1.21】(kube-scheduler 4)kube-scheduler 内部缓存、队列与抢占机制
云原生·架构·kubernetes
数字时代全景窗2 小时前
商业航天不是航天的分支,而是产业革命本身
架构·软件工程
苏渡苇2 小时前
Seata 番外篇:使用 docker-compose 部署 Seata Server(TC)及 K8S 部署 Seata 高可用
spring boot·docker·微服务·容器·kubernetes·seata·springcloud
轻刀快马2 小时前
从繁琐到极简,从幻象到本质:Spring AOP 架构演进与实战避坑指南
java·spring·架构
vortex52 小时前
Polkit 架构原理深度解析
架构