Graviton 迁移实操:从依赖扫描到灰度切流

ARM 架构能省 20% 左右,但迁移不是改个实例类型就完事。这篇写清楚每一步该做什么、容易踩什么坑。

先说结论

Java、Go、Python、Node.js 这几类服务迁 Graviton,大部分情况下改造成本很低,收益是同性能下单价降 15%-25%。值得做,但要按流程做,不要直接改实例类型上生产。

真正会卡住的场景是:依赖了 x86 原生扩展、商业闭源组件、或者用了特定 CPU 指令集加速的库。这些必须提前扫出来。

五个步骤

  1. 01

    依赖扫描

    列出所有运行时依赖,逐个确认有没有 ARM64 版本。Python 看 wheel、Java 看是否有 JNI、Node 看 native module。

    交付物:依赖清单、不兼容项列表

    1-3 天

  2. 02

    多架构镜像构建

    docker buildx 构建 amd64 + arm64 双架构镜像,推到 ECR 生成 manifest list。同一个 tag 在不同架构节点上自动拉对应镜像。

    交付物:双架构镜像、CI 流水线改造

    2-4 天

  3. 03

    功能验证

    在 ARM 节点上跑完整的集成测试。重点看时间处理、字符编码、浮点计算这些容易出现细微差异的地方。

    交付物:测试报告、差异清单

    3-5 天

  4. 04

    性能回归

    同并发下对比 p99 延迟与吞吐,不要只看均值。Graviton 的单核性能与 x86 有差异,有些负载会变快,有些会变慢。

    交付物:性能对比数据、规格调整建议

    3-5 天

  5. 05

    灰度切流

    ALB 权重从 5% 开始,观察一天,再到 25%、50%、100%。保留 x86 节点组作为快速回滚路径。

    交付物:切流记录、各阶段指标

    1-2 周

依赖扫描脚本

bash check-arm-compat.sh
#!/usr/bin/env bash
# 检查项目依赖的 ARM64 兼容性
set -euo pipefail

echo "=== 检查容器镜像的架构支持 ==="
# 列出 compose 或 k8s manifest 里引用的所有镜像
IMAGES=$(grep -rhoP '(?<=image:\s)[^\s"]+' . --include='*.yaml' --include='*.yml' | sort -u)

for img in $IMAGES; do
  archs=$(docker manifest inspect "$img" 2>/dev/null \
    | jq -r '.manifests[]?.platform | "\(.os)/\(.architecture)"' \
    | sort -u | tr '\n' ' ')
  if [[ "$archs" == *"linux/arm64"* ]]; then
    printf '  [OK]   %-52s %s\n' "$img" "$archs"
  else
    printf '  [WARN] %-52s %s\n' "$img" "${archs:-单架构或无法查询}"
  fi
done

echo
echo "=== 检查 Python 依赖的 ARM wheel ==="
if [[ -f requirements.txt ]]; then
  while read -r line; do
    [[ -z "$line" || "$line" == \#* ]] && continue
    pkg="${line%%[<>=!~]*}"
    pkg="$(echo "$pkg" | xargs)"
    [[ -z "$pkg" ]] && continue
    files=$(curl -s "https://pypi.org/pypi/${pkg}/json" \
      | jq -r '.urls[]?.filename' 2>/dev/null || true)
    if echo "$files" | grep -qE 'aarch64|arm64|none-any'; then
      printf '  [OK]   %s\n' "$pkg"
    else
      printf '  [CHECK] %s  需人工确认是否有源码包可编译\n' "$pkg"
    fi
  done < requirements.txt
fi

echo
echo "=== 检查二进制文件架构 ==="
find . -type f \( -name '*.so' -o -name '*.dylib' -o -name '*.a' \) 2>/dev/null | while read -r f; do
  arch=$(file -b "$f" | grep -oE 'x86-64|aarch64|ARM' || echo 'unknown')
  printf '  %-14s %s\n' "$arch" "$f"
done

echo
echo "扫描完成。标记为 CHECK 或 WARN 的项需要人工确认。"

多架构镜像构建

yaml .github/workflows/build-multiarch.yml
name: Build Multi-Arch Image

on:
  push:
    branches: [main]
    tags: ['v*']

permissions:
  contents: read
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-qemu-action@v3      # 交叉编译支持
      - uses: docker/setup-buildx-action@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.ECR_PUSH_ROLE }}
          aws-region: ap-northeast-1

      - uses: aws-actions/amazon-ecr-login@v2
        id: ecr

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64    # 关键:双架构
          push: true
          tags: |
            ${{ steps.ecr.outputs.registry }}/order-service:${{ github.sha }}
            ${{ steps.ecr.outputs.registry }}/order-service:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
          provenance: false                     # ECR 对 provenance 支持有限

      - name: Verify manifest
        run: |
          docker manifest inspect \
            ${{ steps.ecr.outputs.registry }}/order-service:${{ github.sha }} \
            | jq '.manifests[].platform'

QEMU 交叉编译比原生构建慢不少。如果构建时间成为瓶颈,改用 ARM 原生 runner 分别构建再合并 manifest。

我们实测过的迁移结果

服务类型语言/运行时改造工作量性能变化成本变化
API 网关服务Go 1.22仅改构建配置p99 −6%(更快)−22%
订单服务Java 17 / Spring Boot改基础镜像p99 +3%(略慢)−19%
数据处理Python 3.12两个包换版本持平−21%
Node BFFNode 20重装 native modulep99 −4%−23%
图像处理Python + OpenCV需重编译,耗时较多p99 +11%(变慢)−20%,但性能损失需评估
商业中间件闭源 x86 二进制无法迁移--

图像处理这类依赖 SIMD 指令优化的场景要谨慎,可能出现降价但性能损失更多的情况,综合算下来不划算。

混合架构集群的坑

K8s 集群里同时有 x86 和 ARM 节点时,务必给只支持单架构的 Pod 加 nodeSelector 或亲和性规则,否则调度到错误架构的节点上会启动失败。我们的习惯是给节点打 kubernetes.io/arch 标签并在 Deployment 里显式声明。

checklist

  • 依赖清单全量扫过,不兼容项有明确处理方案
  • CI 已能产出双架构镜像并验证 manifest
  • 集成测试在 ARM 节点上全部通过
  • 性能对比数据齐全,包含 p50/p95/p99
  • 回滚路径清晰(保留 x86 节点组或旧启动模板)
  • 监控能按架构维度区分指标,便于对比
  • 灰度计划写明各阶段的观察指标与回滚触发条件

下一步

把云的复杂度交给我们,你只管做业务

留下需求,沐杉云的解决方案架构师会在一个工作日内联系你,提供免费的现状评估、迁移方案草案与 TCO 测算表。