Kubelet 深度剖析

概述

Kubelet 是 Kubernetes 集群中运行在每个节点上的核心组件,负责管理和维护 Pod 的生命周期。它是节点与 API Server 之间的桥梁,将容器运行时(Container Runtime)的实际状态与 etcd 中期望的状态进行调和(Reconciliation)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
┌─────────────────────────────────────────────────────────────────────┐
│ Kubelet 架构图 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ CRI (Container Runtime) │
│ │ PodWorker │ │ SyncHandler │ ┌─────────────────────┐ │
│ └──────┬──────┘ └──────┬──────┘ │ dockershim │ │
│ │ │ │ containerd │ │
│ └────────┬────────┘ │ cri-o │ │
│ │ └─────────────────────┘ │
│ ┌───────▼────────┐ │
│ │ 状态调和 │ ◄──────────────► 容器运行时 │
│ │ (Reconciler) │ │
│ └───────┬────────┘ │
│ │ │
│ ┌─────────────▼───────────────────────────────────────────┐ │
│ │ Kubelet 核心模块 │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PLEG │ │ VolumeMgr │ │ ImageGC │ │ │
│ │ │ (Pod Lifecycle)│ │ (Volume) │ │ (Image GC) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ ProbeMgr │ │ SecretMgr │ │ ConfigMapMgr│ │ │
│ │ │ (健康检查) │ │ (Secret) │ │ (ConfigMap) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ CAdvisor │ │ OOMWatcher │ │ StatusManager│ │ │
│ │ │ (资源监控) │ │ (OOM监控) │ │ (状态同步) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼───────────────────────────────┐ │
│ │ Pod Lifecycle Generator (PLEG) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Container│ │ PodSync │ │ Sync │ │ Garbage │ │ │
│ │ │ Started │ │ Handler │ │ Period │ │ Collect │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
└──────────────────────────────│───────────────────────────────────┘

┌──────────▼──────────┐
│ API Server │
│ (Etcd + Watch) │
└─────────────────────┘

1. Kubelet 启动流程

1.1 启动入口

Kubelet 的启动入口位于 cmd/kubelet/app/server.go

1
2
3
4
5
6
7
8
9
// cmd/kubelet/app/server.go
func Run(ctx context.Context, KubeletConfig *config.KubeletConfiguration, ... ) error {
// 1. 创建 kubelet server 认证信息
// 2. 初始化认证授权
// 3. 创建 kubelet server
// 4. 启动 status manager、pleg、container runtime
// 5. 启动 kubelet server (处理来自 apiserver 的请求)
// 6. 启动 sync loop
}

1.2 核心初始化流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──────────────────────────────────────────────────────────────┐
│ Kubelet 初始化流程 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 1. NewKubeletConfig() ──► 解析配置,创建 KubeletConfiguration │
│ │ │
│ ▼ │
│ 2. RunKubelet() ──► 创建 kubeletServer │
│ │ │
│ ▼ │
│ 3. NewMainKubelet() ──► 创建 Kubelet 实例 │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ statusManager containerRuntime PLEG │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ ▼ │
│ 4. StartKubelet() ──► 启动各个 manager │
│ │ │
│ ▼ │
│ 5. StartLoop() ──► 启动 sync loop │
│ │
└──────────────────────────────────────────────────────────────┘

1.3 关键配置项

Kubelet 配置文件位于 /var/lib/kubelet/config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
authentication:
anonymous: { enabled: false }
webhook: { enabled: true, cacheTTL: 2m0s }
authorization: { mode: Webhook, webhookCacheAuthorizedTTL: 1m0s }
cgroupDriver: systemd
cgroupsPerQOS: true
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock
runtimeRequestTimeout: 2m0s
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
kubeReserved:
cpu: "100m"
memory: 256Mi
systemReserved:
cpu: "100m"
memory: 256Mi

2. Pod 状态同步机制

2.1 Pod Sync Handler

Kubelet 通过 PodSyncHandler 来同步 Pod 的期望状态与实际状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// pkg/kubelet/pod/pod_manager.go
type PodManager interface {
GetPods() []*v1.Pod
GetPodByUID(types.UID) (*v1.Pod, bool)
GetPodByName(namespace, name string) (*v1.Pod, bool)
SetPods(pods []*v1.Pod)
AddPod(pod *v1.Pod)
UpdatePod(pod *v1.Pod)
DeletePod(pod *v1.Pod)
}

