Pod 生命周期与终止流程

概述

Pod 的生命周期从 Pending 开始,经过 Running,最终到达 Succeeded 或 Failed。理解 Pod 生命周期对于正确管理应用至关重要。

Pod 生命周期阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// staging/src/k8s.io/api/core/v1/types.go

type PodPhase string

const (
// PodPending: Pod 已被接受,但容器尚未启动
PodPending PodPhase = "Pending"

// PodRunning: Pod 已绑定到节点,所有容器已创建,至少一个容器正在运行
PodRunning PodPhase = "Running"

// PodSucceeded: Pod 中所有容器已成功终止,不会重启
PodSucceeded PodPhase = "Succeeded"

// PodFailed: Pod 中所有容器已终止,至少一个容器失败
PodFailed PodPhase = "Failed"

// PodUnknown: 无法获取 Pod 状态
PodUnknown PodPhase = "Unknown"
)

生命周期流程图

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
46
47
48
┌──────────────────────────────────────────────────────────────────────────────┐
│ Pod 创建请求 │
│ kube-apiserver -> etcd │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ Pending 阶段 │
│ │
│ 1. Pod 已被 API Server 接受 │
│ 2. 等待调度器绑定到节点 │
│ 3. 拉取镜像 │
│ 4. 创建 Pod Sandbox │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ Init Container 阶段 │
│ │
│ 按顺序执行 Init Container (如果有) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 1. init-container-1 执行 -> 完成 │ │
│ │ 2. init-container-2 执行 -> 完成 │ │
│ │ 3. ... 所有 Init Container 完成 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 注意: 任一 Init Container 失败 -> Pod 失败 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ Running 阶段 │
│ │
│ 1. 启动主容器 (Main Container) │
│ 2. 执行 PostStart Hook (如果有) │
│ 3. 容器运行中 │
│ 4. 定期执行 Liveness/Readiness Probe │
└──────────────────────────────────────────────────────────────────────────────┘

┌───────────────┴───────────────┐
│ │
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ Succeeded 阶段 │ │ Failed 阶段 │
│ │ │ │
│ 所有容器成功退出 │ │ 至少一个容器失败退出 │
│ restartPolicy: Never/OnFailure │ 或被系统终止 │
└──────────────────────────────┘ └──────────────────────────────┘

容器重启策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// staging/src/k8s.io/api/core/v1/types.go

type RestartPolicy string

const (
// 始终重启 (默认)
RestartPolicyAlways RestartPolicy = "Always"

// 失败时重启
RestartPolicyOnFailure RestartPolicy = "OnFailure"

// 从不重启
RestartPolicyNever RestartPolicy = "Never"
)

重启策略行为

重启策略 容器成功退出 容器失败退出 适用场景
Always 重启 重启 长期运行的服务
OnFailure 不重启 重启 批处理任务
Never 不重启 不重启 一次性任务

kubelet 中的 Pod 管理

Pod Worker 流程

  • 文件: pkg/kubelet/pod_workers.go
  • 函数: podWorkers.managePodLoop()
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
┌──────────────────────────────────────────────────────────────────────────────┐
│ Pod 更新事件 │
│ UpdatePod(update.PodWorkType) │
│ │
│ WorkType: │
│ ├── SyncPod: 正常同步 │
│ ├── TerminatingPod: 正在终止 │
│ └── TerminatedPod: 已终止 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ managePodLoop │
│ pkg/kubelet/pod_workers.go:1200 │
│ │
│ switch { │
│ case update.WorkType == TerminatedPod: │
│ err = p.podSyncer.SyncTerminatedPod(ctx, pod, status) │
│ │
│ case update.WorkType == TerminatingPod: │
│ err = p.podSyncer.SyncTerminatingPod(ctx, pod, status, gracePeriod) │
│ │
│ default: │
│ isTerminal, err = p.podSyncer.SyncPod(ctx, updateType, pod, status) │
│ } │
└──────────────────────────────────────────────────────────────────────────────┘

SyncPod 主流程

  • 文件: pkg/kubelet/kubelet.go
  • 函数: Kubelet.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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
