实现StatefulSet的持久化存储

StatefulSet支持通过VolumeClaimTemplate为每个Pod自动创建PVCPV,在缩容或删除Pod时,会保留PVCPV,重新扩容后会自动使用已有PVCPV,确保PV中的数据不会丢失。本文介绍如何通过VolumeClaimTemplateStatefulSet中的Pod挂载云盘存储卷,实现StatefulSet的持久化存储。

使用场景

StatefulSet具有以下特点:

  • 稳定的部署次序:按顺序有序部署或扩展,即从0N-1,在运行下一个Pod时,所有之前的Pod都是RunningReady状态。

  • 稳定的缩容次序:按顺序有序收缩或删除,即从N-10,在删除下一个Pod时 ,所有之前的Pod都已被删除。

  • 稳定的网络标志:Pod重新调度后其PodNameHostName不变。

  • 稳定的持久化存储:基于PVC,Pod重新调度后仍能访问到相同的持久化数据。

基于上述特点,您可以通过VolumeClaimTemplatesStatefulSet中的Pod挂载云盘存储卷,实现以下特性:

说明

VolumeClaimTemplate表示一类PVC的模板,系统会根据StatefulSet配置的replicas数量,创建相应数量的PVC。这些PVC除了名称不一样,其他配置均相同。

  • 创建StatefulSet时,通过VolumeClaimTemplate可以自动创建PVCPV。其中,Pod的名称序号和PVC的名称序号一一对应。例如,名为web-0Pod所使用的PVCdisk-essd-web-0,名为web-1Pod所使用的PVCdisk-essd-web-1。

  • 扩容StatefulSet时,随着Pod的增加,如果已有对应序号的PVC,会自动使用该PVC和对应的PV;如果没有,会自动创建新的PVCPV。

  • 缩容StatefulSet时,随着Pod的减少,PVCPV保持不变。

  • 删除StatefulSet中的Pod时,会保留PVCPV,新创建的Pod会自动使用已删除Pod所使用的PVCPV。

  • 使用云盘作为PV,可以实现持久化存储。

创建StatefulSet并挂载云盘存储卷

  1. 使用以下YAML示例,创建statefulset.yaml文件。

    以下YAML可创建一个StatefulSet和一个Service。其中StatefulSet包含2Pod,且通过volumeClaimTemplates自动创建PVCPV,为每个Pod挂载了一个20 GiBESSD PL1云盘。

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx
      serviceName: "nginx"
      replicas: 2
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: disk-essd
              mountPath: /data
      volumeClaimTemplates:
      - metadata:
          name: disk-essd
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "alicloud-disk-essd"
          resources:
            requests:
              storage: 20Gi

    volumeClaimTemplates中的参数说明如下:

    参数

    说明

    accessModes

    存储卷的访问模式。

    storageClassName

    要使用的StorageClass名称。

    本文使用的alicloud-disk-essdACK默认提供的StorageClass。该StorageClass定义了云盘存储卷使用ESSD云盘,默认PL等级为PL1。

    说明

    云盘采用按量付费。更多信息,请参见块存储计费块存储价格

    storage

    云盘存储卷的容量大小。

  2. 部署StatefulSet。

    kubectl create -f statefulset.yaml
  3. 查看StatefulSetPod的部署情况。

    kubectl get pod -l app=nginx

    预期返回:

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          70s
    web-1   1/1     Running   0          55s
  4. 查看PVC。

    kubectl get pvc

    预期返回如下,可以看到已自动创建了Pod对应的PVCPV。

    NAME              STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
    disk-essd-web-0   Bound    d-uf6727q3zpydr645****   20Gi       RWO            alicloud-disk-essd   <unset>                 93s
    disk-essd-web-1   Bound    d-uf63en5prlz6halx****   20Gi       RWO            alicloud-disk-essd   <unset>                 78s

验证StatefulSet扩缩容时,PVC的变化情况

StatefulSet扩容后自动创建新的PVCPV

  1. 增加StatefulSet的副本数,将StatefulSet扩容到3Pod。

    kubectl scale sts web --replicas=3
  2. 查看Pod,确认扩容成功。

    kubectl get pod -l app=nginx

    预期返回如下,Pod已增加到3个。

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          2m46s
    web-1   1/1     Running   0          2m31s
    web-2   1/1     Running   0          28s
  3. 查看PVC,确认已自动创建新的PVCPV。

    kubectl get pvc

    预期返回如下,可以看到随着StatefulSet扩容到3Pod后,PVCPV也扩容到3个。

    NAME              STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
    disk-essd-web-0   Bound    d-uf6727q3zpydr645****   20Gi       RWO            alicloud-disk-essd   <unset>                 3m11s
    disk-essd-web-1   Bound    d-uf63en5prlz6halx****   20Gi       RWO            alicloud-disk-essd   <unset>                 2m56s
    disk-essd-web-2   Bound    d-uf6f3nbkzljbqyb0****   20Gi       RWO            alicloud-disk-essd   <unset>                 54s