// pkg/kubelet/pod_workers.go
type PodWorkers interface {
UpdatePod(options UpdatePodOptions)
DeletePod(podUID types.UID)
RemoveOrphanedPodImages()
ForgetPod(uid types.UID)
}

2.2 Sync Loop 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌────────────────────────────────────────────────────────────────┐
│ Sync Loop 主循环 │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 同步循环 (每 1 秒) │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐ │ │
│ │ │ dispatch│ │ syncPod │ │ prune │ │ housekeeping │ │
│ │ │ Work │ │ │ │ Pods │ │ │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └────────┘ │ │
│ │ │ │ │ │ │ │
│ │ └───────────┴───────────┴───────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ PodSyncHandler │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ generatePodSync │ │ │
│ │ │ Task │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │ │
│ └───────────────────────┼───────────────────────────┘ │
│ │ │
└────────────────────────────┼───────────────────────────────────┘


┌─────────────────────┐
│ containerRuntime │
│ (CRI) │
└─────────────────────┘

2.3 Pod 同步状态定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// pkg/kubelet/pod_workers.go
type syncStatusType int

const (
// SyncPodCreate 表示创建新的 Pod
SyncPodCreate syncStatusType = iota
// SyncPodUpdate 表示更新已存在的 Pod
SyncPodUpdate
// SyncPodSync 表示同步 Pod 状态
SyncPodSync
// SyncPodKill 表示终止 Pod
SyncPodKill
// SyncPodUpdateType 表示更新 pod 资源
SyncPodUpdateType
)

// UpdatePodOptions 定义了更新 Pod 的选项
type UpdatePodOptions struct {
// Pod 唯一标识
UID types.UID
// 操作类型
Type syncStatusType
// 完整的 Pod 对象
Pod *v1.Pod
// Mirror 镜像信息
MirrorPod *v1.Pod
// Pod 配置时间戳
PodStatus *kubecontainer.PodStatus
// 开始时间
StartTime *metav1.Time
// 失败原因(如果失败)
Failure kuberuntime.ErrCrashLoopBackoff
// 错误通道
ErrCh chan<- error
}

3. Pod 创建流程

3.1 完整创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌─────────────────────────────────────────────────────────────────────────┐
│ Pod 创建完整流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ API Server Kubelet CRI │
│ │ │ │ │
│ │ Watch: 新建/更新 Pod │ │ │
│ │ ───────────────────────► │ │ │
│ │ │ │ │
│ │ │ 1. ParsePod() │ │
│ │ │ 验证并补充 Pod 信息 │ │
│ │ │ ────────────────────── │ │
│ │ │ │ │
│ │ │ 2. createPodSandbox() │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 3. 拉取 Init Container │ │
│ │ │ 镜像 │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 4. 启动 Init Container │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 5. 拉取业务 Container │ │
│ │ │ 镜像 │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 6. 启动业务 Container │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 7. 挂载 Volume │ │
│ │ │ (EmptyDir/HostPath/ │ │
│ │ │ PVC/ConfigMap/...) │ │
│ │ │ ───────────────────────► │ │
│ │ │ │ │
│ │ │ 8. 启动 Probe Manager │ │
│ │ │ (健康检查) │ │
│ │ │ │ │
│ │ │ 9. 更新状态到 API Server │ │
│ │ ◄ ─────────────────────── │ │ │
│ │ │ │ │
└─────────────────────────────────────────────────────────────────────────┘

3.2 SyncPod 核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// pkg/kubelet/kubelet.go
func (kl *Kubelet) syncPod(ctx context.Context, pod *v1.Pod, podStatus *kubecontainer.PodStatus) error {
// 1. 检查 Pod 是否被删除
if pod.DeletionTimestamp != nil {
return kl.killPod(pod, killReasonGracePeriodOverride, nil)
}

// 2. 创建 PodSandbox (如果需要)
podSandboxID, err := kl.makePodSandbox(pod, uint(runtimeapi.PodSandboxState_SANDBOX_READY))
if err != nil {
message := fmt.Sprintf("Failed to create PodSandbox: %v", err)
kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedCreatePodSandBox, message)
return err
}

// 3. 挂载卷
if err := kl.volumeManager.SetUpAt(pod.Spec.Volumes, pod, podStatus); err != nil {
return err
}

// 4. 拉取镜像
if err := kl.pullImages(pod); err != nil {
return err
}

