コネヒト開発者ブログ

コネヒト開発者ブログ

x86_64からARM64(Graviton)へ──コスト約20%削減を目指したECS移行の記録

こんにちは、コネヒト株式会社で働く北島と申します。社内ではしょうぽんと呼ばれています。

最近弊社ではwebエンジニアもインフラ周りのことを吸収していこう、という機運が高まっています。

そこで今回はECSのコスト削減を実施した事例を紹介します。

x86_64(amd64)から ARM64(Graviton)へ

0. TL;DR

  • ママリのメインAPIのECSワークロード(Fargate + EC2)をx86_64 からARM64(Graviton)へ移行
  • 狙いはコスト削減。AWS公称でGraviton版Fargateはx86比約20%安(price-performanceは最大40%改善)
  • 併せてCIのイメージビルドをARMネイティブランナーに変え、マルチアーキビルドによるCI時間増加を防止
  • dev → 本番カナリア → 本番全体の段階移行で、無停止・即ロールバック可能な形で切り替え

1. 背景・動機

ARM64(Graviton)化をした理由は、AWSの公式アナウンスにある通り、x86_64アーキテクチャに比べて20%程度のコスト削減、最大40%のprice-performance向上が見込まれるからです。

今回移行対象としたサービスはママリの裏側で動いているメインのAPIです。

移行対象には、最も大きくコストを削減できるサービスを選びました。

月間で最低でも数万円以上の削減が見込めると予測しました。これならやる価値があると判断し、すぐに検証を始めました。

ちょうどメインAPIにカナリアリリースの仕組みを導入したこともあり、カナリアがしっかり動いているかの試運転としてもタイミングが良かったです。

2. 移行対象の棚卸し(ここが肝)

移行対象について詳しく説明します。今回移行したメインAPIは、便宜上APIと呼んでいますが、その実いくつかの役割を担っています。

  • ECSと一口に言っても役割ごとにローンチタイプが混在
    • webapp … Fargate
    • batch-fargate … Fargate
    • batch-ec2 … EC2 launch type(→ EC2 キャパシティ=ASG/Launch Template も Graviton 化が必要)

簡単に説明するとこのような構成であり、特に問題となるのはbatch-ec2です。

移行前後の構成を整理すると次のとおりです。

メインAPI(ECS)
├─ webapp        : Fargate           … x86_64       → ARM64
├─ batch-fargate : Fargate           … x86_64       → ARM64
└─ batch-ec2     : EC2 launch type   … x86_64(t3系)  → ARM64(t4g系)
                    └ ASG / Launch Template / AMI も Graviton 化が必要

CI(イメージビルド): マルチアーキ(amd64 + arm64) → ARM ネイティブランナーで arm-only 化

Fargate(webapp / batch-fargate)の移行はそう難しくありません。一方でbatch-ec2は、そもそもなぜEC2で動いているのかという歴史的経緯の把握や、アーキテクチャ移行による機能障害が出ないかの判断が難しい問題でした。

アーキテクチャ移行で起きやすい問題は、ネイティブ拡張・ライブラリに関連したものです。そこでimagickライブラリを使用していないかなどコードを探索し、今回は「ビルドさえできれば問題なさそう」という結論に至りました。

もっと大規模なサービスを一気に移行しようとすると、ネイティブ拡張のランタイム対応など、調べることが非常に多くなりそうです。

3. やったこと

3-1. task definition の ARM64 化

  • runtimePlatform: { cpuArchitecture: ARM64, operatingSystemFamily: LINUX } を付与
    • Fargate はこれだけでarm64タスクが起動するようになります

runtimePlatformを指定すると、ECSはそのタスクをarm64としてスケジュールします。ここで大前提となるのが「タスク定義のアーキテクチャ」と「コンテナイメージのアーキテクチャ」を揃えることです。タスク定義をarm64にしても、起動するイメージがx86_64のままだと、コンテナ起動時にexec format errorで落ちます。次の3-3でイメージ側をarm対応させるのはこのためです。

3-2. EC2 バッチのキャパシティを Graviton 化

  • ASG(Auto Scaling Group)/Launch Templateのインスタンスタイプをt3系からt4g系へ変更
  • AMI(Amazon Machine Image)をECS-optimizedのarm64へ変更
  • IaC管理(ASG/Launch Templateは専用リポジトリで管理)

Fargateと違い、EC2 launch typeではコンテナを載せるEC2インスタンス自体をarm化する必要があります。t3系(Intel)からGraviton系のt4g系へインスタンスファミリーを変え、AMIもarm64版のECS-optimized AMIに差し替えます。Launch Templateを更新してインスタンスを入れ替え、arm64のコンテナインスタンスがクラスタに登録されて初めて、arm64タスクを配置できるようになります。

3-3. コンテナイメージのマルチアーキ対応 → ARM ネイティブビルド

  • 最初はQEMU + buildxでマルチアーキ(amd64, arm64)ビルド(安全だが非常に遅かった)
  • ビルドの低速化を受け、イメージを利用する全ワークロードがarm64化できた段階でarmネイティブランナー(ubuntu-24.04-arm)+arm-onlyビルドへ。QEMU 全廃
  • QEMUマルチアーキ時ビルド時間約17分 → 約3分

ここが一番工夫したところです。移行の過渡期はx86とarmのワークロードが混在するため、どちらでも動くようにマルチアーキ(amd64 + arm64)でイメージをビルドしておく必要がありました。しかしQEMUは別アーキのCPUをソフトウェアで模倣(エミュレート)してビルドするため非常に低速で、ビルド時間が約17分まで膨らんでしまいました。