┌──────────────────────────────────────────────────────────────────────────────┐
│ syncPod │
│ pkg/kubelet/kubelet.go:1800 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 1. 预检查 │
│ │
│ - 检查 Pod 是否已被删除 │
│ - 检查 Pod 是否可以运行 │
│ - 检查资源是否充足 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 2. 创建 Pod 数据目录 │
│ │
│ - 创建 Pod 目录: /var/lib/kubelet/pods/<uid> │
│ - 创建 Volume 目录 │
│ - 创建 Plugin 目录 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 3. 等待 Volume 挂载 │
│ │
│ - 等待所有 PVC 绑定 │
│ - 挂载卷到 Pod 目录 │
│ - 设置 SELinux 上下文 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 4. 获取 Image Pull Secrets │
│ │
│ - 从 Pod.Spec.ImagePullSecrets 获取 │
│ - 解析 Secret 数据 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 5. 执行 SyncPod │
│ pkg/kubelet/kuberuntime/kuberuntime_manager.go │
│ │
│ a) 计算 Sandbox 和容器变更 │
│ b) 终止需要删除的容器 │
│ c) 创建/更新 Pod Sandbox │
│ d) 启动 Init Container (按顺序) │
│ e) 启动主容器 (并行) │
└──────────────────────────────────────────────────────────────────────────────┘

Init Container 流程

执行顺序

  • 文件: pkg/kubelet/kuberuntime/kuberuntime_manager.go
  • 函数: computePodActions()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──────────────────────────────────────────────────────────────────────────────┐
│ Init Container 执行 │
│ │
│ Pod.Spec.InitContainers: [init-1, init-2, init-3] │
│ │
│ 步骤 1: 执行 init-1 │
│ ├── 创建容器 │
│ ├── 执行 PostStart Hook │
│ ├── 等待容器退出 │
│ └── 检查退出码 (0 = 成功) │
│ │
│ 步骤 2: 执行 init-2 │
│ ├── 创建容器 │
│ └── 等待容器退出 │
│ │
│ 步骤 3: 执行 init-3 │
│ ├── 创建容器 │
│ └── 等待容器退出 │
│ │
│ 所有 Init Container 成功 -> 启动主容器 │
│ 任一 Init Container 失败 -> 根据重启策略处理 │
└──────────────────────────────────────────────────────────────────────────────┘

Init Container 特点

1
2
3
4
5
6
7
8
9
10
spec:
initContainers:
- name: init-db
image: busybox
command: ['sh', '-c', 'until nslookup db-service; do sleep 1; done']
# 特点:
# 1. 按顺序执行
# 2. 必须成功退出
# 3. 不支持 Readiness/Liveness Probe
# 4. 资源按最严格的计算 (取所有 Init Container 的最大值)

容器 Probe

Probe 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// staging/src/k8s.io/api/core/v1/types.go

type Probe struct {
ProbeHandler // 探测方式
InitialDelaySeconds int32 // 初始延迟
TimeoutSeconds int32 // 超时时间
PeriodSeconds int32 // 探测间隔
SuccessThreshold int32 // 成功阈值
FailureThreshold int32 // 失败阈值
TerminationGracePeriodSeconds *int64 // 终止宽限期
}

type ProbeHandler struct {
Exec *ExecAction // 执行命令
HTTPGet *HTTPGetAction // HTTP 请求
TCPSocket *TCPSocketAction // TCP 连接
GRPC *GRPCAction // gRPC 检查
}

Liveness Probe (存活探测)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──────────────────────────────────────────────────────────────────────────────┐
│ Liveness Probe │
│ │
│ 作用: 检测容器是否存活 │
│ 失败: 重启容器 │
│ │
│ 示例: │
│ livenessProbe: │
│ httpGet: │
│ path: /healthz │
│ port: 8080 │
│ initialDelaySeconds: 30 │
│ periodSeconds: 10 │
│ failureThreshold: 3 │
│ │
│ 流程: │
│ 启动后 30s 开始 -> 每 10s 探测一次 -> 连续 3 次失败 -> 重启容器 │
└──────────────────────────────────────────────────────────────────────────────┘