// 5. 创建并启动容器
// 5.1 启动 Init Container
// 5.2 启动业务 Container
if err := kl.startContainers(pod, podStatus); err != nil {
return err
}

// 6. 启动探针
kl.probeManager.AddPod(pod)

return nil
}

3.3 Init Container 执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) startInitContainers(
pod *v1.Pod,
podStatus *kubecontainer.PodStatus,
) error {
// 1. 获取 Init Containers
initContainers := pod.Spec.InitContainers
if len(initContainers) == 0 {
return nil
}

// 2. 按顺序执行每个 Init Container
for _, container := range initContainers {
// 检查是否已完成
if status, ok := podStatus.ContainerStatus[container.Name]; ok {
if status.State == kubecontainer.ContainerStateExited {
continue // 已完成,跳到下一个
}
}

// 启动 Init Container
if err := m.startContainer(podSandboxID, container, pod, podStatus); err != nil {
return err
}
}
return nil
}

4. 容器运行时接口 (CRI)

4.1 CRI 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────────────┐
│ CRI 架构图 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Kubelet │ │
│ └────────┬────────┘ │
│ │ │
│ │ gRPC (Unix Socket) │
│ │ │
│ ┌────────▼────────┐ │
│ │ CRI │ │
│ │ (RuntimeService│ │
│ │ ImageService) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌───────▼───────┐ ┌──────▼──────┐ │
│ │ dockershim │ │ containerd │ │ cri-o │ │
│ │ (内置) │ │ (cri) │ │ │ │
│ └──────┬──────┘ └───────┬───────┘ └──────┬──────┘ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌───────▼───────┐ ┌──────▼──────┐ │
│ │ Docker │ │ containerd │ │ OCI │ │
│ │ Daemon │ │ daemon │ │ (runc) │ │
│ └────────────┘ └──────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

4.2 CRI 核心接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto (Go 映射)
type RuntimeService interface {
// 容器操作
CreateContainer(ctx context.Context, config *ContainerConfig, sandboxConfig *PodSandboxConfig) (string, error)
StartContainer(ctx context.Context, containerID string) error
StopContainer(ctx context.Context, containerID string, timeout int64) error
RemoveContainer(ctx context.Context, containerID string) error
ListContainers(ctx context.Context, filter *ContainerFilter) ([]*Container, error)
ContainerStatus(ctx context.Context, containerID string) (*ContainerStatus, error)

// 容器执行
ExecSync(ctx context.Context, containerID string, cmd []string, timeout time.Duration) (*ExecSyncResponse, error)
Exec(ctx context.Context, req *ExecRequest) (*ExecResponse, error)
Attach(ctx context.Context, req *AttachRequest) (*AttachResponse, error)
PortForward(ctx context.Context, req *PortForwardRequest) (*PortForwardResponse, error)
}

type ImageService interface {
ListImages(ctx context.Context, filter *ImageFilter) ([]*Image, error)
ImageStatus(ctx context.Context, image *ImageSpec) (*Image, error)
PullImage(ctx context.Context, image *ImageSpec, auth *AuthConfig) (string, error)
RemoveImage(ctx context.Context, image *ImageSpec) error
}

4.3 Pod Sandbox 创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// pkg/kubelet/kuberuntime/kuberuntime_linux.go
func (m *kubeGenericRuntimeManager) createPodSandbox(
ctx context.Context,
pod *v1.Pod,
attempt uint32,
) (string, error) {
// 1. 生成 PodSandbox 配置
sandboxConfig, err := m.generatePodSandboxConfig(pod, attempt)
if err != nil {
return "", err
}

// 2. 调用 CRI 创建 Sandbox
sandboxID, err := m.runtimeService.RunPodSandbox(ctx, sandboxConfig)
if err != nil {
return "", err
}

// 3. 等待 Sandbox 就绪
if err := m.waitSandboxReady(ctx, sandboxID); err != nil {
return "", err
}

// 4. 配置网络 (CNI)
if pod.Spec.HostNetwork {
return sandboxID, nil
}

// 调用 CNI 配置网络
if err := m.networkPlugin.SetUpPod(
sandboxID, pod.Namespace, pod.Name, annotations,
); err != nil {
return "", err
}

return sandboxID, nil
}

5. 容器健康检查

