亚马逊AWS官方博客

跨架构EC2实例升级指南:基于ENI迁移的Xen到Nitro升级方案

概述

随着AWS不断推出性能更优、成本更低的新一代EC2实例类型,许多客户希望将现有工作负载从旧一代实例迁移到新一代实例。然而,从基于Xen虚拟化的C4实例升级到基于AWS Nitro System的C7i实例时,由于底层虚拟化架构和操作系统兼容性的差异,无法使用原地升级的方式,需要采用迁移升级策略。

本文将介绍一种基于ENI(Elastic Network Interface)分离技术的实例升级方案。该方案的核心理念是保留而非重建,来满足无法直接创建新实例的场景(如保留私有IP不变),通过分离和重新附加网络接口和数据卷,实现网络配置和业务数据的完整迁移。

方案核心优势

  • 网络配置零变更:新实例继承旧实例的公网IP、私有IP、安全组和子网配置
  • 业务数据完整保留:EBS数据卷无缝迁移,确保所有业务数据、配置文件不丢失
  • 上下游影响小:IP地址和网络配置保持不变,无需通知和协调其他系统进行变更
  • 停机时间最小化:通过预先准备和快速切换,将停机时间控制在分钟级别
  • 操作标准化:所有步骤均使用AWS CLI,易于脚本化和批量执行

适用场景

本方案适用于以下场景:

1. 跨虚拟化架构升级

从Xen架构(如C4、M4、R4等)升级到Nitro架构(如C7i、M7i、R7i等),由于以下技术差异无法使用原地升级方式:

虚拟化架构差异

  • Xen架构实例(C4、M4、R4等)使用传统的Xen虚拟化技术
  • Nitro架构实例(C7i、M7i、R7i等)基于AWS Nitro System,提供接近裸机的性能

操作系统兼容性差异

  • 网络驱动:Nitro架构需要ENA(Elastic Network Adapter)驱动支持,旧系统可能缺少或版本过低
  • 存储驱动:Nitro架构实例使用NVMe接口访问EBS卷,需要NVMe驱动支持,而Xen架构实例使用传统的块设备接口
  • 启动模式:C7i支持UEFI启动模式,可能与旧系统的Legacy BIOS启动模式不兼容
  • 内核要求:Nitro架构对Linux内核版本有更高要求(如RedHat/Centos建议18+),部分旧系统内核版本过低
  • 虚拟化层差异:从Xen半虚拟化到Nitro硬件虚拟化的转换,可能导致某些系统级应用不兼容

2. 业务系统对IP地址有强依赖

在许多业务场景中,上下游系统会对节点IP有严格的依赖关系:

  • 微服务架构:服务注册中心记录了固定的服务IP地址
  • 数据库连接:应用程序配置文件中硬编码了数据库服务器IP
  • 防火墙规则:安全策略基于特定IP地址配置白名单
  • 第三方集成:外部系统通过IP地址进行服务调用和认证
  • 负载均衡配置:上游负载均衡器配置了固定的后端服务器IP

3. 多租户环境的IP冲突风险

在AWS账号承载多个业务的场景中,存在以下挑战:

  • 并发资源申请:多个租户同时申请EC2资源
  • IP地址竞争:直接停止实例后申请新实例,原内网IP可能被其他租户占用
  • 业务隔离需求:不同业务线需要保持独立的网络标识

4. 数据和配置保留需求

  • 有状态应用:应用数据存储在EBS数据卷中,需要完整保留
  • 复杂网络配置:实例配置了多个安全组、网络ACL等,希望完整继承
  • 合规性要求:某些行业要求保持固定的网络标识用于审计追踪
  • 最小化业务中断:需要将停机时间控制在最短范围内

方案架构

升级方案的架构示意如下:

核心原理

