网站首页 > 精选文章 / 正文
由于 centos7 停止维护,公司需要把原先运行在 centos7 主机上的 ceph 集群迁移到 ubuntu。公司 k8s 集群 pvc 使用的存储也是 ceph,因此 ceph 迁移必然会影响 k8s 服务。 本文介绍如何把 k8s pvc 存储从 centos ceph 迁移到 ubuntu ceph。
迁移看似很难,步步为营,必将成功。下面迁移过程分为以下阶段:迁移前检查、迁移前备份、迁移、迁移后处理。迁移前检查阶段会确认当前 k8s 状态是否正常;迁移前备份阶段会把当前 k8s 资源备份,并生成迁移资源文件;迁移阶段会停止使用 pvc 的服务,使用迁移前备份阶段生成的迁移资源文件,恢复使用 pvc 的服务;迁移后处理会恢复 pv 回收策略、cronjob 状态。
获取新旧集群 ceph 配置
key | 含义 |
NEW_CEPH_CLUSTER_ID | 新 ceph 集群的cluster id |
NEW_CEPH_CLIENT_ADMIN_KEY | 新 ceph 集群的 client admin key |
NEW_CEPH_CONFIG_MONITOR | 新 ceph 集群的 monitor地址 |
CURRENT_CEPH_CLUSTER_ID | 旧 ceph 集群的 cluster id |
迁移前检查
备份 k8s 资源前若检查出问题,先解决问题再备份。
# 检查是否有节点不正常
kubectl get nodes -l node-role.kubernetes.io/edge!=true |grep -i NotReady
# 检查 k8s 控制面是否正常
kubectl -n kube-system get pod |grep -Pv '\s+([1-9]+)\/\1\s+' |grep -Ev "Completed|NAME|Evicted"
# 检查 k8s ceph rbd 使用的 cluster id 是否与旧 ceph 集群 cluster id 一致
kubectl get sc ceph-rbd-hdd-premium -ojsonpath='{.parameters.clusterID}'
确认 k8s 使用 ceph 正常,在 default namespace 进行下面验证:
- 验证 k8s 使用 ceph rbd csi 正常
- 验证 k8s 使用 ceph rbd intree 正常
- 验证 k8s 使用 cephfs csi 正常
- 验证 k8s 使用 cephfs intree 正常
迁移前备份
备份所有 pvc 相关 k8s 资源,以防迁移失败用来恢复。
- 备份 cronjob
记录 pvc 和 cronjob 映射关系
# 获取 ${namespace} 下的 cronjob 名称和 pvc 名称对应关系
kubectl get cronjob -n ${namespace} -o json | jq -r '.items[] | "\(.metadata.name) \(.spec.jobTemplate.spec.template.spec | select(has("volumes")).volumes[] | select(has("persistentVolumeClaim")).persistentVolumeClaim.claimName)"' | grep -v null
# 获取 cronjob suspend 状态
kubectl get cronjob ${cronjob_name} -n ${namespace} -o=jsonpath='{.spec.suspend}'
# 将获取到的 cronjob,pvc 对应信息写入文件 backup/record/pvc_cronjob.txt
echo "${namespace} ${pvc_name} Cronjob/${cronjob_name} ${suspend}" >> backup/record/pvc_cronjob.txt
挂起 cronjob 防止在迁移过程中有 cronjob 往旧 ceph 集群写数据造成迁移后数据不一致,升级结束后恢复。
# 从文件backup/record/pvc_cronjob.txt 获取涉及到的 cronjob,将其挂起
kubectl -n ${namespace} patch ${cronjob_name} -p '{"spec":{"suspend":true}}'
- 挂起所有使用 pvc 的 job/cronjob pod 和所有无主pod
也可以等待这些 pod 执行完毕,是挂起还是等待执行完毕需要根据 pod 对应服务来决定。
# 获取 ${namespace} 下所有使用 pvc 的 pod
kubectl get pod -n ${namespace} -o json | jq -r '.items[] | "\(.metadata.name) \(.spec|select(has("volumes")).volumes[] | select(has("persistentVolumeClaim")).persistentVolumeClaim.claimName)"' | grep -v null
# 获取 ${namespace} 下 ${pod_name} 的 ownerReferences 信息
# 根据 ownerReferences 信息找出使用 pvc 的 job/cronjob pod 和 无主 pod
# 若 ownerReferences kind 是Job,此 pod 是 Job 产生的 pod
# 若 ownerReferences kind 是CronJob,此 pod 是 CronJob 产生的 pod
# 若 ownerReferences 为空,此 pod 是 无主 pod
kubectl get pod ${pod_name} -n ${namespace} -o json | jq -r '.metadata.ownerReferences[0]'
- 备份集群状态
# 备份 pv 状态到文件 backup/status/pv_status.txt
kubectl get pv > backup/status/pv_status.txt
# 备份 pvc 状态到文件 backup/status/pvc_status.txt
kubectl get pvc -A > backup/status/pvc_status.txt
# 备份 pod 状态到文件 backup/status/pod_status.txt
kubectl get pod -owide -A > backup/status/pod_status.txt
# 备份 deploy 状态到文件 backup/status/deploy_status.txt
kubectl get deploy -A > backup/status/deploy_status.txt
# 备份 statefulset 状态到文件 backup/status/sts_status.txt
kubectl get sts -A > backup/status/sts_status.txt
- 记录 pv 和回收策略映射关系
# 记录 pv 和其对应的回收策略到文件 backup/record/pv_policy.txt
kubectl get pv -o=jsonpath='{range .items[*]}{.metadata.name} {.spec.persistentVolumeReclaimPolicy}{"\n"}{end}' > backup/record/pv_policy.txt
- 记录 pvc 和 pv 映射关系
# 记录 pvc 和 其对应的 pv 到文件 backup/record/tmp_pvc_pv_mapping.txt
kubectl get pvc --all-namespaces -o json | jq -r '.items[] | "\(.metadata.namespace) \(.metadata.name) \(.spec.volumeName)"' > backup/record/tmp_pvc_pv_mapping.txt
- 记录 pvc 和 workload 映射关系
# 获取 pod 和 pvc 对应关系
kubectl get pod -n ${namespace} -o json | jq -r '.items[] | "\(.metadata.name) \(.spec|select(has("volumes")).volumes[] | select(has("persistentVolumeClaim")).persistentVolumeClaim.claimName)"' | grep -v null
# 获取 pod ownerReferences 信息
kubectl get pod ${pod_name} -n ${namespace} -o json | jq -r '.metadata.ownerReferences[0]'
# 若 ownerReferences 为空,要特殊处理此 pod, 在迁移前要结束此 pod 运行
# 若 ownerReferences kind 为 ReplicaSet,此 pod 对应的 workload 为 ReplicaSet 或 Deployment
# 获取 pod 对应的 ReplicaSet/Deployment 副本数记录到文件 backup/record/pvc_workload_replicas.txt
echo "${namespace} ${pvc_name} ReplicaSet/${controller_name} ${replica}" >> backup/record/pvc_workload_replicas.txt
echo "${namespace} ${pvc_name} Deployment/${deploy_name} ${replica}" >> backup/record/pvc_workload_replicas.txt
# 若 ownerReferences kind 为 StatefulSet,获取 StatefulSet 副本数记录到文件 backup/record/pvc_workload_replicas.txt
echo "${namespace} ${pvc_name} StatefulSet/${controller_name} ${replica}" >> backup/record/pvc_workload_replicas.txt
- 修改 pv 回收策略为Retain
修改 pv 回收策略为 Retain,防止把 workload 副本数置为0时,pv 对应的 ceph 资源被删除。迁移完毕后恢复 pv 回收策略。
kubectl patch pv "${volume}" -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
- 备份所有的 pvc和pv 资源
# 备份所有的 pv 资源
kubectl get pv ${volume} -ojson | jq 'del(
.metadata.annotations,
.metadata.creationTimestamp,
.metadata.generation,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.uid,
.spec.claimRef.resourceVersion,
.spec.claimRef.uid,
.status
)' > backup/pv/${volume}.json
# 备份所有的 pvc 资源
kubectl -n ${namespace} get pvc ${pvc_name} -ojson | jq 'del(
.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration",
.metadata.annotations."pv.kubernetes.io/bind-completed",
.metadata.annotations."pv.kubernetes.io/bound-by-controller",
.metadata.annotations."volume.beta.kubernetes.io/storage-provisioner",
.metadata.annotations."volume.kubernetes.io/storage-provisioner",
.metadata.creationTimestamp,
.metadata.generation,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.uid,
.status
)' > backup/pvc/${namespace}_${pvc_name}.json
- 备份所有的storageclass资源
kubectl get sc ${storageclass} -ojson | jq 'del(
.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration",
.metadata.creationTimestamp,
.metadata.generation,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.uid,
.status
)' > backup/sc/${storageclass}.json
- 备份所有的 ceph csi 配置文件
保存 kube-system namespace 下面资源:
保存 configmap ceph-csi-config 到文件
backup/ceph/ceph-csi-config.json
保存 secret csi-cephfs-secret 到文件
backup/ceph/csi-cephfs-secret.json
保存 secret csi-rbd-secret 到文件
backup/ceph/csi-rbd-secret.json
保存 secret ceph-rbd-admin 到文件
backup/ceph/ceph-rbd-admin.json
- 备份用于升级测试 k8s 资源文件
单独备份迁移前检查阶段测试 k8s 是否正常的 pvc 和 pv 到目录 backup/migtest/pvc、backup/migtest/pv
- 生成新的 ceph csi 资源文件
复制文件
backup/ceph/ceph-csi-config.json 到
new/ceph/ceph-csi-config.json,修改文件中的 clusterID 为 NEW_CEPH_CLUSTER_ID 对应值,修改 monitors 为 NEW_CEPH_CONFIG_MONITOR 对应值
复制文件
backup/ceph/csi-cephfs-secret.json 到
new/ceph/csi-cephfs-secret.json,修改文件中的 adminkey、userKey 为 NEW_CEPH_CLIENT_ADMIN_KEY 对应值的base64
复制文件
backup/ceph/csi-rbd-secret.json 到
new/ceph/csi-rbd-secret.json,修改文件中 userKey 为 NEW_CEPH_CLIENT_ADMIN_KEY 对应值的base64
复制文件
backup/ceph/ceph-rbd-admin.json 到
new/ceph/ceph-rbd-admin.json,修改文件中 key 为 NEW_CEPH_CLIENT_ADMIN_KEY 对应值的base64
- 生成新的 pv资源文件
复制文件夹 backup/pv 到 new/pv,更改 pv 文件中 clusterID 为 NEW_CEPH_CLUSTER_ID 对应值;更改pv 文件中 spec.rbd.monitors then .spec.rbd.monitors 和 spec.cephfs.monitors then .spec.cephfs.monitors 为 NEW_CEPH_CONFIG_MONITOR 对应值
- 生成新的 storageclass 资源文件
复制文件夹 backup/sc 到 new/sc,更改 sc 文件中 clusterID 为 NEW_CEPH_CLUSTER_ID 对应值;更改 monitors 为 NEW_CEPH_CONFIG_MONITOR 对应值
- 生成用于测试的资源文件
分别复制文件夹 backup/migtest/pvc、backup/migtest/pv 到 new/migtest/pvc、new/migtest/pv。更改 new/migtest/pv 目录文件 clusterID 为 NEW_CEPH_CLUSTER_ID 对应值;spec.rbd.monitors then .spec.rbd.monitors 和 spec.cephfs.monitors then .spec.cephfs.monitors 为 NEW_CEPH_CONFIG_MONITOR 对应值。
迁移
- 确保所有 pv 回收策略为 Retain
检查所有 pv 回收策略是否为 Retain,防止把 workload 副本数置为0时,pv 对应的 ceph 资源被删除。迁移完毕后恢复 pv 回收策略
# 获取所有 pv 回收策略,检查是否都为 Retain
kubectl get pv -o=jsonpath='{range .items[*]}{.metadata.name} {.spec.persistentVolumeReclaimPolicy}{"\n"}{end}'
- dryrun 生成出来的 k8s 资源
用下面命令 dryrun 生成出来的 ceph csi 资源文件,pv资源文件,storageclass 资源文件,检查资源文件是否有语法问题。
kubectl apply -f ${k8s_dir} --dry-run=client
- 停止使用 pvc 的 workload
把
backup/record/pvc_workload_replicas.txt 记录的 workload 副本数都置为0,这样没有服务使用 ceph,就不会产生新的 ceph 数据。
kubectl -n ${namespace} scale ${workload} --replicas=0
然后检查各个 namespace 是否还有使用 pvc 的 pod, 若还有,单独处理这些 pod
# 检查是否还有使用 pvc 的 pod
kubectl get pod -n ${namespace} -o json | jq -r '.items[] | "\(.metadata.name) \(.spec|select(has("volumes")).volumes[] | select(has("persistentVolumeClaim")).persistentVolumeClaim.claimName)"' | grep -v null
- 迁移 ceph 数据
把 ceph 数据从旧集群迁移到新集群,忽略具体迁移过程。
- 更新 ceph csi 控制面
# 更新 ceph csi 资源
kubectl -n kube-system apply -f new/ceph/ --wait=true
# 重启 ceph csi pod
kubectl -n kube-system delete pod -l app=csi-rbdplugin-provisioner
kubectl -n kube-system delete pod -l app=csi-rbdplugin
kubectl -n kube-system delete pod -l app=csi-cephfsplugin-provisioner
kubectl -n kube-system delete pod -l app=csi-cephfsplugin
- 更新 storageclass 配置
# 删除原来的 storageclass(使用了旧 ceph 集群)
kubectl delete -f new/sc/${sc_file} --wait=true --ignore-not-found=true
# 使用最新的 storageclass(即使用新 ceph 集群)
kubectl apply -f new/sc/${sc_file} --wait=true
- 检查 ceph csi 可用性
k8s 使用新的 ceph 集群时,验证 k8s 能正常使用 ceph rbd csi,ceph rbd intree,cephfs csi ,cephfs intree
- 检查迁移过来的 pv/pvc/deployment
# 删除原来的测试 pv/pvc 资源
kubectl delete -f new/migtest/pv/${pv_file} --wait=true --ignore-not-found=true
kubectl delete -f new/migtest/pvc/${pvc_file} --wait=true --ignore-not-found=true
# 使用最新的测试 pv/pvc 资源
kubectl apply -f new/migtest/pv/${pv_file} --wait=true
kubectl apply -f new/migtest/pvc/${pvc_file} --wait=true
# 把 default namesapce 下的 deployment(确认 k8s 使用 ceph 正常)副本数设置为1
# 检查迁移过来的 pv/pvc/deployment 是否都正常
kubectl -n default scale deployment ${name} --replicas=1
# 检查完毕后,重新把 default namesapce 下的 deployment 副本数置为0,防止影响后续步骤
kubectl -n default scale deployment ${name} --replicas=0
- 重建 pvc/pv 前检查
# 检查是否有 pod 使用 pvc/pv
kubectl get pod -n ${namespace} -o json | jq -r '.items[] | "\(.metadata.name) \(.spec|select(has("volumes")).volumes[] | select(has("persistentVolumeClaim")).persistentVolumeClaim.claimName)"' | grep -v null
- 删除涉及到的 pvc/pv
# 删除涉及到的 pvc
kubectl delete -f new/pvc/${pvc_file} --wait=true --ignore-not-found=true
# 删除涉及到的 pv
kubectl delete -f new/pv/${pv_file} --wait=true --ignore-not-found=true
- 重建涉及到的 pvc/pv
# 重建涉及到的 pv
kubectl apply -f new/pv/${pv_file} --wait=true
# 重建涉及到的 pvc
kubectl apply -f new/pvc/${pvc_file} --wait=true
- 等待 pvc正确 bound pv
从文件
backup/record/pvc_pv_mapping.txt 获取正在重建的 pvc,检查 pvc 正确 bound pv
kubectl -n ${namespace} wait --for=jsonpath='{.status.phase}'=Bound pvc/${pvc_name} --timeout=60s
- 启动应用服务
查看
backup/record/pvc_workload_replicas.txt 文件记录的 workload 和 对应副本数,恢复对应workload
kubectl -n ${namespace} scale ${workload} --replicas=${replicas}
迁移后处理
- 恢复 pv 的回收策略
在迁移前把所有 pv 回收策略设置为 Retain,防止把 workload 副本数置为0时,pv 对应的 ceph 资源被删除。现在查看
backup/record/pv_policy.txt 文件记录的 pv 回收策略并恢复。
kubectl patch pv "${pv_name}" -p "{\"spec\":{\"persistentVolumeReclaimPolicy\":\"$pv_policy\"}}"
- 恢复挂起的 cronjob
迁移前挂起 cronjob 防止在迁移过程中有 cronjob 往旧 ceph 集群写数据造成迁移后数据不一致。查看
backup/record/pvc_cronjob.txt 文件记录的 cronjob 状态,若迁移前 cronjob 挂起现在开启它
kubectl -n ${namespace} patch ${cronjob_name} -p '{"spec":{"suspend":false}}'
- 更新 ceph-expand
从新 ceph 集群中获取到 ceph.conf 和 ceph.client.admin.keyring 这2个配置文件,
# 生成对应的ceph configmap
kubectl -n ${namespace} create configmap ceph-configmap --from-file=ceph.client.admin.keyring --from-file=ceph.conf --dry-run=client -o yaml > ./ceph-configmap.yaml
# 直接更新
kubectl -n ${namespace} apply -f ./ceph-configmap.yaml
# 重启ceph-expand pod
kubectl -n ${namespace} delete po -l app=cephfs-expand
整个迁移过程耗时比较长,可以开发为自动化脚本。迁移过程中进行了全方位的验证,保证迁移成功。
Tags:yaml map
猜你喜欢
- 2025-05-03 大模型直接搜索图片内容!群晖部署AI相册管理工具 immich(二)
- 2025-05-03 这样构建 K8s 中间件运维平台,运维真的能少遭很多罪……
- 2025-05-03 构建 Kubernetes中间件运维平台:标准化、可视化与全栈运维
- 2025-05-03 flutter集成 百度地图 ^2.0.1版本 | 绕坑必备
- 2025-05-03 涨薪技术|Kubernetes(k8s)之yaml语法大全
- 2025-05-03 opsone运维管理平台的独门绝技系列之一
- 2025-05-03 教你 7 步快速构建 GitLab 持续集成环境
- 2025-05-03 Kubernetes中的PV、PVC、Configmap介绍
- 2025-05-03 kubernetes环境手动部署 Prometheus 监控系统安装文档
- 2025-05-03 ArgoCD 可观测性最佳实践(可观测性理论)