5.1 Probe 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─────────────────────────────────────────────────────────────────┐
│ Probe 健康检查机制 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Startup │ │ Liveness │ │ Readiness │ │
│ │ Probe │ │ Probe │ │ Probe │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ 初始化探测 │ │ 存活探测 │ │ 就绪探测 │ │
│ │ (可选) │ │ (必需) │ │ (必需) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Probe 配置 │ │
│ │ │ │
│ │ initialDelaySeconds: 启动后多久开始探测 │ │
│ │ periodSeconds: 探测间隔 (默认 10s) │ │
│ │ timeoutSeconds: 超时时间 (默认 1s) │ │
│ │ failureThreshold: 失败多少次后标记为失败 (默认 3) │ │
│ │ successThreshold: 成功多少次后标记为成功 (默认 1) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

5.2 Probe Manager 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// pkg/kubelet/probe/probe.go
type Manager interface {
AddPod(pod *v1.Pod)
RemovePod(pod *v1.Pod)
CleanupPods(pods []*v1.Pod)
UpdatePodStatus(pod *v1.Pod, status *v1.PodStatus)
}

type manager struct {
workers map[workerKey]*prober
mu sync.Mutex
}

func (m *manager) AddPod(pod *v1.Pod) {
for _, c := range pod.Spec.Containers {
// 1. 启动探针 worker
if c.StartupProbe != nil {
m.addWorker(pod, c, startup)
}
// 2. 存活探针 worker
if c.LivenessProbe != nil {
m.addWorker(pod, c, liveness)
}
// 3. 就绪探针 worker
if c.ReadinessProbe != nil {
m.addWorker(pod, c, readiness)
}
}
}

// pkg/kubelet/probe/exec/prober.go
func (p *prober) probe(execProbeType probeType) (probe.Result, string, error) {
switch {
case c.Exec != nil:
// 执行命令探测
return p.exec.Probe(e.pod, e.container, c.Exec)
case c.HTTPGet != nil:
// HTTP GET 探测
return p.http.Probe(e.pod, e.container, c.HTTPGet)
case c.TCPSocket != nil:
// TCP 端口探测
return p.tcp.Probe(e.pod, e.container, c.TCPSocket)
}
}

5.3 就绪探针与流量控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// pkg/kubelet/kubelet_pods.go
func (kl *Kubelet) updatePodReady(pod *v1.Pod) {
ready := kl.probeManager.IsPodReady(pod)

// 更新 Pod 状态
kl.statusManager.SetPodReady(pod.UID, ready)
}

// pkg/kubelet/status/status_manager.go
func (m *manager) SetPodReady(uid types.UID, ready bool) {
m.setPodStatus(uid, func(status *v1.PodStatus) {
status.Conditions = updatePodReadyCondition(status.Conditions)
v1meta.SetPodCondition(&status.Conditions, v1.PodCondition{
Type: v1.PodReady,
Status: v1.ConditionTrue,
})
})
}

6. 资源管理

6.1 cAdvisor 集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────────────┐
│ cAdvisor 资源监控架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ cAdvisor │ │
│ │ ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ │ │
│ │ │ Container │ │ Node │ │ Stats │ │ │
│ │ │ Metrics │ │ Metrics │ │ Provider │ │ │
│ │ │ (CPU/Mem/Net) │ │ (sys fs) │ │ │ │ │
│ │ └───────────────┘ └───────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Kubelet │ │
│ │ ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ │ │
│ │ │ Summary │ │ Resource │ │ OOM │ │ │
│ │ │ Provider │ │ Manager │ │ Manager │ │ │
│ │ └───────────────┘ └───────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Summary API (/stats) │ │
│ └─────────────────────────────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ Metrics Server Prometheus Dashboard │
│ │
└─────────────────────────────────────────────────────────────────┘

6.2 资源 QoS 层级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// pkg/kubelet/cm/container_manager_linux.go
// Kubernetes QoS 层级定义
// 1. Guaranteed - Pod 所有容器设置相同的 CPU/Memory requests 和 limits
// 2. Burstable - Pod 至少一个容器设置了 CPU/Memory requests
// 3. BestEffort - Pod 所有容器都没有设置 CPU/Memory requests/limits

// QoS 分类规则
qosClass := v1qos.GetPodQOS(pod)

// v1qos.GetPodQOS 实现
func GetPodQOS(pod *v1.Pod) v1.PodQOSClass {
// 1. 检查是否是 Guaranteed
if isGuaranteed(pod) {
return v1.PodQOSGuaranteed
}
// 2. 检查是否是 BestEffort
if isBestEffort(pod) {
return v1.PodQOSBestEffort
}
// 3. 默认为 Burstable
return v1.PodQOSBurstable
}