本方案的核心思想是利用AWS EC2的两个关键特性来实现无缝迁移:

  1. ENI的可分离性:EC2实例的主ENI(Elastic Network Interface)可以在实例终止后保留,并附加到新实例上。ENI携带了实例的完整网络身份:
  • 私有IP地址:保持内网通信地址不变
  • 弹性IP地址(EIP:保持公网访问地址不变
  • 安全组配置:完整继承防火墙规则
  • 子网和VPC关联:保持网络拓扑关系
  1. EBS数据卷的可分离性:EBS数据卷可以从旧实例分离并挂载到新实例,实现数据的完整迁移:
  • 业务数据保留:所有存储在数据卷中的业务数据完整保留
  • 配置文件保留:应用配置、日志文件等重要数据不丢失
  • 数据库文件保留:数据库文件和索引完整迁移

为什么必须使用ENI分离方案?

传统升级方式的问题

如果采用传统的”终止-启动新实例”方式,会面临以下风险:

  1. IP地址不可控
  • 直接启动新实例,可能会获取到不同的私有IP:
# 传统方式的问题示例
# 终止旧实例
aws ec2 terminate-instances —instance-ids i-old123
   
# 启动新实例 - IP地址可能发生变化!
aws ec2 run-instances —image-id ami-new456 —instance-type c7i.xlarge
# 新实例可能获得不同的私有IP:172.31.1.100 → 172.31.1.200
  • EC2启动时可以指定使用原有的私有IP,但在一些IP竞争的场景下,此方法则失效:
    • 在多租户环境下,在停止旧实例到启动新实例的时间窗口内,原IP地址会被释放回IP池;其他租户的实例可能抢占这个IP地址;即使在启动命令指定原来的IP,由于争用机制,无法保证获取到。
    • 在自动扩缩容(如auto scaling group)的场景下,如有其他业务触发了该子网扩容操作,占用了该私有IP,也会导致保留私有IP的操作失效。
  1. 业务中断风险
  • 上游系统需要更新配置指向新IP
  • SecurityGroup, NetworkACL等规则需要重新配置
  • 服务发现机制需要重新注册
  • DNS记录可能需要更新

ENI分离方案的优势

通过ENI分离方案,可以完美解决上述问题:

# ENI分离方案确保IP不变
# 1. 保留ENI(包含IP地址)
aws ec2 modify-network-interface-attribute \
    --network-interface-id eni-12345 \
    --attachment DeleteOnTermination=false

# 2. 终止旧实例(ENI被保留)
aws ec2 terminate-instances --instance-ids i-old123

# 3. 新实例使用相同ENI(IP地址完全一致)
aws ec2 run-instances \
    --image-id ami-new456 \
    --instance-type c7i.xlarge \
    --network-interfaces '[{"DeviceIndex":0,"NetworkInterfaceId":"eni-12345"}]'

关键优势

  • IP地址100%不变:私有IP和公网IP完全保持一致
  • 零配置变更:上下游系统无需任何调整
  • 避免IP竞争:ENI绑定确保IP不会被其他实例占用
  • 网络配置继承:安全组、子网、VPC配置自动继承

升级流程概览

整个升级过程分为三个主要阶段:

阶段一:准备新实例(业务不中断)

  1. 启动新的C7i实例并进行环境配置
  2. 安装必要的软件和依赖
  3. 验证应用在C7i架构上的兼容性

阶段二:ENI和数据迁移(短暂停机)

  1. 停止旧实例,保留ENI和数据卷
  2. 从旧实例分离EBS数据卷
  3. 修改ENI删除行为并终止旧实例
  4. 使用保留的ENI启动新C7i实例
  5. 将数据卷挂载到新实例

阶段三:验证和清理

  1. 验证网络配置和数据完整性
  2. 启动业务应用并进行功能验证
  3. 清理临时资源

前提条件

在开始升级之前,请确保满足以下条件:

  1. AWS CLI已安装和配置:参考AWS CLI安装文档
  2. 旧实例配置了Elastic IP:确保实例有固定的公网IP地址
  3. 数据存储在EBS数据卷:业务数据应存储在独立的EBS数据卷中,而非根卷
  4. VPC有可用IP地址:确保子网中有足够的可用IP地址
  5. 已选择合适的AMI:确定新实例使用的操作系统AMI ID
  6. 具有必要的IAM权限:确保有权限执行EC2相关操作

详细实施步骤

如下实施步骤的操作,通过AWS CLI方式提供,均支持在Cloud Shell中执行,方便运维人员快速使用。

准备工作:设置环境变量

为了简化操作并减少错误,我们首先设置所有需要的环境变量:

# 旧实例信息
export OLD_INSTANCE_ID="i-0f9566b26d4ae5cea"
export REGION="ap-southeast-1"

# 新实例配置
export NEW_AMI_ID="ami-xxxxxxxxx"  # C7i兼容的AMI
export INSTANCE_TYPE="c7i.xlarge"
export KEY_PAIR_NAME="your-key-pair-name"
export INSTANCE_NAME="your-instance-name"
export SSH_KEY_PATH="~/.ssh/your-key.pem"

# 验证配置
echo "旧实例ID: $OLD_INSTANCE_ID"
echo "区域: $REGION"
echo "新实例类型: $INSTANCE_TYPE"

第一步:收集旧实例信息

在进行任何变更之前,我们需要收集旧实例的所有关键信息,包括网络配置、存储配置等。

1.1 获取根卷设备名

export ROOT_DEVICE=$(aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $OLD_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].RootDeviceName' \
    --output text)

echo "根卷设备名: $ROOT_DEVICE"

对于CentOS或Debian,命令返回根分区的名字格式一般如下:

/dev/xvda

1.2 获取数据卷信息

aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $OLD_INSTANCE_ID \
    --query "Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName!=\`$ROOT_DEVICE\`]"

根据输出结果,手动保存数据卷信息(如果有多个数据卷,需要保存多个):

export DATA_VOLUME_ID="vol-04cd406fe431e5bb9"
export DATA_DEVICE_NAME="/dev/sdb"

示例输出:

[
    {
        "DeviceName": "/dev/sdb",
        "Ebs": {
            "AttachTime": "2025-11-12T05:25:03+00:00",
            "DeleteOnTermination": false,
            "Status": "attached",
            "VolumeId": "vol-04cd406fe431e5bb9"
        }
    }
]

1.3 获取主ENI信息

这是最关键的一步,我们需要获取旧实例的主ENI信息,包括ENI ID、子网、VPC等:

# 获取主ENI的详细信息
ENI_INFO=$(aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $OLD_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].NetworkInterfaces[?Attachment.DeviceIndex==`0`].[Attachment.AttachmentId,NetworkInterfaceId,SubnetId,VpcId]' \
    --output text)

# 解析并保存到变量
export OLD_ATTACHMENT_ID=$(echo $ENI_INFO | awk '{print $1}')
export OLD_PRIMARY_ENI_ID=$(echo $ENI_INFO | awk '{print $2}')
export OLD_SUBNET_ID=$(echo $ENI_INFO | awk '{print $3}')
export OLD_VPC_ID=$(echo $ENI_INFO | awk '{print $4}')
echo "Attachment ID: $OLD_ATTACHMENT_ID"
echo "主ENI ID: $OLD_PRIMARY_ENI_ID"
echo "子网ID: $OLD_SUBNET_ID"
echo "VPC ID: $OLD_VPC_ID"

示例输出:

Attachment ID: eni-attach-0ea954b8ace63193c
主ENI ID: eni-064d1bbe65a26ed75
子网ID: subnet-09b02e5019aa19067
VPC ID: vpc-0dd5dd221676f6b08

1.4 获取安全组信息

export OLD_SECURITY_GROUP_ID=$(aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $OLD_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].NetworkInterfaces[?Attachment.DeviceIndex==`0`].Groups[0].GroupId' \
    --output text)
echo "安全组ID: $OLD_SECURITY_GROUP_ID"

示例输出:

安全组ID: sg-0d83f15034c1c984d

第二步:启动并初始化新实例

现在我们启动一个临时的C7i实例,用于安装软件、配置环境和测试兼容性。

2.1 启动新实例

使用从旧实例获取的网络配置启动新实例:

export NEW_INSTANCE_ID=$(aws --region $REGION --no-cli-pager ec2 run-instances \
    --image-id "$NEW_AMI_ID" \
    --instance-type "$INSTANCE_TYPE" \
    --key-name "$KEY_PAIR_NAME" \
    --network-interfaces "[
        {
            \"DeviceIndex\": 0,
            \"SubnetId\": \"$OLD_SUBNET_ID\",
            \"Groups\": [\"$OLD_SECURITY_GROUP_ID\"],
            \"AssociatePublicIpAddress\": true,
            \"DeleteOnTermination\": true
        }]" \
    --block-device-mappings '[
        {
            "DeviceName": "/dev/xvda",
            "Ebs": {
                "VolumeSize": 30,
                "VolumeType": "gp3",
                "DeleteOnTermination": true
            }
        }
    ]' \
    --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=\"$INSTANCE_NAME-temp\"}]" \
    --query 'Instances[0].InstanceId' \
    --output text)