そこで、すべてのワークロードがarm64へ移りきった段階でx86向けビルドを廃止し、armネイティブランナー上でのarm-onlyビルドへ切り替えました。エミュレーションが不要になったことで、ビルド時間は約3分まで短縮できました。「過渡期はマルチアーキで安全に、移行完了後はネイティブで高速に」という順序がポイントです。

3-4. 不具合検知の強化

  • カナリア専用のCloudWatchアラームをいくつか構築しました。5xx系を検知するアラートとレスポンスタイムの遅れを検知するアラートを重点的に設定しています。
  • カナリア専用ロググループの作成(ログを監視して何かあればすぐ切り戻しできるように)

アラートとロググループをカナリア専用に分けたのは、通常のベースラインと切り分けて「arm化したカナリアだけで起きている異常」をすぐに拾えるようにするためです。アーキテクチャ起因の不具合はレスポンスタイムの悪化やエラー率の上昇として表れやすいため、この2つを重点的に監視しました。

4. 段階移行の進め方

まずはdevで先行検証しました。画面が開けること、ある程度メインどころのエンドポイント・バッチが動くことを確認しました。

しばらくモンキーテスト的なことをdevで行ってからカナリアリリースに踏み切りました。カナリアのアラート・ログ関連をかなり強化していたことや、切り戻しの方法を事前にまとめておいたこともあって、比較的落ち着いてリリースできました。

カナリアで一日程度稼働させ、レスポンスタイムや5xx系のメトリクスに異常がないことを確認してから、本番リリースに臨みました。

結果的には今のところ問題なく動いており、当初の目的であったコスト削減に向けた移行を無事に完了できました(実測値の検証は次節のとおりこれから行います)。

5. ハマったところ / 注意点

これから同じ移行を試す方向けに、実際にハマったところと回避策をまとめます。

  • ARM Spotキャパシティの枯渇:Spotはアーキテクチャ・インスタンスタイプごとに別のキャパシティプールを持ちます。armのプールはx86に比べてまだ小さく、devでFARGATE_SPOTのみを使っていたところ、arm64ではSpotが枯渇してCD(デプロイ)が失敗しました。対応として、ECSのキャパシティプロバイダー戦略にFARGATE(on-demand)を加え、baseで最低タスク数を確保する構成に変更しています。
  • サイドカー/エージェントのarm対応確認:Datadog Agentなどのサードパーティイメージが、利用したいタグでarm64を提供しているかを事前に確認します。固定タグがマルチアーキのマニフェストになっていないと、armでpullできずにタスクが起動しないことがあります。
  • アプリ依存のネイティブ拡張:拡張モジュールやpeclビルド等がarmでビルド・動作するかを確認します。今回のイメージもAlpine(musl)ベースで、pecl経由のimagick等をビルドしています。glibcベースに比べてmusl環境はビルドで詰まりやすいため、armランナー(やApple Siliconのローカル環境)で実際にイメージをビルドし、起動・疎通まで通して確認するのが確実です。
  • 移行には順序依存がある:batch-ec2は、EC2インスタンスがarm化されるまでarm-onlyイメージにできません。「①イメージのarm対応 → ②キャパシティ(EC2/Fargate)のarm化 → ③arm-only化」の順を守らないと、起動できないタスクが生まれてしまいます。この順序が今回の移行設計の肝でした。

6. 効果測定(これから)

  • 移行前後の比較方法:CloudWatch(ALB TargetResponseTime / ECS CPU・Mem)、同曜日・同トラフィック帯で RequestCount 正規化
  • ベースライン比較:x86_64 ベースライン(移行直前7日間のレスポンスタイム/CPU/Mem)を記録 → ARM64 後と比較
  • コスト:単価ベースの試算では、同一スペック構成のままなので約20%の削減が見込める。実際の削減幅は実請求の前後比較とSpot/on-demand比率で確認する

7. まとめ

今回は、ママリのメインAPIのECSワークロードをx86_64からARM64(Graviton)へ移行し、コスト削減に取り組みました。Fargateだけでなく EC2 launch type も移行対象になること、サイドカーやネイティブ拡張のarm対応を事前に確認しておくことが、実務上つまずきやすいポイントでした。段階移行と即ロールバックの仕組みを用意しておいたおかげで、大きなトラブルなく切り替えられています。

コスト削減のような施策は緊急度が高いわけではなく、つい後回しにしてしまいがちです。それでも腰を据えて進めてみると、削減できた分がそのままチームや会社への貢献につながり、地味ながら確かな手応えと喜びがありました。

事業戦略の観点でも、AIの活用によって「攻め」と「守り」を同時に進められるようになったのは大きな変化です。先日リリースしたレシートエールのAmazonギフト券交換機能のような新機能開発(攻め)と、今回のようなコスト削減(守り)は、これまでであればリソースの取り合いになりがちでした。AIの後押しによって開発のスループットが上がったことで、限られた人数でも事業の成長と足腰の強化を両立しやすくなったと感じています。

冒頭でも触れたとおり、弊社ではwebエンジニアもインフラ周りに積極的に挑戦していこうという機運が高まっています。普段はアプリケーションを書いているエンジニアでも、コスト削減やカナリアリリースの仕組みづくりといったインフラ領域の改善に手を伸ばせる環境だと感じています。今後は、今回得た知見を活かして他サービスへも横展開していく予定です。

参考リンク