func isGuaranteed(pod *v1.Pod) bool {
for _, container := range pod.Spec.Containers {
if container.Resources.Requests.Cpu() == nil ||
container.Resources.Requests.Memory() == nil ||
container.Resources.Limits.Cpu() == nil ||
container.Resources.Limits.Memory() == nil {
return false
}
}
return true
}

7. 卷管理

7.1 Volume Manager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────────────────────────────────────────────────────────────┐
│ Volume Manager 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Volume Manager │ │
│ │ │ │
│ │ ┌────────────────┐ ┌────────────────┐ │ │
│ │ │ VolumeHost │◄──────►│ Reconciler │ │ │
│ │ │ (kubelet) │ │ (状态调和) │ │ │
│ │ └────────────────┘ └────────┬───────┘ │ │
│ │ │ │ │
│ │ ┌────────────────────────────────────┼────────────────┐ │ │
│ │ │ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ │ ┌───────────┐ │ │ │
│ │ │ │ desiredState│ │actualState │ │ │operation │ │ │ │
│ │ │ │ OfWorld │ │ OfWorld │ │ │ Queue │ │ │ │
│ │ │ │ (期望状态) │ │ (实际状态) │ │ │ (操作队列) │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ │ └───────────┘ │ │ │
│ │ └────────────────────────────────────┼────────────────┘ │ │
│ └───────────────────────────────────────┼───────────────────┘ │
│ │ │
└──────────────────────────────────────────┼───────────────────────┘

┌──────────────────────┼──────────────────────┐
│ │ │
┌─────────▼─────────┐ ┌─────────▼─────────┐ ┌──────▼──────┐
│ EmptyDir │ │ PVC (CSI) │ │ ConfigMap │
│ VolumePlugin │ │ VolumePlugin │ │ VolumePlugin │
└───────────────────┘ └───────────────────┘ └─────────────┘

7.2 Volume 挂载流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// pkg/kubelet/volumemanager/volume_manager.go
func (vc *VolumeController) Run(stopCh <-chan struct{}) {
// 启动 reconciler
go vc.reconciler.Run(stopCh)
// 启动状态同步
syncLoopPeriod := 100 * time.Millisecond
wait.Until(vc.operationExecutor, syncLoopPeriod, stopCh)
}

func (rc *reconciler) reconcile() {
// 1. 确保期望状态的卷已挂载
rc.mountAttach volumesNeedReporting
for _, volumeToMount := range rc.desiredStateOfWorld.GetVolumesToMount() {
if !rc.actualStateOfWorld.VolumeExistsWithSpecName(
volumeToMount.PodName, volumeToMount.VolumeSpecName) {
// 挂载卷
rc.operationExecutor.MountVolume(...)
}
}

// 2. 清理未使用的卷
for _, volumeToUnmount := range rc.actualStateOfWorld.GetVolumesToUnmount() {
if !rc.desiredStateOfWorld.PodExistsInVolume(volumeToUnmount.PodName, ...) {
// 卸载卷
rc.operationExecutor.UnmountVolume(...)
}
}
}

8. Pod 驱逐机制

8.1 驱逐策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// pkg/kubelet/eviction/eviction_manager.go
// 驱逐阈值配置
type thresholds struct {
// 硬驱逐阈值 (强制驱逐)
hard []evictionapi.Threshold
// 软驱逐阈值 (优雅驱逐)
soft []evictionapi.Threshold
// 驱逐信号
signals map[evictionapi.Signal]float64
}

// 驱逐信号类型
// - memory.available: 内存可用量
// - nodefs.available: 节点文件系统可用空间
// - nodefs.inodesFree: inode 可用数量
// - imagefs.available: 镜像文件系统可用空间
// - imagefs.inodesFree: 镜像文件系统 inode 可用数量
// - pid.available: PID 可用数量

8.2 驱逐流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌─────────────────────────────────────────────────────────────────┐
│ Pod 驱逐流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 监控资源使用 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ cAdvisor 采集节点资源状态 │ │
│ │ - 内存使用率 │ │
│ │ - 磁盘使用率 │ │
│ │ - Inode 使用率 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. 比较阈值 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 硬驱逐: 达到阈值立即驱逐 │ │
│ │ 软驱逐: 达到阈值 + gracePeriod 仍未恢复则驱逐 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. 选择要驱逐的 Pod │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 1. QoS 优先级: BestEffort > Burstable > Guaranteed │ │
│ │ 2. 资源使用量: 使用量越大越优先被驱逐 │ │
│ │ 3. Pod Priority: priority 值越小越优先被驱逐 │ │
│ │ 4. 运行时间: 运行时间越短越优先被驱逐 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 4. 执行驱逐 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 1. 调用 API Server 删除 Pod │ │
│ │ 2. 停止容器 │ │
│ │ 3. 清理卷挂载 │ │
│ │ 4. 清理网络命名空间 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

