在容器编排领域,StatefulSet 是 Kubernetes 中专门为有状态应用设计的控制器对象。与常见的 Deployment 不同,StatefulSet 为每个 Pod 提供稳定的网络标识和持久化存储,解决了分布式系统中服务发现和数据持久化的核心难题。
我在实际生产环境中部署 Cassandra 集群时,深刻体会到 StatefulSet 的价值。当我们需要维护数据库、消息队列等需要稳定标识和持久化存储的服务时,常规的 Deployment 控制器会显得力不从心。StatefulSet 通过三个关键特性解决了这些问题:
StatefulSet 通过 Headless Service 为每个 Pod 分配唯一的 DNS 记录,格式为:
code复制<pod-name>.<service-name>.<namespace>.svc.cluster.local
这种设计使得集群内其他组件可以通过固定域名访问特定实例,这对需要维护成员关系的分布式系统(如ETCD、Zookeeper)至关重要。
我在配置 Elasticsearch 集群时,通过以下 Service 定义实现了稳定的网络标识:
yaml复制apiVersion: v1
kind: Service
metadata:
name: elasticsearch
labels:
app: elasticsearch
spec:
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
clusterIP: None
selector:
app: elasticsearch
StatefulSet 使用 PersistentVolumeClaimTemplate 为每个 Pod 动态创建 PVC,确保 Pod 重新调度后仍能挂载原有的数据卷。这是通过 StorageClass 的动态供应机制实现的。
典型的数据卷声明配置示例:
yaml复制volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "ssd"
resources:
requests:
storage: 100Gi
下面以部署一个三节点MySQL集群为例,演示完整操作流程:
yaml复制apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mysql-ssd
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
yaml复制apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
set -ex
[[ `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
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "mysql-ssd"
resources:
requests:
storage: 10Gi
有序索引:StatefulSet 创建的 Pod 会获得从0开始的连续索引(如mysql-0, mysql-1),这个序号会影响:
更新策略:
yaml复制updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 1
partition 参数控制灰度发布过程,只有序号大于等于 partition 值的 Pod 会被更新
存储类选择:
反亲和性配置:
yaml复制affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- mysql
topologyKey: "kubernetes.io/hostname"
确保不同实例分散在不同节点,提高容错能力
Pod卡在Terminating状态:
bash复制kubectl describe pvc <pvc-name> -n <namespace>
扩容失败:
bash复制kubectl describe quota -n <namespace>
网络连接问题:
bash复制kubectl run -it --rm --image=infoblox/dnstools dnstools
host mysql-0.mysql
通过拓扑分布约束实现:
yaml复制topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: mysql
通过编写Operator扩展StatefulSet功能,实现:
go复制// Operator示例代码片段
func (r *MySQLReconciler) handleUpgrade(ctx context.Context, sts *appsv1.StatefulSet) error {
if needsUpgrade(sts) {
if err := r.backupAllPods(ctx); err != nil {
return err
}
return r.rollingUpgrade(ctx, sts)
}
return nil
}
建议配置完善的探针:
yaml复制livenessProbe:
exec:
command:
- mysqladmin
- ping
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- mysql
- -h127.0.0.1
- -e
- "SELECT 1"
initialDelaySeconds: 5
periodSeconds: 2
容器资源指标:
应用层指标:
Prometheus配置示例:
yaml复制- job_name: 'mysql-statefulset'
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['production']
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: 'mysql'
- source_labels: [__meta_kubernetes_pod_container_port_number]
action: keep
regex: '9104'
我在实际运维中发现,StatefulSet 的有序特性虽然保证了数据安全,但也带来了特殊的运维考量。比如在节点故障时,需要特别注意序号连续的 Pod 不要被集中调度到同一节点。通过合理设置 PodDisruptionBudget 和反亲和性规则,可以显著提高集群的健壮性。