StatefulSet 控制器流程

概述

StatefulSet 用于管理有状态应用,提供有序部署、稳定网络标识和持久化存储。与 Deployment 不同,StatefulSet 保证 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
┌──────────────────────────────────────────────────────────────────────────────┐
│ StatefulSetController │
│ pkg/controller/statefulset/stateful_set.go │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ kubeClient │ │ setLister │ │ podLister │ │ queue │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ StatefulSetControlInterface │
│ pkg/controller/statefulset/stateful_set_control.go │
│ │
│ - sync() 协调 StatefulSet │
│ - updateStatefulSet() 更新状态 │
│ - adoptOrphanRevisions() 采纳孤儿 Revision │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ StatefulPodControl │
│ pkg/controller/statefulset/stateful_pod_control.go │
│ │
│ - createPod() 创建 Pod │
│ - deletePod() 删除 Pod │
│ - updatePod() 更新 Pod │
│ - createPersistentVolumeClaims() 创建 PVC │
└──────────────────────────────────────────────────────────────────────────────┘

核心数据结构

StatefulSet API 类型

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/api/apps/v1/types.go

type StatefulSetSpec struct {
Replicas *int32 // 副本数
Selector *metav1.LabelSelector // Pod 选择器
Template v1.PodTemplateSpec // Pod 模板
VolumeClaimTemplates []v1.PersistentVolumeClaim // PVC 模板
ServiceName string // Headless Service 名称
PodManagementPolicy PodManagementPolicyType // Pod 管理策略
UpdateStrategy StatefulSetUpdateStrategy // 更新策略
RevisionHistoryLimit *int32 // 历史版本限制
MinReadySeconds int32 // 最小就绪时间
}

type StatefulSetUpdateStrategy struct {
Type StatefulSetUpdateStrategyType // RollingUpdate/OnDelete
RollingUpdate *RollingUpdateStatefulSetStrategy
}

type RollingUpdateStatefulSetStrategy struct {
Partition *int32 // 分区更新
MaxUnavailable *intstr.IntOrString
}

Pod 管理策略

1
2
3
4
5
6
7
8
9
type PodManagementPolicyType string

const (
// 默认:有序创建/删除
OrderedReady PodManagementPolicyType = "OrderedReady"

// 并行创建/删除
Parallel PodManagementPolicyType = "Parallel"
)

控制器工作流程