8.3 驱逐顺序代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pkg/kubelet/eviction/eviction_manager.go
func (m *manager) rankForEviction(pods []*v1.Pod) {
// 按驱逐优先级排序
sort.Slice(pods, func(i, j int) bool {
// 1. 比较 QoS 类别
if qosClass[i] != qosClass[j] {
return rankQoS(pods[i]) > rankQoS(pods[j])
}
// 2. 比较内存使用量
if memory[i] != memory[j] {
return memory[i] > memory[j]
}
// 3. 比较 Priority
if podPriority[i] != podPriority[j] {
return getPodPriority(pods[i]) < getPodPriority(pods[j])
}
// 4. 比较启动时间
return getContainerStartTime(pods[i]).Before(getContainerStartTime(pods[j]))
})
}

9. Mirror Pod 与静态 Pod

9.1 Mirror Pod 机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────────┐
│ Mirror Pod 机制 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ API Server Kubelet │
│ │ │ │
│ │ 1. 创建 Mirror Pod 规范 │ │
│ │ ◄──────────────────────── │ │
│ │ │ │
│ │ 2. 同步到节点 kubelet │ │
│ │ ────────────────────────► │ │
│ │ │ │
│ │ 3. 创建实际的 Pod │ │
│ │ │ ───────────────────────► │
│ │ │ │
│ │ 4. 上报状态 │ │
│ │ ◄──────────────────────── │ │
│ │ │ │
└─────────────────────────────────────────────────────────────────┘

9.2 静态 Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 静态 Pod 由 kubelet 直接管理,不由 API Server 控制
// 配置文件位置由 --pod-manifest-path 指定

// 静态 Pod 的特点:
// 1. 不需要 API Server 批准
// 2. 由 kubelet 直接创建和管理
// 3. 即使 API Server 不可用也会运行
// 4. 状态会上报为 Mirror Pod

// 静态 Pod 配置文件示例:
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
app: static
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80

10. 关键源码路径

功能 源码路径
入口函数 cmd/kubelet/app/server.go
Pod Workers pkg/kubelet/pod_workers.go
Pod Manager pkg/kubelet/pod/pod_manager.go
Sync Pod pkg/kubelet/kubelet.go (syncPod)
PLEG pkg/kubelet/pleg/generic.go
CRI Runtime pkg/kubelet/kuberuntime/
Probe Manager pkg/kubelet/probe/
Volume Manager pkg/kubelet/volumemanager/
Eviction pkg/kubelet/eviction/
Status Manager pkg/kubelet/status/status_manager.go
Image GC pkg/kubelet/images/image_gc_manager.go
cAdvisor pkg/kubelet/cadvisor/

面试题

基础题

1. Kubelet 的主要职责是什么?

Kubelet 是运行在每个 Kubernetes 节点上的核心组件,主要职责包括:

  • 监听 API Server 上的 Pod 分配任务
  • 创建和管理 Pod 容器
  • 负责容器健康检查(Probe)
  • 监控容器资源使用
  • 上报节点和 Pod 状态到 API Server
  • 与容器运行时(CRI)交互

2. Kubelet 如何与容器运行时通信?

Kubelet 通过 Container Runtime Interface (CRI) 与容器运行时通信。CRI 定义了 gRPC 接口,包括 RuntimeService 和 ImageService 两组接口。Kubelet 通过 Unix Socket 或 TCP 连接向容器运行时发送请求,如创建容器、启动容器、停止容器等操作。

3. 什么是 Mirror Pod?

Mirror Pod 是静态 Pod 在 API Server 上的镜像。Kubelet 读取静态 Pod 配置文件后,会自动在 API Server 上创建一个同名的 Mirror Pod,使得静态 Pod 的状态可以通过 kubectl 查看。实际容器由 Kubelet 直接管理,不经过 API Server。

4. Pod 创建流程中 Init Container 的作用是什么?

