tags:
– Kubernetes
– K8s系列
– DevOps
– StatefulSet
– 有状态应用
– 存储
– 容器编排
K8s 系列 | 第 10 天:StatefulSet:有状态应用的部署与管理
第 10/30 天
引言
在前面的文章中,我们学习了 Deployment 和 ReplicaSet 如何管理无状态应用——这些 Pod 可以随时被销毁、重建、迁移,像极了”一次性筷子”。然而,现实世界中有大量有状态应用:数据库(MySQL、PostgreSQL、Redis)、消息队列(Kafka、RabbitMQ)、分布式存储(Cassandra、Elasticsearch)等。这些应用要求:
- 每个 Pod 实例有稳定的网络标识(即使重启也不变)
- 每个 Pod 绑定独立的持久化存储
- Pod 按顺序启动、更新、销毁
Kubernetes 为此提供了 StatefulSet 控制器。今天,我们就来深入探讨 StatefulSet 的工作原理、核心特性与实战部署。
核心概念
StatefulSet vs Deployment
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 标识 | 随机名称(如 app-5d7f8c9b6-x3k2p) |
有序名称(如 web-0, web-1, web-2) |
| 存储 | 共享卷或临时卷 | 每个 Pod 绑定独立 PVC |
| 启停顺序 | 无序并行 | 顺序启停(0→1→2,2→1→0) |
| 网络标识 | Pod IP(不稳定) | 稳定 DNS 名称(如 web-0.svc.ns.svc.cluster.local) |
| 适用场景 | 无状态应用 | 有状态应用 |
核心特性详解
- 有序、优雅的部署与扩缩
- 创建时从
0到N-1依次启动 - 缩容时从
N-1到0依次终止 -
每个 Pod 的
READY状态确认后才启动下一个 -
稳定的网络标识
- 每个 Pod 名称固定:
<statefulset-name>-<ordinal> - 配合 Headless Service(
ClusterIP: None)实现稳定的 DNS 解析 -
Pod 重启后 IP 可能变,但 DNS 名称不变
-
稳定的持久化存储
- 基于
volumeClaimTemplates自动创建 PVC - 每个 Pod 绑定专属 PV,Pod 重建后挂载同一存储
- Pod 删除不会清除 PVC,确保数据安全
Pod 名称模式
当创建一个名为 mysql、副本数为 3 的 StatefulSet 时:
- Pod 名称:
mysql-0,mysql-1,mysql-2 - DNS:
mysql-0.mysql.default.svc.cluster.local - PVC 名称:
data-mysql-0,data-mysql-1,data-mysql-2
实战步骤
1. 部署 Headless Service
StatefulSet 必须配合 Headless Service 使用,为每个 Pod 提供稳定的 DNS 入口。
# headless-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
name: mysql
clusterIP: None # Headless Service 关键字段
selector:
app: mysql
2. 创建 StatefulSet
下面部署一个 MySQL 主从架构的 StatefulSet:
# statefulset-mysql.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql # 关联 Headless Service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_REPLICATION_USER
value: "repl_user"
- name: MYSQL_REPLICATION_PASSWORD
value: "repl_password"
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readinessProbe:
exec:
command:
- mysqladmin
- ping
initialDelaySeconds: 10
timeoutSeconds: 5
volumeClaimTemplates: # 自动为每个 Pod 创建 PVC
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: standard # 使用默认 StorageClass(前文第9天内容)
resources:
requests:
storage: 10Gi
3. 创建 Secret(用于 MySQL 密码)
# mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
root-password: cHJvZDJkYjIwMjY= # base64 编码的密码
4. 部署并验证
# 1. 创建命名空间(可选)
kubectl create namespace database
# 2. 部署 Secret
kubectl apply -f mysql-secret.yaml -n database
# 3. 部署 Headless Service
kubectl apply -f headless-svc.yaml -n database
# 4. 部署 StatefulSet
kubectl apply -f statefulset-mysql.yaml -n database
# 5. 观察 Pod 有序创建(逐个启动)
kubectl get pods -n database -w
预期输出顺序如下:
mysql-0 0/1 Pending 0 0s
mysql-0 0/1 Pending 0 2s
mysql-0 0/1 ContainerCreating 0 2s
mysql-0 1/1 Running 0 30s
mysql-1 0/1 Pending 0 1s ← 等待 mysql-0 Ready 后才启动
mysql-1 0/1 ContainerCreating 0 2s
mysql-1 1/1 Running 0 35s
mysql-2 0/1 Pending 0 1s ← 等待 mysql-1 Ready 后才启动
mysql-2 0/1 ContainerCreating 0 2s
mysql-2 1/1 Running 0 40s
5. 验证稳定网络标识
# 进入一个临时 Pod 测试 DNS 解析
kubectl run -it --rm dns-test --image=busybox:1.28 -- sh
# 在容器内执行
nslookup mysql-0.mysql.database.svc.cluster.local
# 输出: Name: mysql-0.mysql.database.svc.cluster.local
# Address: 10.244.1.10
nslookup mysql-1.mysql.database.svc.cluster.local
# 输出: Name: mysql-1.mysql.database.svc.cluster.local
# Address: 10.244.2.11
即使 Pod 被删除重建,DNS 名称保持不变,新的 Pod 会以相同的序号重新接管。
6. 扩缩容操作
# 扩容到 5 个副本
kubectl scale statefulset mysql --replicas=5 -n database
# 观察:mysql-3 启动 → mysql-4 启动(有序扩展)
# 缩容到 2 个副本
kubectl scale statefulset mysql --replicas=2 -n database
# 观察:mysql-4 终止 → mysql-3 终止 → mysql-2 终止(从高序号到低序号)
7. 滚动更新
# 更新策略:滚动更新 + 分区更新
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 只更新序号 >= 2 的 Pod
# 触发滚动更新(修改 image 标签)
kubectl set image statefulset/mysql mysql=mysql:8.0.35 -n database
# 查看更新状态
kubectl rollout status statefulset/mysql -n database
常见问题
Q1:StatefulSet 删除后数据会丢失吗?
不会。 StatefulSet 删除时不会自动删除关联的 PVC。需要手动清理:
# 先删除 StatefulSet
kubectl delete statefulset mysql -n database
# 手动删除 PVC(确认数据不需要后)
kubectl delete pvc -l app=mysql -n database
Q2:如何实现 MySQL 主从自动配置?
可以在 StatefulSet 的 Pod 中通过 $HOSTNAME 环境变量判断角色:
# mysql-0 作为主库
if [[ "$HOSTNAME" == "mysql-0" ]]; then
# 配置为主节点
mysql -e "CREATE USER 'repl_user'@'%' IDENTIFIED BY 'password';"
mysql -e "GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';"
else
# 配置为从节点
MASTER_HOST="mysql-0.mysql.database.svc.cluster.local"
mysql -e "CHANGE MASTER TO MASTER_HOST='${MASTER_HOST}', ..."
mysql -e "START SLAVE;"
fi
Q3:StatefulSet 支持并行启动吗?
支持。设置 podManagementPolicy: Parallel 可以让所有 Pod 同时启动,适用于不需要严格顺序的场景(如 Cassandra、Elasticsearch):
spec:
podManagementPolicy: Parallel
serviceName: elasticsearch
replicas: 3
Q4:StorageClass 不可用怎么办?
确保集群中有可用的 StorageClass。如果使用本地存储或特定云平台,需要先配置:
# 查看当前 StorageClass
kubectl get sc
# 不存在时可以使用 local-path-provisioner(K3s 自带)
# 或参考第 9 天内容配置 NFS/Rook Ceph 等
总结
StatefulSet 是 Kubernetes 中管理有状态应用的核心控制器,它的三大基石——稳定的 Pod 标识、独立的持久化存储、有序的部署与销毁——让数据库、消息队列等有状态工作负载能够自信地在 K8s 上运行。
| 关键点 | 说明 |
|---|---|
| Headless Service | 必须配合 StatefulSet,提供稳定 DNS |
| volumeClaimTemplates | 自动为每个 Pod 创建独立 PVC |
| Pod 命名规则 | <sts-name>-<ordinal>,从 0 开始 |
| 有序策略 | 默认 OrderedReady,可改为 Parallel |
| 更新策略 | 支持分区更新、回滚、金丝雀发布 |
一句话记住:无状态用 Deployment,有状态用 StatefulSet——这是 K8s 工作负载设计的第一原则。
下期预告
第 11 天我们将学习 Ingress 与 Ingress Controller——如何将集群外部的流量优雅地路由到内部 Service,实现域名绑定、TLS 终结和路径转发,打通用户到应用的”最后一公里”。
系列目录:
– 第 1 天:Kubernetes 是什么?核心概念与架构全景解析
– 第 2 天:手把手搭建你的第一个 K8s 集群(kubeadm 实战)
– 第 3 天:Pod 详解:K8s 最小的调度单元与生命周期管理
– 第 4 天:Deployment 与 ReplicaSet:声明式应用管理
– 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解
– 第 6 天:Namespace 与资源配额:多租户隔离基础
– 第 7 天:ConfigMap 与 Secret:配置管理最佳实践
– 第 8 天:Volume 与 PersistentVolume:存储抽象层的核心机制
– 第 9 天:StorageClass 与动态存储供给实战
– 第 10 天:StatefulSet:有状态应用的部署与管理(本篇)
– 第 11 天:Ingress 与 Ingress Controller:外部流量接入全攻略
– … 持续更新中 …















暂无评论内容