之前部署服务时 PVC 使用的 StorageClass 为 local-path,现在为了服务高可用需要切换到 longhorn,所以需要对现有使用 local-path 作为 StorageClass 的 PVC 做数据迁移

经过评估下面这种方式最合适,并且有下面优点:

  1. 操作简便,相比能想到的其他方法,这是最简便的了
  2. 理论上可以支持所有的 StorageClass,因为迁移数据时只需要将老PVC和新PVC挂载到同一个迁移Pod中即可
  3. 可以保留旧 PVC 数据,方便回滚

缺点:

  1. 需要停服,有时甚至需要删除 StatefulSet(删除之前需要提前备份 yaml,如果使用的是 Helm 也应当有所备份)
  2. 虽然这已经是想到的最简单的方法,但是整体过程还是较为麻烦,不够自动化,当需要迁移的 PVC 较多的情况下,这个过程将会比较耗时,还有可能由于人为因素造成问题,如果只是部署时没有使用正确的 StorageClass,没有产生业务数据的情况下,那倒不如直接删除重新部署来的方便

总而言之,这是一个可供参考的方法,实际操作的话建议综合评估再选择

准备测试 Pod

创建一个 StatefulSet,运行 1 个 MySQL Pod 副本,通过volumeClaimTemplates为 Pod 副本创建 PVC 并挂载,使用的 StorageClass 为local-path

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-pvc-test
  namespace: test
spec:
  serviceName: mysql-pvc-test
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Retain
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: swr.cn-east-3.myhuaweicloud.com/r4in/bitnami/mysql:8.0.34
          imagePullPolicy: IfNotPresent
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
            - name: ALLOW_EMPTY_PASSWORD
              value: "no"
          ports:
            - containerPort: 3306
              name: mysql
              protocol: TCP
          volumeMounts:
            - name: data
              mountPath: /bitnami/mysql/data
              subPath: mysql
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app: mysql
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: local-path
        resources:
          requests:
            storage: 20Gi

查看 Pod,PVC

kubectl get pods -n test
NAME               READY   STATUS    RESTARTS   AGE
mysql-pvc-test-0   1/1     Running   0          20s


kubectl get pvc -n test
NAME                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-mysql-pvc-test-0   Bound    pvc-d40a13bd-3f3d-4640-b9de-c874ea8012e6   20Gi       RWO            local-path     <unset>                 26s

创造数据,在 MySQL Pod 中创建个数据库aaa

kubectl exec -it -n test mysql-pvc-test-0 -- mysql -uroot -p123456 -e 'create database aaa; show databases;'

创建新的 PVC

新建 PVC,指定StorageClasslonghorn,PVC名称为是在旧 PVC 名前加上了longhorn-前缀,名称为longhorn-data-mysql-pvc-test-0

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: mysql
  name: longhorn-data-mysql-pvc-test-0
  namespace: test
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: longhorn

查看 PVC

kubectl get pvc -n test
NAME                             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-mysql-pvc-test-0            Bound    pvc-a1aca789-6130-4e26-8aac-1f15dac54bc0   20Gi       RWO            local-path     <unset>                 94s
longhorn-data-mysql-pvc-test-0   Bound    pvc-b6296cf3-2540-4c49-95af-85d93dcf316c   20Gi       RWO            longhorn       <unset>                 8s

创建 Rsync Pod 迁移数据

步骤1:MySQL StatefulSet 副本调为0

kubectl scale statefulset mysql-pvc-test -n test --replicas 0

步骤2:创建数据迁移 Pod

apiVersion: v1
kind: Pod
metadata:
  name: data-mysql-pvc-test-0-migrate
  namespace: test
spec:
  restartPolicy: Never
  containers:
    - name: sync-data
      image: swr.cn-east-3.myhuaweicloud.com/r4in/eeacms/rsync:2.9
      command:
        - sh
        - -c
        - |
            rsync -avh --progress --delete /old-data/ /new-data && \
            ls -rlth /new-data && \
            du -sh /new-data
      volumeMounts:
        - name: old-volume
          mountPath: /old-data
        - name: new-volume
          mountPath: /new-data
  volumes:
    - name: old-volume
      persistentVolumeClaim:
        claimName: data-mysql-pvc-test-0
    - name: new-volume
      persistentVolumeClaim:
        claimName: longhorn-data-mysql-pvc-test-0

查看迁移 Pod

# 查看 Pod
kubectl get pods -n test
NAME                            READY   STATUS      RESTARTS   AGE
data-mysql-pvc-test-0-migrate   0/1     Completed   0          38s


# 查看日志
kubectl logs -f -n test data-mysql-pvc-test-0-migrate
sending incremental file list
deleting lost+found/
./
mysql/
mysql/#ib_16384_0.dblwr
        196.61K 100%  156.25MB/s    0:00:00 (xfr#1, to-chk=175/178)
(...省略...)
mysql/sys/sys_config.ibd
        114.69K 100%  203.64kB/s    0:00:00 (xfr#170, to-chk=0/178)

sent 191.87M bytes  received 3.30K bytes  127.92M bytes/sec
total size is 191.81M  speedup is 1.00
total 4K
drwxrwxrwx    8 root     root        4.0K Nov  8 09:45 mysql
183.2M        /new-data

恢复 Pod

数据迁移到新的 PVC 之后就可以修改 StatefulSet 中的 PVC 相关的字段名称然后重新启动 MySQL Pod

由于 StatefulSet 不支持修改volumeClaimTemplates字段,所以不能直接通过kubectl edit等方式来直接修改现有的 StatefulSet 中volumeClaimTemplates,就需要先删除之前的 StatefulSet,然后修改里面的volumeClaimTemplates再重新创建 StatefulSet,由于之前已经将数据同步到了新的 PVC,所以重新创建之后数据还在

如果没有通过volumeClaimTemplates的方式来创建 PVC,使用 Deployment 运行 Pod 并直接使用已存在的 PVC,则可以直接修改volumes中的使用的 PVC 的名称,步骤会简单一些

步骤1:删除旧 MySQL StatefulSet

kubectl delete sts -n test mysql-pvc-test

步骤2:修改 MySQL StatefulSet yaml 文件并重新创建

  • 修改spec.volumeClaimTemplates.metadata.namelonghorn-data(增加了longhorn-前缀,与之前手动创建的 PVC 名称添加的longhorn-前缀对应)
  • 修改spec.template.spec.containers.volumeMounts.namelonghorn-data(与上面的对应)
  • 修改spec.volumeClaimTemplates.spec.storageClassNamelonghorn
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-pvc-test
  namespace: test
spec:
  serviceName: mysql-pvc-test
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Retain
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: swr.cn-east-3.myhuaweicloud.com/r4in/bitnami/mysql:8.0.34
          imagePullPolicy: IfNotPresent
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
            - name: ALLOW_EMPTY_PASSWORD
              value: "no"
          ports:
            - containerPort: 3306
              name: mysql
              protocol: TCP
          volumeMounts:
            - name: longhorn-data
              mountPath: /bitnami/mysql/data
              subPath: mysql
  volumeClaimTemplates:
    - metadata:
        name: longhorn-data
        labels:
          app: mysql
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: longhorn
        resources:
          requests:
            storage: 20Gi

待 MySQL Pod 启动之后检查数据,之前创建的aaa数据库还在

kubectl exec -it -n test mysql-pvc-test-0 -- mysql -uroot -p123456 -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| aaa                |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+