大数跨境
0
0

跨云迁移实战:从 OpenShift 到 AKS 的有状态工作负载迁移实践指南

跨云迁移实战:从 OpenShift 到 AKS 的有状态工作负载迁移实践指南 云原生SRE
2025-11-20
2

 

欢迎点击下方👇关注我,记得星标哟~

文末会有重磅福利赠送


摘要:
将运行在 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. 1. 云厂商特定的存储驱动:AWS EBS、GCP Persistent Disk 和 Azure Disk 的 CSI (Container Storage Interface) 驱动实现各不相同。
  2. 2. 可用区/地域不兼容:us-east-1a是 AWS 的地理标签,在 Azure 中没有对应的概念。
  3. 3. 拓扑标签差异:各个云平台使用自己的一套拓扑标签来标识节点位置。
  4. 4. CSI 驱动不兼容:不同云的 CSI 实现细节不同,无法直接通用。
  5. 5. 元数据校验失败:AKS 的 API Server 会拒绝包含 AWS 或 GCP 特定字段(如 awsElasticBlockStore)的资源定义。

二、 解决方案核心关注点

我们的核心策略是将应用资源的迁移与存储资源的创建解耦

  1. 1. 备份应用,排除存储:使用 Velero 仅备份应用层面的资源(如 Deployments, StatefulSets, Services, ConfigMaps 等),并明确排除 persistentvolumespersistentvolumeclaims, 和 storageclasses
    
          
           
          velero backup create $BACKUP_NAME \
      --exclude-resources persistentvolumes,persistentvolumeclaims,storageclasses
  2. 2. 导出并转换存储定义:从源集群手动导出 PV 和 PVC 的 YAML 文件,然后通过脚本(例如使用 yq工具)将其转换为 AKS 兼容的格式。
    
          
           
          oc get pv -o yaml | yq eval '...transformation logic...' > transformed-pvs.yaml
  3. 3. 恢复应用:在目标 AKS 集群上,使用 Velero 恢复应用资源。
    
          
           
          velero restore create $RESTORE_NAME --from-backup $BACKUP_NAME
  4. 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. 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. 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. 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. 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. 1. 检查 Pod 状态
    
          
           
          $ kubectl get pods
    NAME                 READY   STATUS    RESTARTS   AGE
    postgresql-aks-0     1/1     Running   0          5m

    Pod 状态变为 Running,表明服务已正常。

  2. 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 脚本框架。它编排了整个迁移流程,从依赖检查、备份、转换到恢复和验证。

前提:运行此脚本的堡垒机或操作环境中已安装 ocazkubectlveleroyq等命令行工具,并已通过身份验证可访问源和目标集群。


   
    
   #!/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工具推荐:资源编排新利器:三大云厂商联合推出 KRO

K8S工具推荐:告别复杂认证!Kubernetes登录神器kubelogin指南

K8S工具推荐:Kubernetes资源优化神器KRR:一键诊断集群资源浪费

Kubernetes工具推荐:使用 k8s-pod-restart-info-collector简化故障排查

K8S工具推荐:动态无缝的Kubernetes多集群解决方案-Liqo

K8S学习路线2025

𝙺̲𝚞̲𝚋̲𝚎̲𝚛̲𝚗̲𝚎̲𝚝̲𝚎̲𝚜̲ 管理的最佳实践(2025)

如何落地一个企业级PaaS容器云平台:从规划到实施全流程指南


【声明】内容源于网络
0
0
云原生SRE
懂点K8S的SRE,关注云原生、DevOps、AI&ChatGPT等技术热点
内容 313
粉丝 0
云原生SRE 懂点K8S的SRE,关注云原生、DevOps、AI&ChatGPT等技术热点
总阅读41
粉丝0
内容313