Init Container 在 Pod 的业务容器启动之前运行,主要用于:

  • 等待依赖服务就绪(如等待数据库启动)
  • 初始化配置(如从远程拉取配置、设置数据库 schema)
  • 预拉取数据(如预热缓存)
  • 执行一次性任务(如注册服务、生成密钥)

所有 Init Container 必须按顺序执行完成,才能启动业务容器。

5. Probe 健康检查有哪三种类型?

  • Startup Probe(启动探针):判断容器是否启动成功,用于慢启动容器,避免在启动期间被 Liveness Probe 杀死
  • Liveness Probe(存活探针):判断容器是否存活,如果失败则重启容器
  • Readiness Probe(就绪探针):判断容器是否就绪,就绪前不会接收 Service 流量

中级题

6. Kubelet 的 PLEG(Pod Lifecycle Event Generator)是什么?

PLEG 是 Kubelet 中的 Pod 生命周期事件生成器,它定期检查容器运行时的状态变化,生成 Pod 事件供 Kubelet 处理。PLEG 解决了轮询的性能问题,通过间歇性检查而非持续轮询来发现状态变化。主要流程:

  1. PLEG 定期(如 1 秒)调用 ListContainers 获取所有容器状态
  2. 与上次状态比较,找出变化的容器
  3. 生成 PodLifecycleEvent
  4. Kubelet 的 syncLoop 处理事件并同步 Pod 状态

7. Kubelet 的 Sync Loop 是如何工作的?

Sync Loop 是 Kubelet 的主循环,核心机制:

  1. 配置源同步:从 configZooKeeper 或 API Server 获取期望的 Pod 列表
  2. 容器运行时同步:从 PLEG 获取实际的容器状态
  3. 状态调和:比较期望状态与实际状态,生成同步任务
  4. 执行同步:调用 Pod Workers 执行创建、更新、删除操作
  5. 状态上报:将 Pod 状态上报到 API Server
1
2
3
4
5
6
7
8
9
10
11
12
13
// 伪代码
for {
// 获取期望的 Pod 列表
desiredPods := kl.podManager.GetPods()
// 获取实际的 Pod 状态
actualPods := kl.pleg.GetPodStatuses()
// 生成同步任务
for _, pod := range diff(desiredPods, actualPods) {
kl.dispatchWork(pod)
}
// 等待下次同步
time.Sleep(syncFrequency)
}

8. Kubelet 如何管理卷(Volume)的挂载和卸载?

Kubelet 的 Volume Manager 负责卷管理:

  1. 期望状态:跟踪 Pod 需要的卷配置
  2. 实际状态:跟踪已挂载的卷
  3. Reconciler:调和期望与实际状态
  4. OperationExecutor:执行挂载/卸载操作

挂载流程:Kubelet 获取 Pod 的 Volume 配置 → 调用相应的 Volume Plugin → 调用容器运行时挂载卷到容器

9. Pod 驱逐的 QoS 优先级是怎样的?

Pod 驱逐的 QoS 优先级(从高到低):

  1. BestEffort - 最高优先级被驱逐
  2. Burstable - 中等优先级
  3. Guaranteed - 最低优先级被驱逐

在同一 QoS 级别内,优先驱逐:

  • 资源使用量大的 Pod
  • Priority 值小的 Pod
  • 运行时间较短的 Pod

10. Kubelet 的身份认证和授权机制是什么?

Kubelet 的认证和授权采用 Webhook 模式,由 API Server 进行验证:

  1. 认证方式:Kubelet 使用 x509 证书或 Bearer Token 向 API Server 认证
  2. Webhook 模式:API Server 调用 Kubelet 的 /metrics/stats 等端点时,Kubelet 会向 API Server 发送 TokenReview 请求验证
  3. RBAC 授权:API Server 根据 Kubelet 的身份(由认证决定)通过 RBAC 进行授权

配置示例:

1
2
3
4
5
6
7
authentication:
webhook:
enabled: true
cacheTTL: 2m0s
authorization:
mode: Webhook
webhookCacheAuthorizedTTL: 1m0s

简而言之:Kubelet 作为服务端接收 API Server 请求时,API Server 会验证 Kubelet 的证书/Token(认证),并检查其权限(授权)。

高级题

11. 分析 Kubelet 的状态上报机制和 StatusManager 的实现

StatusManager 负责将 Pod 状态同步到 API Server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// pkg/kubelet/status/status_manager.go
type manager struct {
kubeClient clientset.Interface
podManager kubeletpod.PodManager
// 缓存待同步的 Pod 状态
podStatusChannel chan podStatusSyncRequest
}