echo "新实例ID: $NEW_INSTANCE_ID"

2.2 等待实例启动并获取公网IP

echo "等待新实例启动..."
aws --region $REGION --no-cli-pager ec2 wait instance-status-ok --instance-ids $NEW_INSTANCE_ID

# 获取公网IP
export NEW_TEMP_PUBLIC_IP=$(aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $NEW_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].PublicIpAddress' \
    --output text)
echo "新实例临时公网IP: $NEW_TEMP_PUBLIC_IP"

2.3 初始化新实例

登录新实例并进行必要的初始化:

ssh -i $SSH_KEY_PATH admin@$NEW_TEMP_PUBLIC_IP

在新实例上执行以下操作,以CentOS 为例:

# 更新系统
sudo yum update -y

# 安装必要的软件
sudo yum install -y <your-required-packages>

# 配置应用环境
# ... 根据实际需求进行配置

# 测试应用兼容性
# ... 运行测试脚本验证应用在机器上正常工作

重要提示:这一步是验证应用在新实例类型上兼容性的关键阶段。建议进行充分的测试,包括:

  • 性能测试
  • 功能测试
  • 依赖项验证

第三步:创建AMI备份

在进行任何破坏性操作之前,我们为新旧实例都创建AMI备份,以便在出现问题时快速恢复。