Readiness Probe (就绪探测)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──────────────────────────────────────────────────────────────────────────────┐
│ Readiness Probe │
│ │
│ 作用: 检测容器是否准备好接收流量 │
│ 失败: 从 Service Endpoints 移除 │
│ │
│ 示例: │
│ readinessProbe: │
│ httpGet: │
│ path: /ready │
│ port: 8080 │
│ initialDelaySeconds: 5 │
│ periodSeconds: 5 │
│ failureThreshold: 3 │
│ │
│ 流程: │
│ 启动后 5s 开始 -> 每 5s 探测一次 -> 失败时从 Service 移除 │
└──────────────────────────────────────────────────────────────────────────────┘

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
┌──────────────────────────────────────────────────────────────────────────────┐
│ Pod 删除请求 │
│ kubectl delete pod <name> │
│ 或 DeletionTimestamp 被设置 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 1. API Server 处理删除 │
│ │
│ - 设置 Pod.DeletionTimestamp │
│ - 设置 Pod.DeletionGracePeriodSeconds (默认 30s) │
│ - 更新 etcd │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 2. kubelet 接收更新 │
│ │
│ - 监听到 Pod 更新 │
│ - 检测到 DeletionTimestamp │
│ - 标记 Pod 为 Terminating │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 3. 执行 PreStop Hook │
│ │
│ 如果定义了 lifecycle.preStop: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ lifecycle: │ │
│ │ preStop: │ │
│ │ exec: │ │
│ │ command: ["/bin/sh", "-c", "sleep 15; nginx -s quit"] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ - PreStop 在容器内执行 │
│ - 与 SIGTERM 信号并行 │
│ - 超时时间为 terminationGracePeriodSeconds │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 4. 发送 SIGTERM 信号 │
│ │
│ - 向所有容器发送 SIGTERM │
│ - 容器开始优雅关闭 │
│ - 应用应该: │
│ ├── 停止接收新请求 │
│ ├── 完成处理中的请求 │
│ ├── 关闭数据库连接 │
│ └── 保存状态 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 5. 从 Service 移除 │
│ │
│ - EndpointController 更新 Endpoints │
│ - 从 Service Endpoints 移除 Pod │
│ - 不再转发新流量到该 Pod │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 6. 等待容器退出 │
│ │
│ 等待 terminationGracePeriodSeconds (默认 30s) │
│ │
│ 如果容器在宽限期内退出: │
│ ├── 进入下一步清理 │
│ │ │
│ 如果宽限期到期容器仍未退出: │
│ └── 发送 SIGKILL 信号强制终止 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 7. 清理资源 │
│ │
│ - 卸载卷 │
│ - 释放网络资源 │
│ - 清理 Pod 目录 │
│ - 从 CRI 删除容器 │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 8. 更新 API Server │
│ │
│ - 设置 Pod.Status.Phase = Failed/Succeeded │
│ - 设置 Pod 容器终止状态 │
│ - 移除 Pod (如果非孤儿) │
└──────────────────────────────────────────────────────────────────────────────┘

终止代码

  • 文件: pkg/kubelet/kuberuntime/kuberuntime_container.go
  • 函数: killContainer()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func (m *kubeGenericRuntimeManager) killContainer(
pod *v1.Pod,
containerID containerID,
containerName string,
gracePeriodOverride *int64,
reason string,
message string,
) error {
// 1. 执行 PreStop Hook
if container.Lifecycle != nil && container.Lifecycle.PreStop != nil {
m.runner.Run(containerID, container.Lifecycle.PreStop)
}

// 2. 发送 SIGTERM
m.runtimeService.StopContainer(containerID, gracePeriod)

// 3. 等待容器退出或超时后发送 SIGKILL
}

强制删除 (Force Delete)

1
2
3
4
5
6
7
8
# 强制删除,跳过优雅终止
kubectl delete pod <name> --force --grace-period=0

# 注意:
# 1. 立即从 etcd 删除 Pod
# 2. 不等待容器优雅终止
# 3. 可能导致数据丢失
# 4. StatefulSet 的 PVC 不会自动清理

Pod 状态条件

条件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// staging/src/k8s.io/api/core/v1/types.go

type PodConditionType string

const (
// PodScheduled: Pod 已被调度
PodScheduled PodConditionType = "PodScheduled"

// PodReady: Pod 准备好接收流量
PodReady PodConditionType = "PodReady"

// PodInitialized: Init Container 完成
PodInitialized PodConditionType = "PodInitialized"

// Unschedulable: 无法调度
Unschedulable PodConditionType = "Unschedulable"

// ContainersReady: 所有容器就绪
ContainersReady PodConditionType = "ContainersReady"
)

