こんにちは!ウォンテッドリーでインフラエンジニアをしている加藤 健です。
先日、EKS 上で動いているサービスの一部を、x86_64 (Intel) のインスタンスから Arm64 (AWS Graviton) のインスタンスに移行しました。
この記事では、Arm に移行した理由、Arm64 と x86_64 の違い、実際の移行手順と進め方についてまとめています。これから Arm への移行を検討している方の参考になれば幸いです。
目次
なぜ Arm に移行するのか
一番の理由はコスト最適化です。東京リージョンのオンデマンド料金で比べると、同じ 4xlarge でも Arm の m8g は Intel の m8i より 1 ヶ月あたり約 15% 安くなっています。
本番環境ではおよそ 40 台のノードが稼働しているので、オンデマンド換算だと月 $4,800 (約 75 万円) ほどの節約になります。
また、今回はタイミングも重要でした。ウォンテッドリーでは Savings Plans (SPs) を年間で購入していて、Arm と Intel にそれぞれいくら使うかを購入時に決める必要があります。購入前に Arm で動く Pod を増やしておけば、その分を割引率の高い Arm インスタンスを利用することができます。
Arm64 と x86_64 で何が違うのか
一言でいうと、CPU が理解できる機械語が違います。たとえば、1+1 を返すだけの C のコードでも、Arm 向けと x86_64 向けにコンパイルすると、まったく別の命令列になります。
x86_64 の機械語を Arm の CPU に渡しても、残念ながら理解できません。
では、Ruby や Python のようなスクリプト言語はどうでしょうか。Ruby のソースコード自体は CPU に依存しませんが、それを実行する ruby コマンドは C 言語で実装されています。そのため、Arm のマシンで動かすには Arm 向けにコンパイルされた ruby コマンドが必要になります。
どうやって Arm に移行するのか
移行は大きく 3 つのステップで進めました。
1. 利用ライブラリが Arm に対応しているか確認する
Ruby や Python だけで書かれているライブラリは、特に対応は必要ありません。注意が必要なのは、一部が C/C++ で書かれているライブラリです。C/C++ のライブラリは機械語に変換された状態で読み込まれるため、CPU ごとにライブラリが必要になります。
そのため、Arm 版がリリースされているかを確認し、ない場合は自分でビルドするか、他のライブラリへの置き換えを検討します。
2. Arm 環境でビルドし、Arm 版のコンテナイメージを生成する
GitHub Actions の Arm ランナーを使ってコンテナイメージをビルドするようにしました。Arm 環境でビルドすると、Arm に対応したベースイメージが自動的に選択されます。そのため、ruby コマンドなどの実行ファイルや、ビルド時にインストールされるライブラリも Arm 向けのものになります。
Arm 版と x86_64 版のイメージは 1 つのタグにまとめているので、ノード側は自分の CPU に合ったイメージを自動で pull してくれます。このビルド処理は共通ワークフローとして用意しているので、各リポジトリからはそれを呼び出すだけで済むようにしています。
3. Arm インスタンスへ配置する
最後に、Kubernetes の Node Affinity という機能を使って、kubernetes.io/arch=arm64 というラベルがついたノードに Pod を配置するように設定します。
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: [arm64]
何か問題があった場合も、この設定を戻すだけで x86_64 のノードに切り戻すことができます。切り戻しが簡単なのは、運用する側としてもかなり安心感がありました。
移行の進め方
移行対象は、「コスト効率」と「移行のしやすさ」の 2 つの観点で優先度をつけて進めました。
- コスト効率: CPU やメモリを多く使っているサービスほど、Arm に移行したときの削減額が大きくなります。AWS のコストデータとクラスタのリソース使用量から、サービスごとの寄与を見積もりました。
- 移行のしやすさ: 使っている言語や、C/C++ で書かれたライブラリへの依存、Arm 版の提供状況を確認しました。
優先度の高いものから移行し、効果を見ながら、次の優先度のものを追加で移行していきました。移行しにくいものは、無理に対応せず後回しにしています。
リリースの前には、各プロダクトのチームにスケジュールと切り戻し条件を共有しました。本番環境に適用する前には、開発環境とテスト環境に先にデプロイし、E2E テストなどで動作確認を行っています。また、影響の小さいサービスから本番環境に適用していくことで、後続のリリースのリスクを下げるようにしました。
移行した結果
ウォンテッドリーでは 2024 年から少しずつ Arm への移行を進めてきました。今回の取り組みを始めた 7 月時点では、本番環境の一般的なワークロード用ノード 36 台のうち、Arm で動いているのは 25 台 (約 7 割) でした。
そこから残りの x86_64 ノードで動いていたサービスを優先度順に移行し、現在は 36 台中 32 台 (約 9 割) が Arm で動いています。残りは、Arm 版が提供されていないミドルウェアに依存しているなど、今回は後回しにしたサービスです。
まとめ
今回は、コスト最適化のために Kubernetes 上のサービスを Arm に移行した取り組みを紹介しました。
- Arm インスタンスは同じサイズの Intel インスタンスより約 15% 安い
- Arm64 と x86_64 では理解できる機械語が違うため、Arm 向けにビルドされたライブラリやコマンドを使う必要がある
- ライブラリの確認 → Arm でビルド → Arm ノードへ配置、の 3 ステップで移行でき、本番ノードの約 9 割が Arm で動くようになった
Arm 移行というと大変そうなイメージがありましたが、実際にやってみると、コードの修正はほとんどなく進めることができました。今後も、後回しにしたサービスを含めて Arm で動かせるサービスを増やしていきたいと思います。
最後まで読んでいただき、ありがとうございました!