3.1 备份新实例

export NEW_INSTANCE_AMI_ID=$(aws --region $REGION --no-cli-pager ec2 create-image \
    --instance-id $NEW_INSTANCE_ID \
    --name "backup-$NEW_INSTANCE_ID-$(date +%Y%m%d%H%M%S)" \
    --description "Backup before ENI detach from instance $NEW_INSTANCE_ID" \
    --query 'ImageId' \
    --output text)
echo "新实例AMI ID: $NEW_INSTANCE_AMI_ID"

# 等待AMI创建完成
echo "等待新实例AMI创建完成..."
aws --region $REGION --no-cli-pager ec2 wait image-available --image-ids $NEW_INSTANCE_AMI_ID
echo "新实例AMI创建完成"

# 验证AMI状态,成功则返回available
aws --region $REGION --no-cli-pager ec2 describe-images \
    --image-ids $NEW_INSTANCE_AMI_ID \
    --query 'Images[0].State' \
    --output text

3.2 备份旧实例

export OLD_INSTANCE_AMI_ID=$(aws --region $REGION --no-cli-pager ec2 create-image \
    --instance-id $OLD_INSTANCE_ID \
    --name "backup-$OLD_INSTANCE_ID-$(date +%Y%m%d%H%M%S)" \
    --description "Backup before ENI detach from instance $OLD_INSTANCE_ID" \
    --query 'ImageId' \
    --output text)