条件状态

1
2
3
4
5
6
7
type ConditionStatus string

const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)

Phase 计算逻辑

  • 文件: pkg/kubelet/status/status_manager.go
  • 函数: generatePodPhase()
1
2
3
4
5
6
7
8
9
10
11
func generatePodPhase(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodPhase {
// 1. 如果所有容器未运行且 Pod 有 RestartPolicyAlways -> Running
// 2. 如果至少一个容器正在运行 -> Running
// 3. 如果所有容器成功退出:
// - RestartPolicyNever/OnFailure -> Succeeded
// - RestartPolicyAlways -> Running (等待重启)
// 4. 如果至少一个容器失败退出:
// - RestartPolicyNever -> Failed
// - RestartPolicyOnFailure -> Running (等待重启)
// - RestartPolicyAlways -> Running
}

关键代码路径

文件 说明
pkg/kubelet/kubelet.go kubelet 主逻辑,syncPod
pkg/kubelet/pod_workers.go Pod Worker 管理
pkg/kubelet/status/status_manager.go Pod 状态管理
pkg/kubelet/kuberuntime/kuberuntime_manager.go 容器运行时管理
pkg/kubelet/kuberuntime/kuberuntime_container.go 容器操作
staging/src/k8s.io/api/core/v1/types.go Pod API 类型

常见问题排查

1. Pod 卡在 Pending

1
2
3
4
5
6
7
8
# 检查事件
kubectl describe pod <name>

# 常见原因
# - 资源不足
# - PVC 未绑定
# - 节点选择器不匹配
# - Taint/Toleration 不匹配

2. Pod 卡在 Terminating

1
2
3
4
5
6
7
8
# 检查容器状态
kubectl describe pod <name>

# 检查 kubelet 日志
journalctl -u kubelet | grep <pod-name>

# 强制删除 (谨慎使用)
kubectl delete pod <name> --force --grace-period=0

3. Init Container 失败

1
2
3
4
5
# 查看 Init Container 日志
kubectl logs <pod-name> -c <init-container-name>

# 检查退出码
kubectl get pod <name> -o jsonpath='{.status.initContainerStatuses}'

4. 健康检查失败

1
2
3
4
5
# 检查 Probe 配置
kubectl describe pod <name>

# 手动测试健康检查
kubectl exec <pod-name> -- curl -v http://localhost:8080/healthz

最佳实践

1. 合理设置资源

1
2
3
4
5
6
7
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi

2. 配置健康检查

1
2
3
4
5
6
7
8
9
10
11
12
13
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

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

3. 优雅终止

1
2
3
4
5
6
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15; nginx -s quit"]

terminationGracePeriodSeconds: 60

4. Init Container 使用

1
2
3
4
initContainers:
- name: wait-for-db
image: busybox
command: ['sh', '-c', 'until nc -z db-service 5432; do sleep 1; done']

面试题

基础题

1. Pod 有哪些生命周期阶段?

参考答案:

阶段 说明
Pending Pod 已被接受,但容器尚未启动(调度中、拉取镜像)
Running Pod 已绑定节点,所有容器已创建,至少一个正在运行
Succeeded 所有容器成功终止(exit code 0),不会重启
Failed 所有容器终止,至少一个失败(exit code 非 0)
Unknown 无法获取状态(通常是通信问题)

2. Pod 的重启策略有哪些?

参考答案:

策略 容器成功退出 容器失败退出
Always 重启 重启
OnFailure 不重启 重启
Never 不重启 不重启

默认值:Deployment/ReplicaSet 为 Always,Job 为 OnFailure

3. Init Container 和普通 Container 有什么区别?

参考答案:

特性 Init Container 主 Container
执行顺序 先执行,按顺序 后执行,并行
必须成功 否(取决于重启策略)
健康检查 不支持 支持
资源计算 取最大值 独立计算
用途 初始化、等待依赖 运行应用

中级题

4. Init Container 的执行流程是怎样的?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Pod 创建


Init Container 1 执行

├── 成功 -> Init Container 2 执行
│ │
│ ├── 成功 -> Init Container 3 ...
│ │
│ └── 失败 -> 根据重启策略处理

└── 失败 -> 根据重启策略处理

所有 Init Container 成功


主 Container 并行启动

代码位置pkg/kubelet/kuberuntime/kuberuntime_manager.go:890 - findNextInitContainerToRun()

5. Liveness Probe 和 Readiness Probe 有什么区别?

参考答案:

特性 Liveness Readiness
目的 检测容器是否存活 检测是否可接收流量
失败后果 重启容器 从 Service 移除
影响范围 容器本身 Service Endpoints
启动时 等待 initialDelaySeconds 等待 initialDelaySeconds

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

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

6. Startup Probe 的作用是什么?

参考答案:
Startup Probe 用于慢启动应用,在应用完全启动前禁用 Liveness Probe。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 允许失败 30 次
periodSeconds: 10 # 每 10 秒检查一次
# 总共允许 30 * 10 = 300 秒启动时间

livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
# Startup Probe 成功后才开始检查

好处

  • 避免慢启动应用被误杀
  • 无需设置过长的 livenessProbe.initialDelaySeconds

7. 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
1. 删除请求 -> API Server 设置 deletionTimestamp


2. kubelet 检测到 deletionTimestamp


3. 从 Service Endpoints 移除 Pod


4. 执行 PreStop Hook(如果有)


5. 发送 SIGTERM 信号给所有容器


6. 等待 terminationGracePeriodSeconds(默认 30s)

├── 容器退出 -> 进入清理

└── 超时 -> 发送 SIGKILL 强制终止


7. 清理资源(卸载卷、释放网络)


8. 从 API Server 删除 Pod

高级题

8. PreStop Hook 的执行时机和注意事项?

参考答案:

执行时机

  • 在发送 SIGTERM 之前执行
  • 与 SIGTERM 信号处理并行(不是阻塞)
  • 在容器内执行

注意事项

1
2
3
4
5
6
7
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15; nginx -s quit"]

# 时间计算
# PreStop 执行时间 + 应用优雅关闭时间 <= terminationGracePeriodSeconds

关键点

  1. PreStop 超时包含在 terminationGracePeriodSeconds 内
  2. PreStop 失败不影响 Pod 终止
  3. 容器已崩溃时 PreStop 不会执行

9. 如何实现零停机部署?

参考答案:

1. 正确配置 Readiness Probe

1
2
3
4
5
6
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

2. 使用 PreStop Hook

1
2
3
4
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]