StatefulSet缩容后PVC保持不变

  1. 减少StatefulSet的副本数,将StatefulSet缩容回2Pod。

    kubectl scale sts web --replicas=2
  2. 查看Pod,确认缩容成功。

    kubectl get pod -l app=nginx

    预期返回如下,Pod已减少到2个。

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          3m51s
    web-1   1/1     Running   0          3m36s
  3. 查看PVC,确认缩容后PVC保持不变。

    kubectl get pvc

    预期返回如下,可以看到StatefulSet缩容到2Pod后,PVC仍是3个,说明PVC不会随着StatefulSet缩容而缩减。

    NAME              STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
    disk-essd-web-0   Bound    d-uf6727q3zpydr645****   20Gi       RWO            alicloud-disk-essd   <unset>                 4m6s
    disk-essd-web-1   Bound    d-uf63en5prlz6halx****   20Gi       RWO            alicloud-disk-essd   <unset>                 3m51s
    disk-essd-web-2   Bound    d-uf6f3nbkzljbqyb0****   20Gi       RWO            alicloud-disk-essd   <unset>                 109s

StatefulSet再次扩容后使用已有PVC

  1. 增加StatefulSet的副本数,将StatefulSet重新扩容到3Pod。

    kubectl scale sts web --replicas=3
  2. 查看Pod,确认扩容成功。

    kubectl get pod -l app=nginx

    预期返回如下,Pod已重新增加到3个。

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          4m49s
    web-1   1/1     Running   0          4m34s
    web-2   1/1     Running   0          8s
  3. 查看PVC,确认新创建的Pod会使用原有PVC。

    kubectl get pvc

    预期返回:

    NAME              STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
    disk-essd-web-0   Bound    d-uf6727q3zpydr645****   20Gi       RWO            alicloud-disk-essd   <unset>                 5m6s
    disk-essd-web-1   Bound    d-uf63en5prlz6halx****   20Gi       RWO            alicloud-disk-essd   <unset>                 4m51s
    disk-essd-web-2   Bound    d-uf6f3nbkzljbqyb0****   20Gi       RWO            alicloud-disk-essd   <unset>                 2m49s

验证删除StatefulSetPod,PVC保持不变

  1. 查看Pod所使用的PVC。

    以名为web-1Pod为例:

    kubectl describe pod web-1 | grep ClaimName

    预期返回:

    ClaimName:  disk-essd-web-1
  2. 删除StatefulSet中的一个Pod。

    以删除名为web-1Pod为例:

    kubectl delete pod web-1
  3. 查看StatefulSet中的Pod情况。

    kubectl get pod -l app=nginx

    预期返回如下,已自动创建新的Pod。由于StatefulSet的命名特性,新创建的Pod和之前的Pod名称一致。

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          7m8s
    web-1   1/1     Running   0          11s
    web-2   1/1     Running   0          2m27s
  4. 查看PVC。

    kubectl get pvc

    预期返回如下,可以看到新创建的Pod仍会使用原有PVC。

    NAME              STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
    disk-essd-web-0   Bound    d-uf6727q3zpydr645****   20Gi       RWO            alicloud-disk-essd   <unset>                 7m55s
    disk-essd-web-1   Bound    d-uf63en5prlz6halx****   20Gi       RWO            alicloud-disk-essd   <unset>                 7m40s
    disk-essd-web-2   Bound    d-uf6f3nbkzljbqyb0****   20Gi       RWO            alicloud-disk-essd   <unset>                 5m38s

验证云盘存储卷的持久化存储

  1. 查看挂载路径。

    以名为web-1Pod为例:

    kubectl exec web-1 -- ls /data

    预期返回:

    lost+found
  2. 在挂载路径中写入文件。

    以名为web-1Pod为例:

    kubectl exec web-1 -- touch /data/test
    kubectl exec web-1 -- ls /data

    预期返回:

    lost+found
    statefulset
  3. 删除Pod。

    以删除名为web-1Pod为例:

    kubectl delete pod web-1
  4. 查看新创建的Pod。

    kubectl get pod -l app=nginx

    预期返回:

    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          10m
    web-1   1/1     Running   0          10s
    web-2   1/1     Running   0          6m14s
  5. 确认新创建的Pod已重新挂载云盘,云盘中的数据仍然存在。

    kubectl exec web-1 -- ls /data

    预期返回如下,可以看到云盘中存在之前写入的test文件,说明云盘存储卷中的数据可以持久化存储。

    lost+found
    statefulset