echo "旧实例AMI ID: $OLD_INSTANCE_AMI_ID"

# 等待AMI创建完成
echo "等待旧实例AMI创建完成..."
aws --region $REGION --no-cli-pager ec2 wait image-available --image-ids $OLD_INSTANCE_AMI_ID
echo "旧实例AMI创建完成"

# 验证AMI状态,成功则返回available
aws --region $REGION --no-cli-pager ec2 describe-images \
    --image-ids $OLD_INSTANCE_AMI_ID \
    --query 'Images[0].State' \
    --output text

第四步:数据备份验证

在进行切换之前,确保所有重要数据都已妥善保存:

  1. 验证所有业务数据都存储在EBS数据卷中
  2. 如有必要,将根卷中的配置文件备份到S3或EFS
  3. 记录所有重要的系统配置

第五步:执行切换(停机窗口开始)

从这一步开始,业务将进入短暂的停机状态。建议在业务低峰期执行。

5.1 停止临时实例和旧实例

# 停止临时实例
echo "停止新实例..."
aws --region $REGION --no-cli-pager ec2 stop-instances --instance-ids $NEW_INSTANCE_ID
aws --region $REGION --no-cli-pager ec2 wait instance-stopped --instance-ids $NEW_INSTANCE_ID
echo "新实例已停止"

# 停止旧实例
echo "停止旧实例..."
aws --region $REGION --no-cli-pager ec2 stop-instances --instance-ids $OLD_INSTANCE_ID
aws --region $REGION --no-cli-pager ec2 wait instance-stopped --instance-ids $OLD_INSTANCE_ID
echo "实例已停止"

5.2 从旧实例分离数据卷

使用第一步中保存的数据卷信息,从旧机器卸载数据卷(如果有多个数据卷,需要每个都执行一次):

echo "从旧实例卸载数据卷..."
aws --region $REGION --no-cli-pager ec2 detach-volume \
    --volume-id $DATA_VOLUME_ID \
    --instance-id $OLD_INSTANCE_ID

# 等待数据卷完全分离
aws --region $REGION --no-cli-pager ec2 wait volume-available --volume-ids $DATA_VOLUME_ID
echo "数据卷 $DATA_VOLUME_ID 卸载完成"

5.3 修改ENI删除行为并终止旧实例

默认情况下,主ENI会在实例终止时被删除。我们需要修改这个行为:

echo "设置主ENI在实例终止时不删除..."
aws --region $REGION --no-cli-pager ec2 modify-network-interface-attribute \
    --network-interface-id $OLD_PRIMARY_ENI_ID \
    --attachment AttachmentId=$OLD_ATTACHMENT_ID,DeleteOnTermination=false
echo "主ENI删除行为已修改"

# 终止旧实例
echo "终止旧实例..."
aws --region $REGION --no-cli-pager ec2 terminate-instances --instance-ids $OLD_INSTANCE_ID
aws --region $REGION --no-cli-pager ec2 wait instance-terminated --instance-ids $OLD_INSTANCE_ID
echo "旧实例已终止,主ENI已保留"

5.4 使用新AMI和旧ENI启动最终实例

这是整个方案的核心步骤:使用新实例的AMI(包含C7i优化的系统配置)和旧实例的ENI(保留网络身份)启动最终实例:

export FINAL_INSTANCE_ID=$(aws --region $REGION --no-cli-pager ec2 run-instances \
    --image-id "$NEW_INSTANCE_AMI_ID" \
    --instance-type "$INSTANCE_TYPE" \
    --key-name "$KEY_PAIR_NAME" \
    --network-interfaces "[
        {
            \"DeviceIndex\": 0,
            \"NetworkInterfaceId\": \"$OLD_PRIMARY_ENI_ID\"
        }
    ]" \
    --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=\"$INSTANCE_NAME\"}]" \
    --query 'Instances[0].InstanceId' \
    --output text)