3. 设置合适的 grace period

1
terminationGracePeriodSeconds: 60

4. 配置 PDB

1
2
3
4
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 1

5. 使用滚动更新策略

1
2
3
4
5
6
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 零不可用

10. Pod Phase 的计算逻辑是怎样的?

参考答案:

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
// pkg/kubelet/status/status_manager.go

func generatePodPhase(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodPhase {
// 1. 检查容器状态
allRunning := true
allSucceeded := true
anyFailed := false

for _, container := range podStatus.ContainerStatuses {
switch container.State {
case Running:
allSucceeded = false
case Exited:
if container.ExitCode == 0 {
allRunning = false
} else {
allSucceeded = false
anyFailed = true
}
}
}

// 2. 确定最终 Phase
if allRunning && len(podStatus.ContainerStatuses) > 0 {
return v1.PodRunning
}
if allSucceeded {
// 根据重启策略决定
if pod.Spec.RestartPolicy == v1.RestartPolicyNever {
return v1.PodSucceeded
}
return v1.PodRunning // 等待重启
}
if anyFailed {
if pod.Spec.RestartPolicy == v1.RestartPolicyNever {
return v1.PodFailed
}
return v1.PodRunning // 等待重启
}
return v1.PodPending
}

11. 容器重启的退避策略是怎样的?

参考答案:
Kubernetes 使用指数退避策略重启容器:

1
2
3
4
5
6
7
8
9
重启间隔 = min(backoff * 2^(restartCount-1), maxBackoff)

示例(默认 maxBackoff = 5m):
第 1 次重启:10s 后
第 2 次重启:20s 后
第 3 次重启:40s 后
第 4 次重启:80s 后
...
第 N 次重启:5m 后(最大值)

相关参数

1
2
kubelet --container-max-backoff=5m  # 最大退避时间
kubelet --container-min-backoff=10s # 最小退避时间

重置条件

  • 容器运行超过 10 分钟,restartCount 重置为 0

12. Sidecar 容器的终止顺序是怎样的?

参考答案:
Kubernetes 1.28+ 支持 Sidecar 容器,有明确的终止顺序:

1
2
3
4
5
containers:
- name: main
# 主容器
- name: sidecar
restartPolicy: Always # 标记为 Sidecar

终止顺序

1
2
3
4
5
1. 发送 SIGTERM 给主容器
2. 等待主容器退出
3. 发送 SIGTERM 给 Sidecar 容器(按启动逆序)
4. 等待 Sidecar 退出
5. 清理资源

实现pkg/kubelet/kuberuntime/kuberuntime_termination_order.go

好处

  • Sidecar 可以收集主容器的日志/指标直到主容器完全退出
  • 保证依赖关系正确处理

场景题

13. Pod 卡在 Terminating 状态如何处理?

参考答案:

1. 诊断步骤

1
2
3
4
5
6
7
8
# 查看 Pod 详情
kubectl describe pod <pod-name>

# 查看 kubelet 日志
journalctl -u kubelet | grep <pod-name>

# 检查 Finalizers
kubectl get pod <pod-name> -o jsonpath='{.metadata.finalizers}'

2. 常见原因

  • Finalizer 未移除:如 PV 保护
  • kubelet 无响应:节点问题
  • 容器无法停止:进程僵死
  • 网络问题:无法与 API Server 通信

3. 解决方法

1
2
3
4
5
6
7
8
# 方法 1:移除 Finalizer
kubectl patch pod <pod-name> -p '{"metadata":{"finalizers":[]}}'

# 方法 2:强制删除(最后手段)
kubectl delete pod <pod-name> --force --grace-period=0

# 方法 3:重启 kubelet
systemctl restart kubelet

14. 如何排查容器频繁重启的问题?

参考答案:

1. 查看重启历史

1
2
3
4
kubectl describe pod <pod-name>
# 查看 Last State 和 Restart Count

kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].restartCount}'