func (m *manager) SetPodStatus(pod *v1.Pod, status v1.PodStatus) {
// 1. 验证状态合法性
// 2. 计算状态差异
// 3. 缓存状态到 syncMap
// 4. 发送到 channel 触发同步
m.podStatusChannel <- podStatusSyncRequest{pod.UID, status}
}

func (m *manager) syncPod(uid types.UID) {
// 从 channel 获取状态
// 调用 API Server 更新状态
m.kubeClient.CoreV1().Pods(pod.Namespace).UpdateStatus(ctx, pod)
}

关键机制:

  • 批量更新:合并短时间内的多次状态更新
  • 幂等性:只上报变化的字段
  • 错误处理:失败时使用指数退避重试

12. Kubelet 如何处理 Pod 的优雅终止?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) killContainer(...) error {
// 1. 发送 SIGTERM 信号
if err := m.runtimeService.StopContainer(containerID, timeout); err != nil {
// 2. SIGTERM 超时后发送 SIGKILL
m.killContainerWithSignal(containerID, int(syscall.SIGKILL))
}

// 3. 清理网络命名空间
// 4. 清理资源(卷、日志等)
}

// 优雅终止流程:
// 1. API Server 设置 DeletionTimestamp
// 2. Kubelet 检测到删除请求
// 3. 执行 preStop Hook(如果定义)
// 4. 发送 SIGTERM 到所有容器
// 5. 等待 terminationGracePeriodSeconds
// 6. 发送 SIGKILL 到仍未终止的容器
// 7. 更新 API Server 状态为 Terminated

13. Kubelet 的资源 QoS 是如何实现的?

Linux Cgroup 实现 Pod 资源限制:

1
2
3
4
5
6
7
8
9
// pkg/kubelet/cm/container_manager_linux.go
// Cgroup 目录结构:
// /sys/fs/cgroup/cpu/kubepods/
// ├── besteffort/
// │ └── pod<uid>/
// ├── burstable/
// │ └── pod<uid>/
// └── guaranteed/
// └── pod<uid>/

不同 QoS 级别的 Cgroup 配置:

QoS 级别 CPU 限制 Memory 限制
Guaranteed cpu.quota/cpu.period = limit memory.limit_in_bytes = limit
Burstable cpu.quota/cpu.period = request memory.soft_limit_in_bytes = request
BestEffort 无限制(可借用其他 Pod) 无限制(可借用其他 Pod)

场景题

14. 如何排查 Pod 一直处于 Pending 状态的问题?

排查步骤:

  1. 检查节点资源是否足够:kubectl describe node <node>
  2. 检查 Pod 的事件:kubectl describe pod <pod>
  3. 常见原因:
    • 资源不足(CPU/Memory/存储)
    • 节点存在污点但 Pod 没有容忍
    • PVC 未绑定(Pending PV)
    • 镜像拉取失败(配置错误或网络问题)
    • Init Container 失败
    • 调度失败(亲和性/反亲和性限制)
  4. 检查 Kubelet 日志:journalctl -u kubelet

15. 如果一个节点上的 Kubelet 不可用,Pod 会发生什么?

  1. API Server 检测到心跳停止:节点默认 40 秒无心跳标记为 NotReady
  2. Pod 不会被立即驱逐:等待 kube-controller-manager 的 nodecontroller 处理
  3. Pod 删除超时:默认 5 分钟(podEvictionTimeout)后开始驱逐
  4. Pod 重建:ReplicasetController 在其他健康节点上创建新 Pod

关键时间线:

  • 10 秒:无心跳触发 node status 更新失败
  • 40 秒:节点标记为 Unknown
  • 5 分钟:开始驱逐 Pod
  • 删除后由 Replicaset 创建新 Pod

16. 如何优化 Kubelet 的启动时间?

  1. 优化容器镜像预热

    • 使用本地镜像仓库
    • 配置 imagePullPolicy 为 IfNotPresent
  2. 优化卷挂载

    • 使用 CSI 迁移减少转换开销
    • 批量挂载减少串行操作
  3. 优化健康检查

    • 合理设置 initialDelaySeconds
    • 避免过多探针增加负载
  4. 其他优化

    • 升级到最新版本(性能改进)
    • 使用 gRPC 而不是 CNI(containerd)
    • 配置适当的 –runtime-request-timeout