传统环境迁移到云端?“直接搬上去就行”的误解 🚀

“服务器直接搬到AWS上不就行了吗?”

— 这是在迁移现场最常听到,也是最危险的误解。

>


🎯 本文涵盖内容

  • 传统应用程序迁移到云端前,必须进行的兼容性分析
  • AWS迁移工具(MGN)无法解决内核问题的原因
  • 可通过容器解决的案例及具体的Dockerfile示例
  • KVM不可避免的案例及AWS裸金属配置方法
  • 整理判断标准的最终决策树

📌 引言 — “搬上去”和“能运行”是两回事

在传统系统迁移项目中,经常会发生这样的情况:使用AWS MGN (Application Migration Service) 将整个服务器复制到EC2上,但应用程序甚至无法启动

原因很简单。迁移工具仅仅是一种搬运手段。它的全部作用就是以块为单位复制磁盘并将其附加到EC2。内核2.x、3.x时代的传统应用程序所固有的兼容性问题在迁移后依然存在。

更甚者,MGN在复制过程中如果检测到旧版本内核,会自动替换为Nitro兼容内核。虽然/boot/vmlinuz-3.x文件被复制了,但Nitro管理程序所需的ENA(网络)、NVMe(存储)驱动程序在旧版本内核中不存在。最终,使用原始内核启动变得不可能。

因此,在迁移之前,有一项必须完成的工作:兼容性分析。


🔍 核心问题 — 是“内核依赖”还是“用户空间依赖”?

在现场听到“这个应用程序只能在内核3.x上运行”时,首先应该怀疑。实际上,真正依赖内核本身的案例比想象中要少。大多数是用户空间的问题。

"커널 3.x에서만 됩니다"
          ↓ 실제 원인 분석
┌─────────────────────────────────┐
│ glibc 버전 종속       ← 90% 이 케이스 (userspace)
│ .so 라이브러리 종속   ← userspace
│ 런타임 버전 종속      ← userspace
│ 실제 커널 syscall 종속 ← 진짜 커널 문제 (희귀)
└─────────────────────────────────┘

区分这一点的最快方法是以下两个命令。

# 追踪应用程序使用的syscall列表
strace -c ./legacy-app

# 检查应用程序链接的库和所需的glibc版本
ldd ./legacy-app
objdump -p ./legacy-app | grep GLIBC

如果ldd结果中出现GLIBC_2.12、GLIBC_2.17等版本符号,则表示是用户空间依赖。在这种情况下,可以通过容器解决。


🐳 案例1 — 用户空间(glibc)依赖:通过容器解决

理解容器的核心结构,就能立即明白为什么这可以解决问题。

┌──────────────────────────────────┐
│  컨테이너 이미지 내부             │
│  ┌────────────────────────────┐  │
│  │  레거시 앱                  │  │
│  │  glibc 2.12 (구버전)        │  │  ← 이미지 안에 격리
│  │  필요한 .so 라이브러리들     │  │
│  └────────────────────────────┘  │
├──────────────────────────────────┤
│  호스트 커널 (5.x / 6.x)         │  ← glibc와 무관
└──────────────────────────────────┘

容器只共享内核,而包括glibc在内的所有用户空间库都独立地打包在镜像中。即使宿主内核是6.x,容器内部的glibc 2.12也能正常运行。

旧版本glibc基础镜像选择标准

基础镜像 glibc版本 备注
centos:6 2.12 内核2.6时代的用户空间
centos:7 2.17 最常用的传统基础镜像
ubuntu:14.04 2.19
ubuntu:16.04 2.23
debian:jessie 2.19

###

实际示例 — 容器化glibc 2.12依赖应用程序

首先,检查应用程序所需的glibc版本。

objdump -p ./legacy-app | grep GLIBC
# 输出示例:
# GLIBC_2.12
# GLIBC_2.5

如果GLIBC_2.12是最大版本,则使用centos:6基础镜像。

# 容器化需要glibc 2.12环境的传统应用程序
FROM centos:6

# 安装必要的依赖库(根据应用程序调整)
RUN yum install -y 
    libstdc++ 
    libgcc 
    openssl 
    && yum clean all

# 复制应用程序二进制文件
COPY ./legacy-app /opt/app/legacy-app
RUN chmod +x /opt/app/legacy-app

# 如果有配置文件,一并复制
COPY ./config /opt/app/config

WORKDIR /opt/app
CMD ["./legacy-app"]

如果需要额外库 — 直接打包.so文件

如果应用程序直接依赖于无法通过包管理器安装的特定.so文件,也可以直接复制这些文件。

FROM centos:7

# 直接从现有服务器复制所需的.so文件
COPY ./libs/liblegacy.so.1.2.3 /usr/lib64/liblegacy.so.1.2.3
RUN ldconfig  # 更新动态链接器缓存

