Skip to content
清晨的一缕阳光
返回

云原生迁移实战:传统应用上云指南

引言

企业数字化转型中,传统应用上云是必经之路。

但迁移过程面临诸多挑战:

本文基于6R 迁移模型,详解云原生迁移的完整流程与实战经验。


一、6R 迁移模型

迁移策略选择

graph LR
    A[应用评估] --> B{迁移策略}
    B --> C[Rehost 重新托管]
    B --> D[Replatform 平台重构]
    B --> E[Refactor 重构]
    B --> F[Retire 退役]
    B --> G[Retain 保留]
    B --> H[Repurchase 重新购买]

6R 策略对比

策略说明工作量风险适用场景
Rehost直接迁移(Lift & Shift)快速上云
Replatform平台优化利用云服务
Refactor架构重构云原生改造
Retire退役下线-废弃系统
Retain暂时保留-暂不迁移
Repurchase购买 SaaS通用系统

二、迁移前评估

1. 应用画像

# 应用评估清单
application:
  name: 用户管理系统
  current_state:
    deployment: 物理机
    os: CentOS 7
    runtime: JDK 8
    framework: Spring Boot 2.1
    database: MySQL 5.7
    dependencies:
      - Redis 缓存
      - RabbitMQ 消息
      - LDAP 认证
  
  metrics:
    qps: 500
    tps: 100
    latency_p99: 200ms
    availability: 99.5%
    data_size: 500GB
  
  constraints:
    - 依赖 LDAP(内部)
    - 数据库存储过程
    - 硬编码 IP 地址
    - 日志本地存储

2. 依赖分析

# 使用工具分析依赖
# 1. 应用依赖
mvn dependency:tree > dependencies.txt

# 2. 数据库依赖
pt-query-digest --review /var/log/mysql/slow.log

# 3. 网络依赖
tcpdump -i any -w capture.pcap

# 4. 配置文件扫描
grep -r "192.168" config/

3. 迁移风险评估

风险项风险等级缓解措施
数据库迁移双写 + 数据校验
配置硬编码配置中心改造
会话保持Redis 集中存储
文件存储OSS 迁移
第三方依赖服务网格隔离

三、Rehost 迁移(直接迁移)

适用场景

实施步骤

sequenceDiagram
    participant OnPrem as 本地环境
    participant Cloud as 云环境
    participant LB as 负载均衡
    
    OnPrem->>Cloud: 1. 镜像/VM 迁移
    Cloud->>Cloud: 2. 网络配置
    Cloud->>Cloud: 3. 启动验证
    LB->>Cloud: 4. 流量切换
    Cloud->>OnPrem: 5. 数据同步(可选)

工具选择

工具适用场景特点
AWS SMSVM 迁移增量同步
Azure Migrate混合云评估 + 迁移
阿里云 SMC上云迁移一键迁移
VMware HCXVMware 环境热迁移

四、Replatform 迁移(平台优化)

改造要点

  1. 数据库上云

    # 从自建 MySQL 迁移到 RDS
    # 1. 创建 RDS 实例
    aws rds create-db-instance \
      --db-instance-identifier prod-mysql \
      --db-instance-class db.r5.large \
      --engine mysql \
      --engine-version 8.0 \
      --master-username admin \
      --master-user-password <password> \
      --allocated-storage 500
    
    # 2. 使用 DMS 迁移
    aws dms create-replication-task \
      --replication-task-identifier mysql-migration \
      --source-endpoint-arn arn:aws:dms:... \
      --target-endpoint-arn arn:aws:dms:...
  2. 缓存上云

    # 从本地 Redis 迁移到 ElastiCache
    # application.yml 修改
    spring:
      redis:
        # 修改前
        host: 192.168.1.100
        port: 6379
        
        # 修改后
        host: prod-cache.xxxxxx.ng.0001.apsh-1.cache.amazonaws.com
        port: 6379
        ssl: true
        password: ${REDIS_PASSWORD}
  3. 对象存储

    // 从本地文件存储迁移到 S3
    // 改造前
    public void saveFile(MultipartFile file) {
        String path = "/data/uploads/" + file.getOriginalFilename();
        Files.write(Paths.get(path), file.getBytes());
    }
    
    // 改造后
    public void saveFile(MultipartFile file) {
        String key = "uploads/" + file.getOriginalFilename();
        s3Client.putObject(bucket, key, file.getInputStream(), 
          file.getSize(), null);
    }

五、Refactor 迁移(云原生重构)

容器化改造

1. Dockerfile 编写

# 多阶段构建
FROM maven:3.9-eclipse-temurin-17 AS build

WORKDIR /build
COPY pom.xml .
COPY src ./src

RUN mvn clean package -DskipTests

# 运行镜像
FROM eclipse-temurin:17-jre-alpine

LABEL maintainer="devops@example.com"

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 复制 JAR
COPY --from=build /build/target/app.jar app.jar

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1