主循环

  • 文件: pkg/controller/statefulset/stateful_set.go
  • 函数: StatefulSetController.Run() (第 152 行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──────────────────────────────────────────────────────────────────────────────┐
│ 控制器启动 │
│ StatefulSetController.Run(ctx, workers) │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 等待缓存同步 │
│ WaitForCacheSync(podListerSynced, setListerSynced, │
│ pvcListerSynced, revListerSynced) │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 启动 Worker │
│ for i := 0; i < workers; i++ │
│ go wait.Until(worker, time.Second) │
└──────────────────────────────────────────────────────────────────────────────┘

单次调谐流程

  • 函数: StatefulSetController.sync() -> defaultStatefulSetControl.sync()
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
┌──────────────────────────────────────────────────────────────────────────────┐
│ 从队列获取 StatefulSet │
│ queue.Get() -> key │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 获取 StatefulSet 和关联资源 │
│ │
│ 1. 获取 StatefulSet 对象 │
│ 2. 获取关联的 Pod (通过 selector) │
│ 3. 获取关联的 PVC │
│ 4. 获取 ControllerRevisions │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 检查并创建 Headless Service │
│ │
│ - StatefulSet 必须关联一个 Headless Service │
│ - Service 提供 Pod 的网络标识 │
│ - 格式: <statefulset-name>-<ordinal>.<service-name>.<namespace>.svc │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 更新状态 │
│ │
│ - 计算当前副本数 │
│ - 计算就绪副本数 │
│ - 计算当前版本 │
│ - 更新 StatefulSetStatus │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 执行协调逻辑 │
│ │
│ 根据当前状态选择操作: │
│ ├── 缩容 -> 删除多余的 Pod (逆序) │
│ ├── 扩容 -> 创建新的 Pod (顺序) │
│ └── 更新 -> 滚动更新 Pod │
└──────────────────────────────────────────────────────────────────────────────┘

有序部署/删除流程

OrderedReady 策略 (默认)

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
┌──────────────────────────────────────────────────────────────────────────────┐
│ 创建 Pod (扩容) │
│ │
│ StatefulSet: web, Replicas: 3 │
│ │
│ 步骤 1: 创建 web-0 │
│ ├── 创建 PVC: www-web-0 │
│ ├── 创建 Pod: web-0 │
│ └── 等待 web-0 Running && Ready │
│ │
│ 步骤 2: 创建 web-1 │
│ ├── 创建 PVC: www-web-1 │
│ ├── 创建 Pod: web-1 │
│ └── 等待 web-1 Running && Ready │
│ │
│ 步骤 3: 创建 web-2 │
│ ├── 创建 PVC: www-web-2 │
│ ├── 创建 Pod: web-2 │
│ └── 等待 web-2 Running && Ready │
└──────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────────┐
│ 删除 Pod (缩容) │
│ │
│ StatefulSet: web, Replicas: 1 (从 3 缩容) │
│ │
│ 步骤 1: 删除 web-2 │
│ └── 等待 web-2 完全终止 │
│ │
│ 步骤 2: 删除 web-1 │
│ └── 等待 web-1 完全终止 │
│ │
│ 结果: 只保留 web-0 │
│ 注意: PVC 不会被删除 │
└──────────────────────────────────────────────────────────────────────────────┘

Parallel 策略

1
2
3
4
5
6
7
8
9
10
11
12
┌──────────────────────────────────────────────────────────────────────────────┐
│ 并行创建/删除 │
│ │
│ StatefulSet: web, Replicas: 3, PodManagementPolicy: Parallel │
│ │
│ 同时创建: │
│ ├── web-0 (创建 PVC: www-web-0, 创建 Pod) │
│ ├── web-1 (创建 PVC: www-web-1, 创建 Pod) │
│ └── web-2 (创建 PVC: www-web-2, 创建 Pod) │
│ │
│ 不等待前一个 Pod 就绪 │
└──────────────────────────────────────────────────────────────────────────────┘

滚动更新流程

RollingUpdate 策略

  • 文件: pkg/controller/statefulset/stateful_set_control.go
  • 函数: rollingUpdate()
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
┌──────────────────────────────────────────────────────────────────────────────┐
│ 滚动更新触发 │
│ StatefulSet Spec 变更 (Pod 模板更新) │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 创建新的 ControllerRevision │
│ │
│ - 记录新的 Pod 模板 │
│ - 更新 StatefulSet 的 currentRevision │
│ - 保留历史版本 (受 revisionHistoryLimit 限制) │
└──────────────────────────────────────────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────────────────┐
│ 逆序更新 Pod │
│ │
│ StatefulSet: web, Replicas: 3, Partition: 0 │
│ │
│ 步骤 1: 更新 web-2 │
│ ├── 删除旧 Pod web-2 │
│ ├── 等待 PVC 就绪 (如果更新了) │
│ ├── 使用新模板创建 web-2 │
│ └── 等待 web-2 Running && Ready │
│ │
│ 步骤 2: 更新 web-1 │
│ ├── 删除旧 Pod web-1 │
│ ├── 等待 PVC 就绪 │
│ ├── 使用新模板创建 web-1 │
│ └── 等待 web-1 Running && Ready │
│ │
│ 步骤 3: 更新 web-0 │
│ ├── 删除旧 Pod web-0 │
│ ├── 等待 PVC 就绪 │
│ ├── 使用新模板创建 web-0 │
│ └── 等待 web-0 Running && Ready │
└──────────────────────────────────────────────────────────────────────────────┘

Partition 分区更新

1
2
3
4
5
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只有 ordinal >= 2 的 Pod 会更新
1
2
3
4
5
6
7
8
9
StatefulSet: web, Replicas: 4, Partition: 2

更新前:
web-0 (v1), web-1 (v1), web-2 (v1), web-3 (v1)

更新后 (新版本 v2):
web-0 (v1), web-1 (v1), web-2 (v2), web-3 (v2)

只有 ordinal >= 2 的 Pod 被更新

稳定网络标识

Pod 域名格式

1
2
3
4
5
<pod-name>.<service-name>.<namespace>.svc.cluster.local

示例:
web-0.headless-service.default.svc.cluster.local
web-1.headless-service.default.svc.cluster.local

Headless Service 配置

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: headless-service
spec:
clusterIP: None # Headless
selector:
app: web
ports:
- port: 80

DNS 记录

1
2
3
4
5
6
7
# 每个 Pod 获得一个 A 记录
web-0.headless-service.default.svc.cluster.local -> 10.244.1.5
web-1.headless-service.default.svc.cluster.local -> 10.244.1.6
web-2.headless-service.default.svc.cluster.local -> 10.244.1.7

# Service 本身也有记录 (指向所有 Pod)
headless-service.default.svc.cluster.local -> 10.244.1.5, 10.244.1.6, 10.244.1.7

PVC 管理

PVC 创建

  • 文件: pkg/controller/statefulset/stateful_pod_control.go
  • 函数: createPersistentVolumeClaims()
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
┌──────────────────────────────────────────────────────────────────────────────┐
│ PVC 命名规则 │
│ │
│ <volumeclaim-name>-<statefulset-name>-<ordinal> │
│ │
│ 示例: www-web-0, www-web-1, www-web-2 │
└──────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────────┐
│ PVC 生命周期 │
│ │
│ 创建 Pod 前: │
│ 1. 检查 PVC 是否存在 │
│ 2. 如果不存在,从 VolumeClaimTemplate 创建 │
│ 3. 等待 PVC 绑定 (Bound) │
│ 4. 创建 Pod │
│ │
│ 删除 Pod 时: │
│ - PVC 不会被删除 │
│ - 数据被保留 │
│ - Pod 重新创建时绑定同一个 PVC │
│ │
│ 删除 StatefulSet 时: │
│ - PVC 不会被删除 │
│ - 需要手动清理 │
└──────────────────────────────────────────────────────────────────────────────┘

VolumeClaimTemplate 示例

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: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: headless-service
replicas: 3
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 1Gi
template:
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html

关键代码路径

文件 说明
pkg/controller/statefulset/stateful_set.go StatefulSet 控制器
pkg/controller/statefulset/stateful_set_control.go 协调逻辑
pkg/controller/statefulset/stateful_pod_control.go Pod 操作
pkg/controller/statefulset/stateful_pod_status_updater.go 状态更新
staging/src/k8s.io/api/apps/v1/types.go API 类型

常见问题排查

1. Pod 卡在 Pending

1
2
3
4
5
6
7
8
9
10
# 检查 PVC 状态
kubectl get pvc

# 检查事件
kubectl describe pod <pod-name>

# 常见原因
# - PVC 未绑定
# - 前一个 Pod 未就绪
# - 资源不足

2. 滚动更新卡住

1
2
3
4
5
6
7
8
# 检查 StatefulSet 状态
kubectl describe statefulset <name>

# 检查 Partition 设置
kubectl get statefulset <name> -o yaml | grep partition -A 2

# 手动暂停更新
# 设置 partition 为 replicas

3. PVC 数据丢失

1
2
3
4
5
6
7
8
# 检查 PVC 绑定
kubectl get pvc

# 检查 PV 回收策略
kubectl get pv

# 注意: 删除 StatefulSet 不会删除 PVC
# 需要手动删除 PVC 才能释放 PV

4. 网络标识问题

1
2
3
4
5
6
7
8
# 检查 Headless Service
kubectl get svc <service-name>

# 检查 DNS 解析
kubectl exec -it <pod> -- nslookup web-0.headless-service

# 检查 Endpoints
kubectl get endpoints <service-name>

配置示例

完整 StatefulSet

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
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: nginx-headless
replicas: 3
selector:
matchLabels:
app: nginx
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 1Gi
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html

面试题

基础题

1. StatefulSet 与 Deployment 有什么区别?

参考答案:

特性 StatefulSet Deployment
Pod 标识 稳定(web-0, web-1) 随机(web-7f8b9c-x5k2)
启动顺序 有序(0→1→2) 并行
存储 每个Pod独立PVC 共享或无
网络标识 稳定域名
扩缩容 有序 并行
适用场景 有状态应用 无状态应用

2. StatefulSet 的典型应用场景有哪些?

参考答案:

  • 数据库:MySQL、PostgreSQL、MongoDB
  • 消息队列:Kafka、RabbitMQ、Pulsar
  • 缓存:Redis Cluster、Memcached
  • 分布式存储:Elasticsearch、Cassandra、Ceph
  • 协调服务:ZooKeeper、etcd

3. StatefulSet Pod 的命名规则是什么?

参考答案:

1
2
3
4
5
6
<statefulset-name>-<ordinal>

示例:
web-0, web-1, web-2
mysql-0, mysql-1, mysql-2
kafka-0, kafka-1, kafka-2

特点

  • ordinal 从 0 开始
  • 连续递增
  • 删除后重建保留原名称

中级题

4. 解释 StatefulSet 的有序部署流程。

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StatefulSet: web, Replicas: 3

步骤 1: 创建 web-0
├── 创建 PVC www-web-0
├── 创建 Pod web-0
└── 等待 web-0 Running && Ready

步骤 2: 创建 web-1
├── 创建 PVC www-web-1
├── 创建 Pod web-1
└── 等待 web-1 Running && Ready

步骤 3: 创建 web-2
├── 创建 PVC www-web-2
├── 创建 Pod web-2
└── 等待 web-2 Running && Ready

关键代码pkg/controller/statefulset/stateful_set_control.go:365 - processReplica()

5. StatefulSet 的 Headless Service 有什么作用?

参考答案:

1. DNS 记录

1
2
3
web-0.headless-service.default.svc.cluster.local -> 10.244.1.5
web-1.headless-service.default.svc.cluster.local -> 10.244.1.6
web-2.headless-service.default.svc.cluster.local -> 10.244.1.7

2. 网络标识

1
2
3
4
5
6
7
8
9
10
11
# Headless Service 配置
apiVersion: v1
kind: Service
metadata:
name: headless-service
spec:
clusterIP: None # 关键:设置为 None
selector:
app: web
ports:
- port: 80

6. StatefulSet 的 PVC 管理机制是怎样的?

参考答案:

PVC 命名规则

1
2
3
4
5
<volumeclaim-name>-<statefulset-name>-<ordinal>

示例:
www-web-0, www-web-1, www-web-2
data-mysql-0, data-mysql-1

生命周期

1
2
3
4
5
6
7
创建 Pod -> 创建 PVC -> 绑定 PV -> 创建 Pod

独立生命周期

删除 Pod -> PVC 保留
删除 StatefulSet -> PVC 保留
手动删除 PVC -> PV 回收

7. StatefulSet 的两种 Pod 管理策略有什么区别?

参考答案:

策略 OrderedReady Parallel
启动 顺序,等待前一个就绪 并行
终止 逆序,等待前一个终止 并行
扩缩 有序 并行
适用场景 需要严格顺序 可并行初始化

配置示例

1
2
spec:
podManagementPolicy: Parallel # 或 OrderedReady(默认)

高级题

8. StatefulSet 滚动更新的流程是怎样的?

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
StatefulSet: web, Replicas: 3

步骤 1: 更新 web-2(最高 ordinal)
├── 删除 Pod web-2
├── 使用新模板创建 web-2
└── 等待 Running && Ready

步骤 2: 更新 web-1
├── 删除 Pod web-1
├── 使用新模板创建 web-1
└── 等待 Running && Ready

步骤 3: 更新 web-0(最低 ordinal)
├── 删除 Pod web-0
├── 使用新模板创建 web-0
└── 等待 Running && Ready

关键点

  • 逆序更新:从最高 ordinal 开始
  • 一次一个:等待前一个就绪后才更新下一个
  • 保留存储:删除 Pod 不会删除 PVC

9. 解释 Partition 参数的作用。

参考答案:
Partition 用于金丝雀发布,只更新 ordinal >= partition 的 Pod。

1
2
3
4
5
6
spec:
replicas: 5
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 3 # 只有 web-3, web-4 会被更新

示例

1
2
3
4
Partition: 3, Replicas: 5

更新前: web-0(v1), web-1(v1), web-2(v1), web-3(v1), web-4(v1)
更新后: web-0(v1), web-1(v1), web-2(v1), web-3(v2), web-4(v2)

10. 如何实现 StatefulSet 的零停机更新?

参考答案:

1. 使用 OnDelete 策略

1
2
3
spec:
updateStrategy:
type: OnDelete # 手动删除 Pod 才更新

2. 使用 PreStop Hook

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

3. 使用 PodDisruptionBudget

1
2
3
4
5
6
7
8
9
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web

11. StatefulSet 缩容时 PVC 会怎样?如何实现自动清理?

参考答案:

默认行为:PVC 不会被删除,数据保留。

自动清理配置(Kubernetes 1.23+):

1
2
3
4
5
6
7
8
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # StatefulSet 删除时:Retain 或 Delete
whenScaled: Delete # 缩容时:Retain 或 Delete

场景题

12. 如何部署一个高可用的 MySQL 集群?

参考答案:

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
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
# 设置 server-id
[[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# 主从配置
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/master.cnf /mnt/conf.d/
else
cp /mnt/config-map/slave.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql-read
spec:
selector:
app: mysql
ports:
- port: 3306

关键设计点

  • 使用 Init Container 动态配置 server-id
  • ordinal=0 为主节点,其他为从节点
  • 读写分离通过不同 Service 实现

13. StatefulSet 更新卡住如何排查?

参考答案:

1. 检查 StatefulSet 状态

1
kubectl describe statefulset <name>

2. 检查 Pod 状态

1
2
kubectl get pods -l app=<app> -o wide
kubectl describe pod <pod-name>

3. 常见卡住原因

  • 镜像拉取失败:检查 imagePullPolicy 和 secret
  • PVC 未绑定:检查 storageClass 和 PV
  • 健康检查失败:检查 liveness/readiness probe
  • 前一个 Pod 未就绪:OrderedReady 策略会等待

14. 如何实现 StatefulSet 的蓝绿部署?

参考答案:

方案 1:使用 Partition 分阶段

1
2
3
4
5
6
7
# 阶段 1:只更新 1 个 Pod(金丝雀)
kubectl patch statefulset web --type='json' \
-p='[{"op":"replace","path":"/spec/updateStrategy/rollingUpdate/partition","value":2}]'

# 验证通过后,继续更新
kubectl patch statefulset web --type='json' \
-p='[{"op":"replace","path":"/spec/updateStrategy/rollingUpdate/partition","value":0}]'

15. 如何处理 StatefulSet 节点故障?

参考答案:

手动处理

1
2
3
4
5
6
7
# 1. 确认节点不可恢复
kubectl get nodes

# 2. 强制删除 Pod(从故障节点)
kubectl delete pod <pod-name> --force --grace-period=0

# 3. Pod 会在新节点重建,使用原有 PVC

最佳实践

1
2
3
4
5
6
7
8
9
# 使用 podAntiAffinity 分散部署
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: kubernetes.io/hostname