echo "最终实例ID: $FINAL_INSTANCE_ID"

# 等待实例启动
echo "等待最终实例启动..."
aws --region $REGION --no-cli-pager ec2 wait instance-status-ok --instance-ids $FINAL_INSTANCE_ID
echo "最终实例已启动"

5.5 挂载数据卷

将第一步中获得的数据卷挂载到新机器(如果有多个数据卷,需要执行多次):

echo "将数据卷挂载到最终实例..."
aws --region $REGION --no-cli-pager ec2 attach-volume \
    --volume-id $DATA_VOLUME_ID \
    --instance-id $FINAL_INSTANCE_ID \
    --device $DATA_DEVICE_NAME

# 等待挂载完成
aws --region $REGION --no-cli-pager ec2 wait volume-in-use --volume-ids $DATA_VOLUME_ID
echo "数据卷挂载完成"

# 验证数据卷挂载状态
aws --region $REGION --no-cli-pager ec2 describe-volumes \
    --volume-ids $DATA_VOLUME_ID \
    --query 'Volumes[0].Attachments[0].[State,Device,InstanceId]'

第六步:验证和恢复业务

6.1 验证网络配置

# 获取最终实例的公网IP
export FINAL_PUBLIC_IP=$(aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $FINAL_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].PublicIpAddress' \
    --output text)

echo "最终实例公网IP: $FINAL_PUBLIC_IP"

# 验证IP地址与旧实例一致
aws --region $REGION --no-cli-pager ec2 describe-instances \
    --instance-ids $FINAL_INSTANCE_ID \
    --query 'Reservations[0].Instances[0].[InstanceId,PrivateIpAddress,PublicIpAddress,NetworkInterfaces[0].NetworkInterfaceId]'

6.2 登录实例并挂载文件系统

ssh -i $SSH_KEY_PATH admin@$FINAL_PUBLIC_IP

在实例内部:

# 查看fstab配置
sudo cat /etc/fstab

# 挂载所有文件系统
sudo mount -a

# 验证挂载
df -h

# 启动业务应用
sudo systemctl start your-application

6.3 验证业务功能

进行全面的业务验证:

  • 检查应用日志
  • 测试关键业务功能
  • 验证上下游系统连接
  • 监控性能指标

第七步:清理临时资源

确认业务正常运行后,清理临时资源以节省成本:

# 终止临时实例
echo "终止临时实例..."
aws --region $REGION --no-cli-pager ec2 terminate-instances --instance-ids $NEW_INSTANCE_ID
echo "临时资源已清理"

回滚方案

如果升级后发现问题,可以快速回滚到旧实例:

回滚步骤

1. 停止新实例

echo "停止新实例..."
aws --region $REGION --no-cli-pager ec2 stop-instances --instance-ids $FINAL_INSTANCE_ID
aws --region $REGION --no-cli-pager ec2 wait instance-stopped --instance-ids $FINAL_INSTANCE_ID

2. 使用旧实例AMI恢复

export ROLLBACK_INSTANCE_ID=$(aws --region $REGION --no-cli-pager ec2 run-instances \
    --image-id "$OLD_INSTANCE_AMI_ID" \
    --instance-type "c4.xlarge" \
    --key-name "$KEY_PAIR_NAME" \
    --network-interfaces "[
        {
            \"DeviceIndex\": 0,
            \"NetworkInterfaceId\": \"$OLD_PRIMARY_ENI_ID\"
        }
    ]" \
    --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=\"$INSTANCE_NAME-rollback\"}]" \
    --query 'Instances[0].InstanceId' \
    --output text)

echo "回滚实例ID: $ROLLBACK_INSTANCE_ID"

