ENECHANGE Developer Blog

ENECHANGE開発者ブログ

Rails 6アプリのFargateをGraviton2に替えるために対応したこと

仕事ではコストを、プライベートでは悪玉コレステロール値を減らしたい、CTO室の岩本 (iwamot) です。

コスト削減を目的に、先日、あるRails 6アプリの基盤をIntel x86ベースのFargateからGraviton2ベースのFargateに替えました。

対応したことを簡単にまとめると、下記のようになります。

  • CodeBuildでARM用Dockerイメージをビルドできるようにした
    • CodeBuildビルド環境をLINUX_CONTAINERからARM_CONTAINERに替えた
    • ExecJSのJavaScriptランタイムをmini_racerからNode.jsに替えた
    • NokogiriをネイティブビルドするようBundlerを設定した
    • CodeBuildビルド環境のAWS CLIを更新した
  • FargateをGraviton2ベースに替えた
    • ECSタスク定義を更新した

以下、もう少し詳しく書き残しておきます。

CodeBuildでARM用Dockerイメージをビルドできるようにした

CodeBuildビルド環境をLINUX_CONTAINERからARM_CONTAINERに替えた

DockerイメージのビルドにはCodeBuildを使っているので、そのビルド環境をLINUX_CONTAINERからARM_CONTAINERに替えました。

Terraformテンプレートの差分で示すと、下記のようになります。

resource "aws_codebuild_project" "this" {
   environment {
-    image           = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
-    type            = "LINUX_CONTAINER"
+    image           = "aws/codebuild/amazonlinux2-aarch64-standard:2.0"
+    type            = "ARM_CONTAINER"

ExecJSのJavaScriptランタイムをmini_racerからNode.jsに替えた

対応前は、ExecJSのJavaScriptランタイムとして、mini_racerが使われていました。

ただ、mini_racerの依存しているlibv8は、現状ではARMをサポートしていません

そこで、Dockerイメージからmini_racer gemを削除し、代わりにNode.jsを追加しました。ExecJSがサポートしているJavaScriptランタイムのうち、慣れているものを選んだ形です。

NokogiriをネイティブビルドするようBundlerを設定した

プリコンパイル済みのNokogiriをaarch64-linuxで使うには glibc >= 2.29 が要件となります

ただし今回は、プラットフォーム側のglibcを更新するのは難しいと考え、Nokogiriをネイティブビルドすることにしました。

具体的には、Dockerfileに下記のような分岐を加えています。

RUN if [[ $(arch) == "aarch64" ]]; then \
      bundle config set force_ruby_platform true; \
    fi

CodeBuildビルド環境のAWS CLIを更新した

ARM_CONTAINER (aws/codebuild/amazonlinux2-aarch64-standard:2.0) には、バージョン1.20.58と古いAWS CLIがインストールされています。

そのため、ARM_CONTAINERへの移行後に、ビルド処理の一部が失敗するようになりました。

この点については、buildspec.ymlを下記のように変更し、AWS CLIを更新することで解決しています。

   pre_build:
     on-failure: ABORT
     commands:
+      - pip3 install awscli --upgrade --user

FargateをGraviton2ベースに替えた

ECSタスク定義を更新した

ARM用Dockerイメージさえビルドできれば、あとはFargateをGraviton2ベースに替えるだけです。

Terraformテンプレートの差分で示すと、下記のように1行加えるだけでした。

resource "aws_ecs_task_definition" "this" {
   runtime_platform {
     operating_system_family = "LINUX"
+    cpu_architecture        = "ARM64"
   }

おわりに

試行錯誤はしましたが、結果的にはそれぞれ数行の変更で済みました。今後はスムーズに対応できそうです。

目的のコスト削減効果は20%でした。これは単純に、Intel x86ベースのFargateより料金が20%安いためです。また、Graviton2はパフォーマンスも優れているため、アプリによっては、割り当てるvCPUやメモリを小さくすることで、さらにコストが抑えられます。

弊社には、Intel x86ベースのFargateで動いているアプリが他にもいくつかあるので、それらもGraviton2に替えていきたいと考えています。