2. 查看退出日志

1
kubectl logs <pod-name> --previous

3. 常见原因和解决

原因 诊断方法 解决
OOM 查看 exit code 137 增加内存限制
Liveness 失败 检查 probe 配置 调整阈值或增加超时
应用崩溃 查看日志 修复应用 bug
资源不足 检查节点资源 扩容或优化
依赖不可用 检查网络/服务 增加重试逻辑

15. 如何设计一个优雅关闭的应用?

参考答案:

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
// Go 示例
func main() {
// 监听 SIGTERM
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

// 启动服务
server := startServer()

// 等待信号
<-sigChan
log.Println("收到终止信号,开始优雅关闭...")

// 创建关闭超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

// 1. 停止接收新请求
server.StopAcceptingNewConnections()

// 2. 等待现有请求完成
server.WaitForExistingRequests(ctx)

// 3. 关闭数据库连接
db.Close()

// 4. 清理其他资源
cleanup()

log.Println("优雅关闭完成")
}

2. Kubernetes 配置

1
2
3
4
5
6
7
8
9
10
11
12
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"] # 给负载均衡器时间更新
readinessProbe:
httpGet:
path: /ready
port: 8080

3. 健康检查端点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// /ready - 是否可接收流量
// /health - 是否存活

var ready atomic.Bool

func readyHandler(w http.ResponseWriter, r *http.Request) {
if ready.Load() {
w.WriteHeader(200)
} else {
w.WriteHeader(503)
}
}

func shutdown() {
ready.Store(false) // 立即标记为不就绪
// ... 清理逻辑
}

16. 如何处理长时间运行的任务不被中断?

参考答案:

1. 使用 Job 而非 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: batch/v1
kind: Job
metadata:
name: long-task
spec:
backoffLimit: 0
activeDeadlineSeconds: 86400 # 最长运行 24 小时
template:
spec:
containers:
- name: worker
image: worker:v1
restartPolicy: Never

2. 增加 terminationGracePeriodSeconds

1
2
spec:
terminationGracePeriodSeconds: 3600 # 1 小时

3. 使用 PreStop Hook 保存状态

1
2
3
4
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "save-progress.sh"]

4. 检查点机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定期保存进度
func runTask() {
for {
doWork()
saveCheckpoint()

if isShuttingDown() {
saveCheckpoint()
return
}
}
}

// 重启时恢复
func resume() {
checkpoint := loadCheckpoint()
continueFrom(checkpoint)
}