# 等待实例启动
aws --region $REGION --no-cli-pager ec2 wait instance-status-ok --instance-ids $ROLLBACK_INSTANCE_ID

3. 重新挂载数据卷

aws --region $REGION --no-cli-pager ec2 attach-volume \
    --volume-id $DATA_VOLUME_ID \
    --instance-id $ROLLBACK_INSTANCE_ID \
    --device $DATA_DEVICE_NAME

aws --region $REGION --no-cli-pager ec2 wait volume-in-use --volume-ids $DATA_VOLUME_ID

4. 验证并恢复服务

登录回滚实例,挂载文件系统并启动服务。

最佳实践和注意事项

1. 充分测试

  • 在生产环境执行前,先在测试环境完整演练整个流程
  • 验证应用在C7i实例上的兼容性和性能
  • 测试回滚流程,确保可以快速恢复

2. 选择合适的维护窗口

  • 在业务低峰期执行切换操作
  • 预留足够的时间进行验证
  • 提前通知相关团队和用户

3. 监控和告警

  • 在切换前后密切监控系统指标
  • 设置关键指标的告警阈值
  • 准备好应急响应预案

4. 数据安全

  • 确保所有重要数据都有备份
  • 验证AMI备份的完整性
  • 考虑使用AWS Backup进行自动化备份

5. 网络配置

  • 确认安全组规则正确
  • 验证路由表配置
  • 检查网络ACL设置

6. 成本优化

  • 及时清理不再使用的资源(AMI、快照、EIP等)
  • 考虑使用Savings Plans或Reserved Instances降低C7i成本
  • 监控新实例的资源利用率,必要时调整实例大小

故障排查

问题1:ENI无法附加到新实例

原因:ENI可能仍然附加到旧实例,或者处于”in-use”状态。

解决方案

# 检查ENI状态
aws ec2 describe-network-interfaces --network-interface-ids $OLD_PRIMARY_ENI_ID

# 如果仍然附加到旧实例,强制分离
aws ec2 detach-network-interface --attachment-id <attachment-id> --force

问题2:数据卷挂载失败

原因:设备名称可能不匹配,或者卷仍然附加到其他实例。

解决方案

# 检查卷状态
aws ec2 describe-volumes --volume-ids $DATA_VOLUME_ID

# 确保卷处于available状态
# 在实例内部检查实际设备名称
lsblk

问题3:应用无法启动

原因:可能是Nitro架构的驱动或配置问题。

解决方案

  • 检查系统日志:sudo journalctl -xe
  • 验证ENA驱动已安装:modinfo ena
  • 检查NVMe驱动:lsmod | grep nvme

总结

本文介绍的基于ENI迁移的EC2实例升级方案,为从C4升级到C7i提供了一个安全、高效、可回滚的解决方案。通过充分利用AWS的AMI和ENI特性,我们实现了:

  • 最小化停机时间:实际停机时间可控制在5-10分钟内
  • 零网络配置变更:完整保留IP地址和安全组配置
  • 快速回滚能力:通过AMI备份可在5分钟内回滚
  • 自动化潜力:所有步骤均可脚本化,支持批量升级

随着AWS持续推出性能更优的新一代实例,掌握这种迁移方法将帮助您更好地优化工作负载,降低成本,提升性能。

相关资源

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

李惟忠

益世界游戏平台运维工程师。

罗伟潮

亚马逊云科技解决方案架构师,拥有多年互联网和游戏SRE/基础架构经验,专注于游戏应用现代化和生成式AI解决方案。

黄际东

亚马逊云科技解决方案架构师,有过银行、旅游、直播、教育等行业的十多年项目经验。曾独立主导实现从零到千万用户级别的教育类应用,也参与研发过月活跃用户过千万的全球知名直播平台。在互联网领域拥有多年的研发及运维经验,是一个喜欢编程的解决方案架构师。

AWS 架构师中心: 云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用