COPY ./legacy-app /opt/app/legacy-app
RUN chmod +x /opt/app/legacy-app

CMD ["/opt/app/legacy-app"]

容器运行验证

# 构建镜像
docker build -t legacy-app:v1 .

# 在容器内部使用ldd检查库链接
docker run --rm legacy-app:v1 ldd /opt/app/legacy-app

# 实际运行测试
docker run --rm legacy-app:v1

# 使用strace检查syscall是否正常工作(调试时)
docker run --rm --cap-add SYS_PTRACE legacy-app:v1 
  strace -c /opt/app/legacy-app

直至AWS ECS部署

容器化完成后,将其上传到ECR并使用ECS进行部署。

# ECR登录
aws ecr get-login-password --region ap-northeast-2 | 
  docker login --username AWS 
  --password-stdin <account-id>.dkr.ecr.ap-northeast-2.amazonaws.com

# 标记并推送镜像
docker tag legacy-app:v1 
  <account-id>.dkr.ecr.ap-northeast-2.amazonaws.com/legacy-app:v1
docker push 
  <account-id>.dkr.ecr.ap-northeast-2.amazonaws.com/legacy-app:v1

🖥️ 案例2 — 真正的内核依赖:KVM不可避免的情况

以下情况无法通过容器解决。

  • 需要直接加载内核模块(insmod)的应用程序
  • 直接调用现代内核中已移除的syscall的应用程序
  • 以内核版本特定方式硬编码访问/dev、/proc的应用程序
  • 内核2.x依赖(Nitro管理程序本身要求4.x+驱动)

在这种情况下,EC2裸金属 + KVM嵌套虚拟化是标准做法。

┌─────────────────────────────────┐
│      EC2 Bare Metal             │
│   (i3.metal, m5.metal 등)       │
│                                 │
│  ┌───────────────────────────┐  │
│  │  Host OS (Amazon Linux 2) │  │
│  │  KVM + QEMU               │  │
│  │  ┌─────────────────────┐  │  │
│  │  │  Guest VM            │  │  │
│  │  │  커널 3.x 직접 설치   │  │  │
│  │  │  레거시 앱 실행       │  │  │
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

普通EC2(Nitro VM)无法更改内核,但裸金属实例可以直接访问硬件而无需管理程序,因此可以直接运行KVM。

# 在Amazon Linux 2上安装KVM
yum install -y qemu-kvm libvirt libvirt-python libguestfs-tools
systemctl enable --now libvirtd

# 检查KVM支持
lscpu | grep Virtualization

# 使用传统操作系统镜像启动VM
qemu-system-x86_64 
  -enable-kvm 
  -m 4096 
  -cpu host 
  -drive file=/var/lib/libvirt/images/legacy.qcow2,format=qcow2 
  -net nic -net user

但是,裸金属实例的成本相当高。

实例 规格 按需费用
m5.metal 96 vCPU, 384GB 约$5/小时
i3.metal 72 vCPU, 512GB 约$4.99/小时

为了一个传统应用程序支付如此高的费用是否合理,需要事先进行评估。从长远来看,应用程序移植通常更经济


⚠️ 注意事项 — 常见错误

1. 未经strace/ldd分析就决定“使用KVM” 令人惊讶的是,许多用户空间依赖的案例却配置了虚拟机。分析必须先行。

2. 对centos:6基础镜像EOL缺乏认识 CentOS 6已于2020年11月EOL。由于没有安全补丁,需要遵循最小化外部暴露、内部网络运行原则

3. 忽略glibc版本符号检查 不能只看ldd。需要通过`objdump -p | grep GLIBC`来确认符号版本,才能准确选择基础镜像。

4. 误解AWS MGN能解决兼容性问题 MGN只是一个复制工具。兼容性问题仍然需要手动解决。


✅ 最终决策树

레거시 앱 클라우드 이전
          ↓
   strace / ldd 분석
          ↓
userspace 종속?
(glibc, .so, 런타임)
   ↓ YES
   컨테이너 (구버전 base 이미지) ✅
          ↓ NO
커널 모듈 / 제거된 syscall?
   ↓ YES
   EC2 Bare Metal + KVM ✅
          ↓ NO
소스코드 있음?
   ↓ YES
   재컴파일 / 포팅 (근본 해결) ✅
          ↓ NO
User-mode Linux (UML) 검토 △

🏁 总结

传统迁移的核心是没有分析就没有迁移

当听到“内核版本必须匹配”时,不要立即转向虚拟机配置,而应首先使用strace和ldd来识别依赖的性质。实际上,容器能解决的案例要多得多,而且成本和运营负担也轻得多。

如果确实是需要内核模块或已移除syscall的真正内核依赖案例,KVM是不可避免的,但即使在这种情况下,也应牢记应用程序移植是长期的正确解决方案


Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注