欢迎光临
我们一直在努力

StatefulSet 详解:在 K8s 中部署 MySQL 等有状态应用时需要注意哪些坑

在Kubernetes(K8s)中部署无状态应用(如Web服务器)通常使用Deployment,但对于MySQL、Kafka或ZooKeeper这类有状态应用,它们需要稳定的网络标识和持久化存储。这时,我们就需要使用StatefulSet

StatefulSet提供了以下关键特性:
1. 稳定的网络标识(DNS): 每个Pod都有一个基于序号的稳定主机名。
2. 有序的部署和扩展: Pod按照序号(0, 1, 2…)顺序创建和销毁。
3. 稳定的持久存储: 通过VolumeClaimTemplate,为每个Pod分配独立的PersistentVolume。

本文将以部署MySQL为例,详细讲解StatefulSet的配置和需要避开的陷阱。

步骤一:创建Headless Service

StatefulSet必须配合Headless Service(无头服务)使用。Headless Service不分配Cluster IP,而是返回所有关联Pod的IP地址,从而保证了每个StatefulSet Pod拥有一个稳定的、可解析的DNS名称(格式为:...svc.cluster.local)。

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless-service
  labels:
    app: mysql
spec:
  ports:
  - port: 3306
    name: mysql
  clusterIP: None # 关键:设置为None即为Headless Service
  selector:
    app: mysql

步骤二:定义StatefulSet

下面是一个部署MySQL的StatefulSet示例,重点关注volumeClaimTemplates的使用。

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
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: your_strong_password
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
      # 确保容器入口点(Entrypoint)能处理初始化和集群配置

  # 关键配置:持久卷声明模板
  volumeClaimTemplates:
  - metadata:
      name: mysql-data # 必须匹配上面 volumeMounts 中的 name
    spec:
      accessModes:
      - ReadWriteOnce # 通常用于单个Pod独占存储
      storageClassName: standard-sc # 替换为您的存储类名称
      resources:
        requests:
          storage: 10Gi # 请求10GB存储空间

常见陷阱及解决方案

使用StatefulSet部署有状态应用时,如果不理解其工作原理,可能会遇到以下几个“坑”:

陷阱一:删除StatefulSet不会删除PV/PVC

问题: 当你使用 kubectl delete statefulset mysql 命令删除StatefulSet后,其关联的PersistentVolumeClaim (PVC) 和 PersistentVolume (PV) 默认不会被删除。它们仍然保留在集群中,如果重新部署同名的StatefulSet,它会尝试重新绑定到旧的存储上。

风险: 存储泄露或重新部署时加载到旧数据。

解决方案: 删除StatefulSet后,必须手动删除相关的PVC和PV。

# 检查并删除PVC
kubectl get pvc | grep mysql-data-mysql
kubectl delete pvc mysql-data-mysql-0 mysql-data-mysql-1 mysql-data-mysql-2

# 检查PV(如果StorageClass的reclaimPolicy是Retain,则PV也需要手动删除)

陷阱二:存储回收策略(Reclaim Policy)

问题: 如果你使用的StorageClassreclaimPolicy设置为Retain,即使你删除了PVC,底层的PV和数据仍会保留。如果设置为Delete,则删除PVC时,PV也会被自动删除。

解决方案: 在生产环境中,对于关键数据库,建议使用Retain策略以防止意外删除导致数据丢失,但这意味着管理负担增加。在测试环境,可以使用Delete

陷阱三:依赖序号的初始化逻辑

对于MySQL集群(如主从复制或Galera集群),实例0通常是Primary/Master,而实例1、2…是Replica/Slave。

问题: 容器启动时,它不知道自己是StatefulSet中的哪一个序号,因此无法自动配置自己的角色。

解决方案: 在容器的Entrypoint脚本中,通过读取Pod名称来获取序号,并据此执行不同的初始化逻辑。

例如,在启动脚本中可以检查:

if hostname | grep "-0$"; then
    # I am the master (mysql-0)
    echo "Initializing Master Role"
    # 执行主节点特有的初始化和配置
else
    # I am a replica (mysql-1, mysql-2, ...)
    echo "Initializing Replica Role"
    # 配置连接到Master节点(使用mysql-0.mysql-headless-service 作为Master地址)
fi

陷阱四:启动顺序和健康检查

StatefulSet严格按照序号顺序启动(0 -> 1 -> 2)。如果在启动下一个Pod前,上一个Pod没有达到“就绪”状态,部署会停滞。

解决方案: 务必为MySQL容器配置准确的 readinessProbelivenessProbe,确保只有当MySQL服务完全启动、数据库初始化完成且能够接受连接时,Pod才被标记为Ready。如果初始化时间较长,需要设置较高的 initialDelaySeconds

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » StatefulSet 详解:在 K8s 中部署 MySQL 等有状态应用时需要注意哪些坑
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址