在 Kubernetes 的世界里,服务通常分为有状态和无状态两种类型。对于无状态服务,我们可以轻松地使用 Deployment 或 RC 来管理,但当面对需要稳定网络标识和持久化存储的有状态服务时,StatefulSet 就成为了最佳选择。本文将深入解析 StatefulSet 控制器的核心概念、工作原理及实际应用。
一、StatefulSet 核心概念解析
有状态服务 vs 无状态服务
Kubernetes 中存在两种服务类型,它们的管理方式有着本质区别:
-
无状态服务:由 RC、Deployment、DaemonSet 等控制器管理,其 Pod 的 IP、名称、启停顺序都是随机的,个体对整体无影响,所有 Pod 共享相同的数据卷(如 Tomcat 集群)
-
有状态服务:由 StatefulSet 管理,其 Pod 名称固定不变,每个 Pod 拥有独立的持久化存储目录,适用于需要稳定标识和状态的服务(如 MySQL 主从、Redis 集群等)
StatefulSet 的三大组成部分
- Headless Service:用于定义 Pod 的网络标识,生成可解析的 DNS 记录
- volumeClaimTemplates:存储卷申请模板,自动创建 PVC,为每个 Pod 提供独立存储
- StatefulSet 控制器:核心管理组件,负责 Pod 的创建、更新和维护
Headless Service 详解
Headless Service 是 StatefulSet 的关键组件,它与普通 Service 的最大区别是不分配 ClusterIP。通过解析其 DNS 可以直接返回所有关联 Pod 的 DNS 和 IP 地址(这是 StatefulSet 部署的 Pod 独有的特性)。
为什么需要 Headless Service?
- StatefulSet 要求 Pod 名称必须有序且固定,重建后名称保持不变
- Pod IP 是变化的,需要用固定的 Pod 名称作为唯一标识符
- 提供稳定的网络标识,确保服务发现的可靠性
Headless Service 会为自身分配固定域名:
plaintext
<service name>.<namespace name>.svc.cluster.local
而每个 Pod 则会获得如下格式的 DNS 名称:
plaintext
<pod name>.<service name>.<namespace name>.svc.cluster.local
二、StatefulSet 资源清单编写指南
StatefulSet 的资源清单包含几个关键部分,我们可以通过kubectl explain命令查看详细定义:
bash
运行
# 查看StatefulSet的顶级字段
kubectl explain statefulset
# 查看spec字段详情
kubectl explain statefulset.spec
# 查看Pod模板字段
kubectl explain statefulset.spec.template
StatefulSet 清单的核心字段说明:
spec.replicas:指定副本数量spec.selector:标签选择器,必须匹配 Pod 模板中的标签spec.serviceName:关联的 Headless Service 名称(必填)spec.template:Pod 模板定义spec.volumeClaimTemplates:存储卷申请模板
注意:StatefulSet 中有两个 spec 字段,外层 spec 用于定义 StatefulSet 属性,内层 spec(spec.template.spec)用于定义 Pod 容器属性
三、StatefulSet 实战:部署 Web 站点
下面通过一个实例演示如何使用 StatefulSet 部署 Web 服务:
1. 编写资源清单文件
创建statefulset.yaml文件,包含 Headless Service 和 StatefulSet 定义:
yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: "None" # 定义为Headless Service
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx-svc" # 关联Headless Service
replicas: 2 # 两个副本
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
2. 部署并验证
bash
运行
# 应用配置
kubectl apply -f statefulset.yaml
# 查看StatefulSet
kubectl get statefulset
# 查看创建的Pod(名称是有序的)
kubectl get pods -l app=nginx
# 查看Headless Service
kubectl get svc -l app=nginx
执行结果会显示两个有序命名的 Pod:web-0和web-1,以及一个类型为 ClusterIP 且 CLUSTER-IP 为 None 的 Headless Service。
3. 验证 DNS 解析
创建一个测试 Pod 验证 DNS 解析功能:
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-second
spec:
containers:
- name: busybox
image: busybox:1.28
command: ["sh","-c","sleep 3600"]
部署后进入容器测试:
bash
运行
# 进入测试Pod
kubectl exec -it pod-second -- /bin/sh
# 解析单个Pod的DNS
nslookup web-0.nginx-svc.default.svc.cluster.local
# 解析Headless Service(会返回所有Pod的IP)
nslookup nginx-svc.default.svc.cluster.local
四、StatefulSet 管理操作
动态扩容
有两种方式可以实现 StatefulSet 的扩容:
- 修改配置文件中的
replicas值后应用:
bash
运行
kubectl apply -f statefulset.yaml
- 直接编辑 StatefulSet 配置:
bash
运行
kubectl edit sts web
# 修改spec.replicas的值
扩容时,StatefulSet 会按照序号递增的顺序创建新 Pod(如 web-2、web-3 等)。
动态缩容
缩容操作与扩容类似,只需减小replicas的值。需要注意的是,StatefulSet 会按照序号递减的顺序删除 Pod(先删除序号最大的)。
bash
运行
# 修改配置文件后应用
kubectl apply -f statefulset.yaml
版本更新
更新 StatefulSet 管理的 Pod 镜像:
bash
运行
# 编辑StatefulSet配置
kubectl edit sts web
# 修改spec.template.spec.containers.image的值
更新时,StatefulSet 会按照序号从高到低的顺序逐个更新 Pod,确保服务的连续性。
五、经典面试题:Service 与 Headless Service 的区别
| 特性 | 普通 Service | Headless Service |
|---|---|---|
| ClusterIP | 分配唯一的 ClusterIP | 无 ClusterIP |
| DNS 解析 | 返回 Service 的 ClusterIP | 返回所有关联 Pod 的 IP 列表 |
| 适用场景 | 无状态服务、负载均衡 | 有状态服务、需要明确知道每个 Pod |
| Pod 标识 | 随机名称 | 固定有序名称 |
通过nslookup命令可以直观看到区别:
- 普通 Service 解析返回 ClusterIP
- Headless Service 解析返回所有 Pod 的 IP 地址