欢迎点击下方👇关注我,记得星标哟~
文末会有重磅福利赠送
摘要:
将运行在 OpenShift 上的有状态工作负载迁移至 Azure Kubernetes Service (AKS) 是一项充满挑战的任务,传统的 Velero 备份与恢复流程在此场景下常常会“水土不服”。本文将深入剖析跨云迁移中常见的陷阱,并提供一套经过生产环境验证的解决方案。该方案通过将应用资源与存储资源解耦,利用 Velero 处理其擅长的应用迁移,同时对存储配置进行智能转换,最终实现应用完整性的保留与数据卷的平滑过渡。
https://melvin-marshal-pereira.medium.com/migrating-openshift-stateful-workloads-to-azure-kubernetes-service-aks-13c6823eeaca
一、 核心困境:为何标准 Velero 方案在跨云场景下会失败?
Velero 在同一云环境或同一存储体系内的集群迁移中表现卓越,但其设计初衷并未完全覆盖跨云场景的复杂性。当试图将一个在 OpenShift (例如,底层为 AWS 或 vSphere) 上备份的包含 PersistentVolume(PV) 的应用直接恢复到 AKS 时,几乎注定会失败。
以下是一个典型的 OpenShift PV 定义,它在 AKS 上是无效的:
# OpenShift PV (基于 AWS),在 AKS 上将恢复失败
apiVersion: v1
kind: PersistentVolume
metadata:
labels:
topology.kubernetes.io/zone: us-east-1a # ❌ AWS 特定的可用区标签
failure-domain.beta.kubernetes.io/region: us-east-1
spec:
awsElasticBlockStore: # ❌ AWS 特定的存储驱动
volumeID: aws://us-east-1a/vol-123456
nodeAffinity: # ❌ 节点亲和性指向了 Azure 中不存在的可用区
required:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values: [ "us-east-1a" ]
失败的根源在于以下五点:
-
1. 云厂商特定的存储驱动:AWS EBS、GCP Persistent Disk 和 Azure Disk 的 CSI (Container Storage Interface) 驱动实现各不相同。 -
2. 可用区/地域不兼容: us-east-1a是 AWS 的地理标签,在 Azure 中没有对应的概念。 -
3. 拓扑标签差异:各个云平台使用自己的一套拓扑标签来标识节点位置。 -
4. CSI 驱动不兼容:不同云的 CSI 实现细节不同,无法直接通用。 -
5. 元数据校验失败:AKS 的 API Server 会拒绝包含 AWS 或 GCP 特定字段(如 awsElasticBlockStore)的资源定义。
二、 解决方案核心关注点
我们的核心策略是将应用资源的迁移与存储资源的创建解耦。
-
1. 备份应用,排除存储:使用 Velero 仅备份应用层面的资源(如 Deployments, StatefulSets, Services, ConfigMaps 等),并明确排除 persistentvolumes,persistentvolumeclaims, 和storageclasses。velero backup create $BACKUP_NAME \
--exclude-resources persistentvolumes,persistentvolumeclaims,storageclasses -
2. 导出并转换存储定义:从源集群手动导出 PV 和 PVC 的 YAML 文件,然后通过脚本(例如使用 yq工具)将其转换为 AKS 兼容的格式。oc get pv -o yaml | yq eval '...transformation logic...' > transformed-pvs.yaml -
3. 恢复应用:在目标 AKS 集群上,使用 Velero 恢复应用资源。 velero restore create $RESTORE_NAME --from-backup $BACKUP_NAME -
4. 重建存储:在 AKS 上应用(apply)经过转换的存储资源定义文件,创建新的、与 Azure 兼容的 PV 和 PVC。 kubectl apply -f transformed-storage/
通过这种方式,我们充分利用了 Velero 在应用依赖关系和元数据管理上的优势,同时通过手动干预解决了存储层面的不兼容问题。
三、 应对五大挑战的具体转换策略
挑战1:云特定的存储驱动
-
• 问题:OpenShift 使用的存储驱动(如 awsElasticBlockStore)在 AKS 上无效。 -
• 解决方案:将 PV 定义转换为使用 Azure Disk 的 CSI 驱动。 # 在转换脚本中,将 PV 的 spec 部分修改为:
.spec.csi = {
"driver": "disk.csi.azure.com", # ✅ 使用 Azure CSI 驱动
"volumeHandle": "azure-disk-...", # 这里需要根据实际 Azure 磁盘 ID 填充
"fsType": "ext4",
"readOnly": false
}
挑战2:拓扑标签不兼容
-
• 问题:AWS 的可用区标签( topology.kubernetes.io/zone)在 AKS 上会校验失败。 -
• 解决方案:转换为 Azure 特定的拓扑标签。 # 转换 .metadata.labels
.metadata.labels = {
"topology.disk.csi.azure.com/zone": "1", # ✅ Azure 的可用区标签
"topology.disk.csi.azure.com/region": "eastus"
}
挑战3:StorageClass 不匹配
-
• 问题:OpenShift 的 StorageClass 在 AKS 中不存在。 -
• 解决方案:在 AKS 上创建与之匹配或功能对等的 StorageClass。脚本可以根据 AKS 集群的类型(区域性、单可用区、多可用区)动态创建。 # 根据 AKS 集群类型动态创建 StorageClass
case "$cluster_type" in
"regional")
echo "$REGIONAL_STORAGE_CLASS" | kubectl apply -f - ;;
"single_zone")
printf "$ZONAL_STORAGE_CLASS" "$zone" | kubectl apply -f - ;;
"multi_zonal")
printf "$MULTI_ZONAL_STORAGE_CLASS" "$zones" | kubectl apply -f - ;;
esac
挑战4:节点亲和性冲突
-
• 问题:PV 中定义的节点亲和性( nodeAffinity)引用了不存在的 Azure 可用区。 -
• 解决方案:在转换过程中移除或修改有问题的 nodeAffinity。# 使用 yq 删除节点亲和性
del(.items[].spec.nodeAffinity) # ✅ 消除可用区亲和性冲突
挑战5:元数据校验失败
-
• 问题:Azure API Server 会拒绝 AWS/GCP 特定的元数据字段。 -
• 解决方案:清理所有不兼容的云厂商特定字段。 # 删除所有云特定的存储规约
del(.items[].spec.awsElasticBlockStore, .items[].spec.gcePersistentDisk, .items[].spec.azureDisk)
四、 实战演练:迁移步骤详解
前提条件
-
• 您已经拥有一个 OpenShift 集群和一个 AKS 集群。 -
• 本地环境已安装 oc(OpenShift CLI) 和kubectl(Kubernetes CLI) 工具,并已配置好对应集群的访问凭证。
第一步:在 OpenShift 中备份数据
首先,我们需要将正在 OpenShift 上运行的 PostgreSQL Pod 中的数据备份出来。
-
1. 定位目标 Pod 和数据目录 通过
oc get pods命令找到正在运行的 PostgreSQL Pod。$ oc get pods
NAME READY STATUS RESTARTS AGE
postgresql-1-s2p8p 1/1 Running 0 2d1h进入 Pod 内部,确认数据存储的关键目录。对于标准的 PostgreSQL 容器镜像,数据通常位于
/var/lib/postgresql/data。 -
2. 执行数据同步 我们将使用
oc rsync命令。这是一个非常强大的工具,它能将运行中的 Pod 内的数据安全地同步到本地文件系统。# 命令格式: oc rsync <pod-name>:/path/to/data /local/backup/path
oc rsync postgresql-1-s2p8p:/var/lib/postgresql/data ./postgres-backup执行后,您会在本地的
postgres-backup目录下看到所有从 Pod 中同步过来的数据文件。
第二步:在 AKS 中准备环境和恢复数据
数据备份完成后,我们转向 AKS 环境,为数据恢复和应用部署做准备。
-
1. 创建持久化存储 (PVC) 在 AKS 中,我们需要先创建一个
PersistentVolumeClaim,以便让新的 Pod 能够使用持久化存储(例如 Azure Disk 或 Azure Files)。下面是一个 PVC 的 YAML 示例 (
pvc-aks.yaml):apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc-aks
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi # 存储大小应与原环境匹配或更大
storageClassName: managed-csi # 使用 AKS 默认的 StorageClass应用此配置:
kubectl apply -f pvc-aks.yaml -
2. 部署应用并恢复数据 现在,我们在 AKS 上部署 PostgreSQL。关键在于,要确保新的 Pod 使用我们刚刚创建的 PVC。
这是
StatefulSet的 YAML 示例 (postgresql-aks.yaml):apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql-aks
spec:
serviceName: "postgres-svc"
replicas: 1
selector:
matchLabels:
app: postgresql-aks
template:
metadata:
labels:
app: postgresql-aks
spec:
containers:
- name: postgresql
image: postgres:latest # 使用与原环境一致的镜像版本
ports:
- containerPort: 5432
env:
# 确保使用与原数据匹配的认证信息
- name: POSTGRES_USER
value: "youruser"
- name: POSTGRES_PASSWORD
value: "yourpassword"
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
storageClassName: managed-csi注意:在应用此文件之前,我们先不急着让 PostgreSQL 服务完全启动。因为一个空的卷会被初始化,覆盖我们即将恢复的数据。
正确的做法是:
a. 应用StatefulSet配置:kubectl apply -f postgresql-aks.yaml
b. Pod 会因为数据目录问题而启动失败或循环重启,这没关系,这给了我们一个恢复数据的窗口期。
c. 使用kubectl cp将本地备份的数据复制到这个新创建的 Pod 中。# 找到新的 Pod 名称
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgresql-aks-0 0/1 CrashLoopBackOff 2 60s
# 执行复制命令
# 命令格式: kubectl cp /local/backup/path <pod-name>:/path/to/data
kubectl cp ./postgres-backup/data postgresql-aks-0:/var/lib/postgresql/data/
第三步:验证迁移结果
数据恢复后,Kubernetes 会自动重启 Pod。这次,PostgreSQL 将会加载我们刚刚恢复的数据并成功启动。
-
1. 检查 Pod 状态 $ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgresql-aks-0 1/1 Running 0 5mPod 状态变为
Running,表明服务已正常。 -
2. 连接数据库验证数据
通过kubectl exec进入 Pod,使用psql工具亲自检查数据是否完整无误。# 进入 Pod
kubectl exec -it postgresql-aks-0 -- /bin/bash
# 连接数据库并查询
root@postgresql-aks-0:/# psql -U youruser -d yourdatabase
psql (14.5 (Debian 14.5-1.pgdg110+1))
Type "help" for help.
yourdatabase=> SELECT count(*) FROM your_table;
count
-------
12345
(1 row)如果能查询到旧数据,恭喜您,迁移成功!
五、 自动化迁移脚本 (migrate.sh)
以下是一个实现了上述逻辑的 Bash 脚本框架。它编排了整个迁移流程,从依赖检查、备份、转换到恢复和验证。
前提:运行此脚本的堡垒机或操作环境中已安装 oc, az, kubectl, velero, yq等命令行工具,并已通过身份验证可访问源和目标集群。
#!/bin/bash
# =============================================================================
# CROSS-CLOUD MIGRATION: OPENSHIFT TO AKS WITH VELERO
# This script handles cross-cloud migration of stateful workloads.
# =============================================================================
# --- CONFIGURATION ---
OCP_CLUSTER_NAME="your-openshift-cluster"
AKS_CLUSTER_NAME="your-aks-cluster"
GCS_BUCKET="your-gcs-bucket-for-velero" # Velero 使用的备份存储桶
BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
VELERO_NAMESPACE="velero"
NAMESPACES=("app1" "app2" "app3") # 需要迁移的命名空间
# --- Storage Class Definitions for AKS ---
# (此处省略了 REGIONAL/ZONAL/MULTI_ZONAL_STORAGE_CLASS 的 YAML 定义,请参考原文)
# --- Helper Functions (log, error, warning) ---
# (此处省略了日志函数,请参考原文)
# --- Core Logic Functions ---
# 检查依赖工具是否存在
check_dependencies() {
log "Verifying dependencies..."
# ... (循环检查 yq, velero, oc, kubectl, az 是否存在)
log "All dependencies verified."
}
# 备份 OpenShift 应用 (排除存储)
backup_openshift_applications() {
log "Backing up OpenShift applications (excluding storage resources)..."
velero backup create "$BACKUP_NAME" \
--include-namespaces "$(IFS=,; echo "${NAMESPACES[*]}")" \
--exclude-resources persistentvolumes,persistentvolumeclaims,storageclasses \
--wait --namespace "$VELERO_NAMESPACE"
log "Application backup completed: $BACKUP_NAME"
}
# 导出并转换存储定义
export_and_transform_storage() {
log "Exporting and transforming storage metadata using yq..."
mkdir -p "storage-backup"
# 导出并转换 PVCs
for namespace in "${NAMESPACES[@]}"; do
oc get pvc -n "$namespace" -o yaml | yq eval '...' - > "storage-backup/$namespace/pvcs-transformed.yaml"
log "Transformed PVCs for namespace: $namespace"
done
# 导出并转换 PVs
oc get pv -o yaml | yq eval '...' - > "storage-backup/pvs-transformed.yaml"
log "Storage metadata transformed for AKS compatibility."
}
# 在 AKS 上安装 Velero
install_velero_aks() {
log "Installing Velero on AKS..."
# ... (执行 velero install 命令)
log "Velero installed on AKS."
}
# 在 AKS 上配置 StorageClass
setup_aks_storage() {
log "Setting up AKS storage classes..."
# ... (根据集群类型动态创建 StorageClass)
log "AKS storage classes configured."
}
# 在 AKS 上恢复应用
restore_to_aks() {
log "Restoring applications to AKS..."
velero restore create "app-restore-${BACKUP_NAME}" --from-backup "$BACKUP_NAME" --wait
log "Applications restored to AKS."
}
# 在 AKS 上重建存储
recreate_storage_on_aks() {
log "Recreating storage on AKS from transformed files..."
kubectl apply -f "storage-backup/pvs-transformed.yaml"
for namespace in "${NAMESPACES[@]}"; do
kubectl apply -f "storage-backup/$namespace/pvcs-transformed.yaml" -n "$namespace"
done
log "Storage recreated on AKS."
}
# 验证流程 (等待 PVC 绑定、重启应用、检查 Pod 状态)
verify_migration() {
log "Waiting for PVCs to bind..."
# ... (循环检查 PVC 状态是否为 'Bound')
log "Restarting applications to mount new storage..."
# ... (滚动重启 Deployments 和 StatefulSets)
log "Verifying final migration status..."
# ... (检查所有 Pods 是否 Running, 所有 PVCs 是否 Bound)
}
# --- Main Execution ---
main() {
trap cleanup EXIT
check_dependencies
log "Starting OpenShift to AKS migration..."
# Phase 1: OpenShift Backup & Transformation
backup_openshift_applications
export_and_transform_storage
# Phase 2: AKS Restore & Storage Recreation
install_velero_aks
setup_aks_storage
restore_to_aks
recreate_storage_on_aks
# Phase 3: Verification
verify_migration
log "Migration completed successfully!"
}
main "$@"
注意:上述脚本是一个高度简化的框架,实际的 yq转换逻辑较为复杂,请参考原文中的详细实现。
六、 生产环境考量与成功指标
-
• 回滚策略:在确认 AKS 上的应用完全正常工作前,务必保留源 OpenShift 集群。验证应包括 Pod 健康检查、存储读写测试和核心业务功能验证。 -
• 安全考量:脚本本身不存储任何凭据。操作人员应在本地通过 oc login和az login进行认证。云厂商的凭据(如用于 Velero 访问 GCS/S3/Blob 的密钥)应通过 Kubernetes Secret 进行安全管理。 -
• 成功指标: -
• 迁移成功率:99.9% (相比传统方案的 40%) -
• 停机时间减少:降低 70% 的应用停机时间 -
• 存储兼容性:100% 的存储资源兼容 -
• 自动化水平:95% 的流程自动化
七、 结论
本文提出的方案代表了跨云 Kubernetes 迁移的一种思维转变。我们不再试图强行抹平云平台之间的差异,而是承认并拥抱这些差异,通过智能化的转换来解决不兼容问题。
关键的启示是:应用层面的迁移可以极大地受益于 Velero 强大的资源处理能力,而存储层面的迁移则需要我们注入云平台特定的逻辑。通过这种方式,我们为企业的有状态工作负载迁移提供了一条可预测、可靠且自动化的路径。
往期回顾
K8S工具推荐,Kargo:下一代 GitOps 持续交付工具
K8S工具推荐:Bufstream-唯一通过 Jepsen 验证的云原生 Kafka 实现
K8S工具推荐: 使用 Kubemark 进行 Kubernetes 大规模集群模拟实践
K8S工具推荐:使用Argo Rollouts实现GitOps自动化测试与回滚
K8S工具推荐:告别复杂认证!Kubernetes登录神器kubelogin指南
K8S工具推荐:Kubernetes资源优化神器KRR:一键诊断集群资源浪费
Kubernetes工具推荐:使用 k8s-pod-restart-info-collector简化故障排查
K8S工具推荐:动态无缝的Kubernetes多集群解决方案-Liqo
𝙺̲𝚞̲𝚋̲𝚎̲𝚛̲𝚗̲𝚎̲𝚝̲𝚎̲𝚜̲ 管理的最佳实践(2025)
如何落地一个企业级PaaS容器云平台:从规划到实施全流程指南

