引言
企业数字化转型中,传统应用上云是必经之路。
但迁移过程面临诸多挑战:
- 如何评估迁移风险?
- 如何选择迁移策略?
- 如何保证数据一致性?
- 如何最小化业务中断?
本文基于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 SMS | VM 迁移 | 增量同步 |
| Azure Migrate | 混合云 | 评估 + 迁移 |
| 阿里云 SMC | 上云迁移 | 一键迁移 |
| VMware HCX | VMware 环境 | 热迁移 |
四、Replatform 迁移(平台优化)
改造要点
-
数据库上云
# 从自建 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:... -
缓存上云
# 从本地 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} -
对象存储
// 从本地文件存储迁移到 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/负载均衡到旧环境
- 停止数据同步
- 验证旧环境
- 通知相关人员
八、迁移验证
验证清单
## 功能验证
- [ ] 核心功能测试
- [ ] 接口兼容性测试
- [ ] 性能基准测试
- [ ] 安全测试
## 数据验证
- [ ] 数据完整性
- [ ] 数据一致性
- [ ] 索引有效性
- [ ] 存储过程/触发器
## 运维验证
- [ ] 监控告警
- [ ] 日志收集
- [ ] 备份恢复
- [ ] 扩缩容测试
## 业务验证
- [ ] 业务流程测试
- [ ] 用户体验测试
- [ ] 第三方集成
九、总结
迁移成功要素
-
充分评估
- 应用依赖分析
- 风险评估
- 成本评估
-
渐进式迁移
- 先非核心后核心
- 先读后写
- 可回滚
-
自动化
- CI/CD 流水线
- 基础设施即代码
- 自动化测试
-
监控保障
- 全链路监控
- 业务指标监控
- 告警响应
常见陷阱
- ❌ 低估数据迁移复杂度
- ❌ 忽视网络延迟影响
- ❌ 未充分测试回滚方案
- ❌ 迁移后未优化成本