USER appuser

ENTRYPOINT ["java", "-jar", "app.jar"]

2. Kubernetes 部署

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
        version: v1
    spec:
      containers:
        - name: user-service
          image: registry.example.com/user-service:1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: "prod"
            - name: DB_HOST
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: db.host
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: app-secret
                  key: db.password
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: 1000m
              memory: 1Gi
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 5

微服务拆分

graph TB
    subgraph Monolith["单体应用"]
        A[用户模块]
        B[订单模块]
        C[商品模块]
        D[支付模块]
        A --- B --- C --- D
    end
    
    subgraph Microservices["微服务"]
        E[用户服务]
        F[订单服务]
        G[商品服务]
        H[支付服务]
        E --> I[(用户 DB)]
        F --> J[(订单 DB)]
        G --> K[(商品 DB)]
        H --> L[(支付 DB)]
    end
    
    Monolith -.->|重构 | Microservices

六、数据库迁移

迁移策略

1. 一次性迁移

# 适用:小数据量、可接受停机

# 1. 停止应用
kubectl scale deployment user-service --replicas=0

# 2. 导出数据
mysqldump -h old-db -u root -p \
  --single-transaction \
  --master-data=2 \
  user_db > dump.sql

# 3. 导入数据
mysql -h new-rds -u admin -p user_db < dump.sql

# 4. 验证数据
mysqltuner --host new-rds

# 5. 启动应用
kubectl scale deployment user-service --replicas=3

2. 双写迁移

// 适用:大数据量、低停机要求

@Service
public class DualWriteUserService implements UserService {
    
    @Autowired
    private UserRepository oldRepository;
    
    @Autowired
    private UserRepository newRepository;
    
    @Override
    public User createUser(User user) {
        // 双写
        User oldUser = oldRepository.save(user);
        User newUser = newRepository.save(user);
        
        // 记录差异
        if (!oldUser.getId().equals(newUser.getId())) {
            log.warn("ID mismatch: old={}, new={}", 
              oldUser.getId(), newUser.getId());
        }
        
        return newUser;
    }
    
    @Override
    public Optional<User> findById(Long id) {
        // 读新库,失败降级到旧库
        return newRepository.findById(id)
          .or(() -> oldRepository.findById(id));
    }
}

3. CDC 同步迁移

# 使用 Debezium 进行 CDC 同步
apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaConnector
metadata:
  name: mysql-source-connector
spec:
  class: io.debezium.connector.mysql.MySqlConnector
  tasksMax: 1
  config:
    database.hostname: old-mysql
    database.port: 3306
    database.user: debezium
    database.password: ${DB_PASSWORD}
    database.server.id: 184054
    database.server.name: mysql-server
    database.include.list: user_db
    table.include.list: user_db.users,user_db.orders
    topic.prefix: mysql-server
    schema.history.internal.kafka.bootstrap.servers: kafka:9092
    schema.history.internal.kafka.topic: schema-changes.user_db

七、数据一致性保障

1. 数据校验

# pt-table-checksum 校验
pt-table-checksum \
  --host=old-mysql \
  --replicate=test.checksums \
  --databases=user_db \
  --tables=users,orders

# 数据量对比
SELECT 
  table_name,
  table_rows,
  data_length,
  index_length
FROM information_schema.tables
WHERE table_schema = 'user_db';

2. 回滚方案

# 回滚流程
rollback:
  trigger_conditions:
    - 错误率 > 5%
    - 延迟 P99 > 500ms
    - 数据不一致 > 100 条
  
  steps:
    - 停止新应用
    - 切换 DNS/负载均衡到旧环境
    - 停止数据同步
    - 验证旧环境
    - 通知相关人员

八、迁移验证

验证清单

## 功能验证
- [ ] 核心功能测试
- [ ] 接口兼容性测试
- [ ] 性能基准测试
- [ ] 安全测试

## 数据验证
- [ ] 数据完整性
- [ ] 数据一致性
- [ ] 索引有效性
- [ ] 存储过程/触发器

## 运维验证
- [ ] 监控告警
- [ ] 日志收集
- [ ] 备份恢复
- [ ] 扩缩容测试

## 业务验证
- [ ] 业务流程测试
- [ ] 用户体验测试
- [ ] 第三方集成

九、总结

迁移成功要素

  1. 充分评估

    • 应用依赖分析
    • 风险评估
    • 成本评估
  2. 渐进式迁移

    • 先非核心后核心
    • 先读后写
    • 可回滚
  3. 自动化

    • CI/CD 流水线
    • 基础设施即代码
    • 自动化测试
  4. 监控保障

    • 全链路监控
    • 业务指标监控
    • 告警响应

常见陷阱


分享这篇文章到:

上一篇文章
多云部署实战:跨云架构设计与落地
下一篇文章
消息队列实战