ECS(Rails API)のEC2をt3からt4gに変更した記録
自分の環境では terraform でインスタンスを立ち上げているのでその前提で書いていきます。
インスタンスタイプの違い
まず t3 と t4g は何が違うの?という疑問があると思いますので自分が調べた範囲を書き出します。
項目/インスタンスタイプ | t3 | t4g |
---|---|---|
CPU アーキテクチャ | x86(intel) | arm ベース(AWS Graviton2) |
パフォーマンス | 1 | 1.4 |
通常価格(small) | USD 0.0272/h | USD 0.0216/h |
Savings Plans,リザーブド前払価格(small) | USD 140/y | USD 111/y |
作業ログ
作業の概要を書きます。
1.arm を調査
2.Terraformでインスタンス指定を修正
3.デプロイ
4.テスト
1.arm を調査
AMI(Amazon Machine Image)とは OS やミドルウェア、アプリケーション、設定等を含めた仮想マシンイメージのテンプレートのようなものです。
今回は ECS を使用するのでそれ用の型番を検索します。検索は AWS の CLI から実行できます。
$ aws ssm get-parameters --names /aws/service/ecs/optimized-ami/amazon-linux-2023/arm64/recommended --region ap-northeast-1
実行結果の中に以下のように arm が表示されていると思いますのでコピーします。
ami-059fab84a3cc4b17f
他の要件のAMIについては検索方法が公式ドキュメントにありましたのでご参照ください。
2.Terraformでインスタンス指定を修正
自分の環境では以下のように指定していました。今回は Rails API を動かすので small を指定しています。
resource "aws_instance" "example" {
- ami = "ami-0ffb5f4e03c892bc5"
- instance_type = "t3.small"
+ ami = "ami-059fab84a3cc4b17f"
+ instance_type = "t4g.small"
}
3.デプロイ
自分の環境ではAWSとアプリの2段階でデプロイします。
AWS 設定反映|デプロイ
いつものように terraform コマンドを実行していきます。
$ terraform init
$ terraform plan
$ terraform apply
Rails アプリ設定反映|デプロイ
以下続けて私の環境での操作ですが ECS 上で動作させる Rails をデプロイします。
$ docker exec -it railscontainer bash
# bundle update
Bundle updated!
# exit
$ git add Gemfile.lock
$ git commit -m "Update bundle"
$ git push origin feature
あとは git hub 上で main ブランチにマージすると GitHub Actions が走り、ecspresso にて ECS 上に反映される流れになります。
エラー
今回インスタンスのCPUアーキテクチャが変わる変更をしたことでいくつか影響がありエラーが出ました。
エラーその1|CPUアーキテクチャに伴って
GitHub Action 上で動作している ecspresso からエラーが出ました。
run FAILED. failed to run task: failed to run task: ResourceNotReady: failed waiting for successful resource state
Error: Process completed with exit code 1.
また ECS のコンソール → クラスターを選択 → サービスを選択 → ログのタブを確認すると以下のエラーが出ていました。
exec /usr/local/bundle/bin/rails: exec format error
エラーの原因
今回 EC2 のインスタンスタイプを変更したことで CPU のアーキテクチャが変わっています。t3 は x86(intel)、t4g は arm ベース(AWS Graviton2)です。こういった背景からビルドする際に arm でコンテナやイメージ、サービスをビルドする必要があります。以下私の環境での変更を書いていきます。
エラーの対応
原因に対応するため Github actions 上でビルドする Docer イメージを arm64CPU に合わせます。EC2 t4g インスタンスは arm64CPU を使用しています。
Actions の設定ファイルで docker image のビルド部分を変更しました。
変更部分(git diff で出したものなので行頭の+が追加した行、ーが削除した行)
.github/workflows/actions.yml
jobs:
build:
steps:
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
- name: Build image and push ecr
id: build-image
+ uses: docker/build-push-action@v5
- env:
- TEST_ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- TEST_ECR_REPOSITORY: ${{ secrets.TEST_AWS_ECR_REPO_NAME }}
- run: |
- docker buildx build -t $TEST_ECR_REGISTRY/$TEST_ECR_REPOSITORY:$TAG -t $TEST_ECR_REGISTRY/$TEST_ECR_REPOSITORY:latest --build-arg RAILS_MASTER_KEY=${{secrets.TEST_KEY}} --build-arg RAILS_ENV="production" .
- docker push $TEST_ECR_REGISTRY/$TEST_ECR_REPOSITORY:$TAG
- docker push $TEST_ECR_REGISTRY/$TEST_ECR_REPOSITORY:latest
- echo "image=$TEST_ECR_REGISTRY/$TEST_ECR_REPOSITORY:$TAG" >> $GITHUB_OUTPUT
+ with:
+ context: .
+ platforms: linux/arm64
+ push: true
+ tags: |
+ ${{ env.TEST_ECR_REGISTRY }}/${{ env.TEST_ECR_REPOSITORY }}:${{ env.TAG }}
+ ${{ env.TEST_ECR_REGISTRY }}/${{ env.TEST_ECR_REPOSITORY }}:latest
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ - name: Output image info
+ run: |
+ echo "image=${{ steps.login-ecr.outputs.registry }}/${{ secrets.TEST_AWS_ECR_REPO_NAME }}:${{ env.TAG }}" >> $GITHUB_OUTPUT
変更点1
Github actions の step で run 実行していた docker build コマンドを docker/build-push-action@v5 に置き換えました。
変更点2
arm64 のイメージを Github Actions のデフォルトランタイムでシュミレーションしながらビルドしているのでビルド時間が 5 倍くらいになりました。その対策として Github actions のキャッシュ機能を使用しています。
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
またこのキャッシュに関してはGithub actionsのコンソールから確認、削除もできます。
キャッシュしたプロセスに変更を加えた際は忘れないよう注意が必要です。
変更点3
2つのdockerに関するアクションを追加しています。
docker/setup-qemu-action@v3
docker/setup-buildx-action@v3
docker/setup-qemu-action@v3
これはGithub Actionsの標準ランタイム(x86)環境上でarm64のイメージをビルドするために必要になります。QEMUというのはCPUやハードウェアをエミュレートするオープンソースの仮想マシンソフトウェアになります。
docker/setup-buildx-action@v3
buildxはGithub Actionsのキャッシュを実現するために必要になります。cache-from、cache-toの部分を実行するために追加しています。buildxはdocker buildで実行できない機能を拡張するプラグインです。
ECSのタスク定義ファイルを編集(ecspressoから読み込み使用)
ecspresso で読み込んでいるecsタスク定義ファイルを (task-def.json)を変更しました。
+ "family": "ecs-test-service",
+ "runtimePlatform": {
+ "cpuArchitecture": "ARM64",
+ "operatingSystemFamily": "LINUX"
+ },
"containerDefinitions": [
{
"cpu": 333,
"memoryReservation": 600,
}
],
ここではルート階層に
"runtimePlatform":として CPU アーキテクチャを定義しています。
memoryReservation に関してはコンテナごとのメモリです。この辺りはアプリケーションによって必要な値が変わることもあると思うので適宜調整します。
副次的トラブル|インスタンスコネクトが使えない
今回AMIを変更したことでインスタンスコネクトが使えないということも起こりました。その対策もまとめましたのでご参照ください。
エラーその2|ECSとEC2の関連が外れた
以下のエラーが出た状況(GitHub Action上のecspresso実行時)として次のような操作を行った後でした。
直前にコンソールから手動でクラスタを削除し terraform から「ECS クラスタのみ生成」したところ、EC2 との関連が外れている状態でした。(クラスタコンソール画面の「登録済みコンテナインスタンス」が1ではなく ー になっていた)
run FAILED. failed to run task: InvalidParameterException: No Container Instances were found in your cluster.
Error: Process completed with exit code 1.
原因
EC2 と ECS クラスタの関連付けが外れてしまっていたのが原因です。
対策
自分の場合は
terraform destroy
↓
terraform plan
↓
terraform apply
rails アプリのデプロイ
↓
Github actions イメージ再作成
↓ ↓
ECR と ECS にデプロイ
で修正できました。
コンソールからも関連付けを修正すれば復活するかもしれません。
その他エラーが起こったら
テスト
コンソールでタスクの起動、ログを確認し、アプリから CRUD を動作させてみます。作成、表示、編集、削除ができれば完了です。
GithubActions のランタイム
Actions のランタイムをMac(arm)にすることもできました。こちらは少し料金が高いので注意が必要です。私の環境ではecspressoがx86で動く状況だったので今回のような形に落ち着きました。
所感
今回の変更で
・年間29ドルのコスト削減
・40%のCPUパフォーマンスアップ
が実現できました。
とはいえCPUアーキテクチャが影響する範囲はまだまだあるな〜というのも実感できました。今回そこまで深く調べていないので触れませんでしたがRailsのGem関連も結構影響を受けていました。参考になりましたら幸いです。
参考
Discussion