🐳

AWS Copilot CLI から CodePipeline 経由で ARM な ECS デプロイする話

2021/12/23に公開

Leaner 開発チームの黒曜(@kokuyouwind)です。

この記事は AWS Containers Advent Calendar 2021 の 23 日目です。

AWS Copilot CLI が ARM ベースの Fargate に対応した話

先月下旬、Fargate で Graviton2 CPU が利用できるアップデートがリリースされました!

x86 CPU を利用するコンテナと比べて、料金は安く、そして性能は向上するという最高すぎるアップデートでしたね。

このあたりの話は AWS の中の人である @torics さんが詳しくツイートしてくださっています。

https://twitter.com/toricls/status/1463285380543774722

そして、同日に AWS Copilot CLI からも ARM CPU を利用できるようアップデートが入りました。

こちらも @torics さんがツイートされている他、毎度おなじみ Developers IOさんの記事でも手順付きでわかりやすくまとめられています。

https://twitter.com/toricls/status/1463293772100354048

https://dev.classmethod.jp/articles/aws-graviton2-fargate-with-aws-copilot-cli/

この手順通りに作業すれば ARM CPU にマイグレーションできますね。めでたし、めでたし。

…となればよかったんですが、既存サービスをマイグレーションしようとしたらもう少し作業が必要だったので、その話をします。

ARM CPU への切り替えで起こった問題

Copilot CLI が起動するサービスを ARM CPU ベースに切り替えるため、まずはマニフェストファイルに platform: linux/arm64 を追加しました。

これで良いかなとデプロイしてみると、なぜかタスクが立ち上がる前に以下のエラーで死んでしまいます。

standard_init_linux.go:228: exec user process caused: exec format error

なんでや!

原因1: デプロイ構成の違い

改めてビデオクリップや記事を見直してみると、デプロイは copilot svc deploy を利用して作業者の環境から直接行っています。

一方で、今回マイグレーションしようとしたサービスは copilot pipeline で作った CodePipeline からデプロイを行っていました。

「CodePipeline 内から copilot svc deploy しているなら何も変わらないのでは?」と思ってしまいそうですが、実際には処理の流れは大きく異なっています。

CodePipeline からのデプロイは、まず CodeBuild でイメージをビルドして ECR に push し、その後 ECS から新しいイメージを見るよう CloudFormation スタックが更新されて ECS タスクが置き換えられる、という流れになっています。

このうち CodeBuild でのビルド処理で platform の指定が効かず、 x86 向けのイメージが作られていることがひとつの原因になっていました。

対応1: CodeBuild の環境イメージを ARM ベースに変更する

要は CodeBuild の動作環境を x86 にしているのが問題なので、 ARM ベースのイメージに変更することでこの問題は解決できます。

特に設定せず作られた CodeBuild では下記のように aws/codebuild/amazonlinux2-x86_64standard:3.0 などの x86_64 イメージが利用されています。

ここを aws/codebuild/amazonlinux2/aarch64-standard:2.0 などの ARM イメージに差し替えてやることで、ビルドされるイメージも ARM ベースになります。

CodeBuild で利用するイメージは buildspec.yml 内で以下のように指定できるため、これを設定したうえで copilot pipeline update を実行します。

build:
  image: aws/codebuild/amazonlinux2-x86_64-standard:3.0

なお Buildx を利用すれば --platform フラグを利用してクロスプラットフォームのイメージビルドを行うことができますが、残念ながら CodeBuild 標準のイメージでは Buildx が有効化されておらず、都度インストールすると時間がかなり掛かるためベースイメージを変更するほうが効率的です。

もし Buildx を利用したい場合は、以下の記事を参考に buildspec.yml を書き換えればよいはずです。[1]

https://zenn.dev/xeres/articles/2021-12-02-cross-platform-build-with-codebuild

原因2: FireLens イメージのビルド

これでメインサービスのイメージはバッチリなのですが、弊社サービスではログルーティングのためにカスタムイメージの FireLens を利用していました。

こちらは GitHub Actions でビルドしており、特に指定していなかったためやはり x86 イメージになってしまっていました。

対応2: FireLens イメージをクロスプラットフォーム対応する

GitHub Actions では稼働環境のアーキテクチャ差し替えが難しそうだったため、 Buildx を利用したクロスプラットフォームビルドを行う方針で対応しました。

幸い、 Buildx のセットアップやクロスプラットフォームのイメージビルドを行うカスタムアクションが揃っていたため、こちらはかなり簡単に対応できました。

最終的に、イメージビルドに関するアクション定義は以下のようになりました。

- name: Set up QEMU
  uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v1
- name: Build and push
  uses: docker/build-push-action@v2
  with:
    platforms: linux/arm64,linux/amd64
    push: true
    tags: [account_id].dkr.ecr.ap-northeast-1.amazonaws.com/[myservice]/fluent-bit:${{ matrix.env }}-${{ github.ref_name }}
    file: docker/Dockerfile.${{ matrix.env }}
    context: .

まとめ

これらの対応によって、無事 ARM イメージを利用できるようになり、 ECS タスクが起動しました!

CPU バウンダリーの処理はほとんどないので性能上の恩恵は正直ほとんどありませんが、料金が安くなるというだけでもありがたいですね。

というわけで AWS Containers Advent Calendar 2021 の 23 日目記事をお届けしました。

明日は @track3jyo さんの「ACK で遊ぶ話」の予定になっています。お楽しみに!

宣伝

Leaner Technologies では AWS コンテナに興味のあるエンジニアを募集しています!

https://careers.leaner.co.jp/

脚注
  1. Copilot CLI を利用する場合、 buildspec.yml は自動生成されたものを使っているため、書き換えてしまうとバージョン更新時に整合性を取る作業が必要になります。追記箇所をわかるようにしておくのがオススメです。 ↩︎

リーナーテックブログ

Discussion