AWS ECS on Fargate構築の課題と解決策、採用してよかったこと
はじめに
先日、AWSで ECS on Fargate環境を構築する機会がありました。
ECSは、数年前に勉強がてら、簡単なサンプルアプリケーションを起動する程度のことをやってみただけで、お客様の本番環境として構築するのは初めてでした。
稼働するサービスは、医療・介護・福祉系SNSで、Microsoft Azure(Virtual Machines+SQL Database)からの移行となります。
大まかなAWS構成はこんな感じです。
(サブネットは省略)
コンテナイメージはアプリケーション開発担当の方が作成するということで、こちらでは、コンテナリポジトリとコンテナ実行環境を構築するのみとなり、スムーズに完了すると思っていました。
ですが、機能名にとまどったり、構築作業中にいくつか課題やリクエストが出てきたりで、なかなか大変でした。
このブログ記事では、2023年3月から5月にかけてのECS on Fargateの構築で出てきた課題と解決策、そして、アプリケーション開発担当の方にうかがった ECS on Fargateを採用してよかった点についてまとめます。
ECS on Fargateや、FireLens、ECS Exec等の細かい設定手順については、ネット上にわかりやすい記事がたくさんありますので、そちらを参照してください。
ECS on Fargate構築における課題と解決策
1. ECSの構成要素を理解する
2. 環境変数の渡し方
3. タスクロールとタスク実行ロール
4. 稼働中のコンテナでデバッグしたい
5. FireLensを使用したログ出力
6. 外部アクセス時のアクセス元IPアドレスを固定したい
1. ECSの構成要素を理解する
いざ、AWSマネジメントコンソールで構築を始めようとしたとき、クラスター、サービス、タスク、タスク定義、コンテナといった構成要素の意味が分からず、とまどいました。
クラスター、サービス、タスクといったあたりは、他の文脈や機能でも使われる汎用的な用語ですし。
AWSのドキュメントのほか、以下の記事が参考になりました。
・Amazon EC2 Container Service(ECS)の概念整理 – Qiita
https://qiita.com/NewGyu/items/9597ed2eda763bd504d7
・Amazon EC2 Container Service(ECS)のデータモデルについて整理した – DevelopersIO
https://dev.classmethod.jp/articles/amazon-ecs-datamodel/
最初のQiita記事の「Serviceが中心」と考えると、理解しやすいと思います。
どの構成要素が、どの属性をもっているかについては、AWSCLIのドキュメントを読むと、理解が早いかもしれません。
・AWS CLI Command Reference
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecs/index.html
2. 環境変数の渡し方
コンテナアプリケーションに環境変数を渡すには、以下の方法があります。
- タスク定義で指定
- Secrets Manager または Systems Managerパラメータストアを使用
- S3バケットに保存した環境変数ファイルを参照
今回は、アプリケーション開発担当の方のご要望で C. を採用しました。
S3バケットを用意し、タスク実行ロールに追加設定を行いました。
3. タスクロールとタスク実行ロール
タスク定義を設定する際に、AWSリソースへのアクセス制御のため、IAMロールを指定するのですが、「タスクロール」と「タスク実行ロール」の2つがあって、とまどいました。
AWSマネジメントコンソールのヘルプを読んでもわかりにくかったのですが、以下のように理解しました。
- タスクロール
コンテナ化されたアプリケーションが、AWSリソースにアクセスするために必要な権限を管理する。
- タスク実行ロール
ECSコンテナエージェントが、コンテナを起動・実行するときに、AWSリソースにアクセスするために必要な権限を管理する。
ややこしい。。
今回の環境では、それぞれのロールに、以下の権限(ポリシー)をアタッチしました。
- タスクロールにアタッチしたポリシー
- コンテンツ配信用S3バケットの読み書き
- ジョブ管理用SQSキューの読み書き
- ECS Exec実行に必要な権限(ログ出力含む)
- FireLens Fluent BitコンテナからCloudWach Logsへのログ出力権限
- タスク実行ロールにアタッチしたポリシー
- S3バケットの環境変数ファイル取得
- AmazonECSTaskExecutionRolePolicy(AWSがデフォルトで用意した、タスク実行に必要な最小限のポリシー。ECRからのコンテナイメージダウンロードやCloudWatch Logsへのログ出力など)
CloudWatch Logsへのログ出力については、デフォルトでは、コンテナアプリケーションではなくECS(のコンテナエージェント)がやってくれるので、「タスク実行ロール」への権限付与でよいのですが、FireLensを使用する場合は、「タスクロール」への権限付与が必要となります。
Fluent Bitコンテナが、(ECSコンテナエージェントを介さずに直接)CloudWatch Logsにログを書き出す、ということですね。
また、ECS Execのログ出力についても、「タスクロール」への権限付与が必要という情報がありました。
(参考)
・ECSのタスクロールとタスク実行ロールの違い – karakaram-blog
https://www.karakaram.com/difference-between-ecs-task-role-and-task-execution-role/
・ECS Exec のログ記録はタスクロールで行われるため注意しよう – DevelopersIO
https://dev.classmethod.jp/articles/ecs-exec-use-task-role-for-logging/
4. 稼働中のコンテナでデバッグしたい
これもアプリケーション開発担当の方からのリクエストでしたが、ECS Execを有効化することで解決しました。
ECS Execは、AWSCLIを使用して、実行中のコンテナに対してコマンドを実行できる機能で、Dockerのdocker execコマンドと同じようなものですね。
この機能のことは知らなくて、Fargateではホストサーバーにログインできませんから、デバッグやトラブルシューティングの際はログを確認するしかないと思っていたのですが、便利な機能ですね。
タスクロールに必要なIAMポリシーを追加し、AWSCLIで ECS Exec機能を有効化しました。
※AWSマネジメントコンソールは、ECS Execを設定できないとか、新バージョンのコンソールではFireLensを設定できないとか、なぜか設定できない項目があるのが不思議です。
(参考)
・[アップデート] 実行中のコンテナに乗り込んでコマンドを実行できる「ECS Exec」が公開されました – DevelopersIO
https://dev.classmethod.jp/articles/ecs-exec/
5. FireLensを使用したログ出力
当初、ECSのアプリケーションログはすべて標準出力に出力し、CloudWatch Logsの1つのロググループに出力するよう設定していました。
しかし、アプリケーション開発担当の方から、以下の理由で、調査に必要なログを見つけにくいという意見がありました。
- 一度のログが複数レコードに分かれてしまう。
- ヘルスチェックなど、重要ではないものも含め、大量のレコードがある。
このため、以下の記事を参考に、アプリケーション側で一度のログをJSON形式の1レコードにまとめ、FireLens Fluent Bitを利用して、重要度やアプリケーション処理によって、CloudWatch Logsの複数のロググループに振り分けるようにしました。
(参考)
・Rails on ECSな構成でFluent Bitを利用して快適なロギングを実現する – Zenn
https://zenn.dev/machamp/articles/50ed6bc383539c
ヘルスチェックやデバッグログの保存期間は1日、アプリケーションログの保存期間は2年など、ログごとに保存期間を変更することもできました。
FireLens Fluent Bitを利用したログ振り分けで苦労したことは、以下の別記事にまとめています。
・AWS FireLens Fluent BitでAND条件のログ振り分け – 稲葉サーバーデザイン
https://inaba-serverdesign.jp/blog/20230524/firelens-fluent-bit-and-condition.html
6. 外部アクセス時のアクセス元IPアドレスを固定したい
今回、外部サービスとの連携のため、
「アプリケーションからインターネットアクセスする際の、アクセス元IPアドレスを固定する」
という要件が出てきました。
外部サービス側に、アクセス元IPアドレスを登録する必要があるとのこと。
Fargate環境はIPアドレスを固定できないので、アクセス元IPアドレスを固定するには、以下の参考記事のとおり、NATゲートウェイを使用しました。
(参考)
・Fargateから固定IPを使って外部サーバーへ接続する方法を調べてみた – DevelopersIO
- Publicサブネットで、NATゲートウェイを起動し、ElasticIPを付与する。
- Fargateが稼働するサブネットのルートテーブルで、デフォルトゲートウェイをNATゲートウェイに設定する。
なお、このような構成にすると、ECSと、VPC外のAWSサービスとの通信もNATゲートウェイ経由となります。
今回は、以下のようなAWSサービスとの通信が該当しました。
- S3への静的コンテンツファイルアップロード、ダウンロード
- CloudWatch Logsへのログ出力
- ECRからのコンテナイメージダウンロード
- SQSキューへのアクセス
NATゲートウェイは、起動時間の課金のほか、インバウンド、アウトバウンド双方向のデータ処理量(≒転送量)の課金があるため、上記の通信がすべて課金対象となってしまいます。
AWSサービスへの通信を、NATゲートウェイ経由ではなくVPCエンドポイント経由とすれば、NATゲートウェイのデータ処理量課金がなくなりますが、今度はVPCエンドポイントの起動時間課金・データ処理量課金が問題となります。
ECRのように、ひとつのサービスへの通信でも、複数のエンドポイントが必要となるのも痛いところです。
AWSサービスへの転送量がどれぐらいになったら、VPCエンドポイントを使用すべきなのかは、以下の記事が参考になります。
・ECS/Fargate VPCエンドポイントとNAT ゲートウェイの損益分岐点をざっくり計算してみた – スクショはつらいよ
今回のケースでは、コンテナのデプロイ回数は比較的少なく、ログもそれほど多くなく、、転送量は数百GBには達しないだろうと予想しました。
このため、S3との通信についてのみ、無料で使用できるゲートウェイ型VPCエンドポイントを使用し、CloudWatch LogsおよびECRについては、VPCエンドポイントを使用せず、NATゲートウェイを使用することになりました。
- S3への静的コンテンツファイルアップロード、ダウンロード → VPCエンドポイント経由(ゲートウェイ型)
- CloudWatch Logsへのログ出力 → NATゲートウェイ経由
- ECRからのコンテナイメージダウンロード → NATゲートウェイ経由
- SQSキューへのアクセス。 → NATゲートウェイ経由
今後の運用で、NATゲートウェイのデータ処理量に注意し、多くなってくるようでしたら、VPCエンドポイントの導入を検討します。
CloudWatchダッシュボードでも、、NATゲートウェイのデータ処理量合計を表示するようにしました。
以下の記事を参考に、NATゲートウェイのメトリクス BytesInFromSource, BytesInFromDestination の、期間内の合計値を表示しています。
・[新機能] NAT GatewayがCloudWatchでモニタリング可能になりました – DevelopersIO
https://dev.classmethod.jp/articles/cloudwatch-for-nat-gateway/
・CloudWatch ダッシュボードで指定した期間のデータ量の合計の表示方法と Metric Math を使用した表示方法の紹介 – DevelopersIO
https://dev.classmethod.jp/articles/aggregate-data-for-the-specified-period-in-the-cloudwatch-dashboard/
AWS ECS on Fargateを採用してよかったこと
アプリケーション開発担当の方にうかがいました。
- これを機に、PC開発環境にDockerを導入。コンテナをDBサーバー、バックエンド(API)、フロントエンドに分割して開発(本番環境ではそれぞれ、RDS, ECS, S3に相当)。デプロイ作業はバックエンドのみをECSにデプロイできるので非常に簡単になった。
- これまではデプロイツールにCapistranoを利用し、Gitに一旦デプロイしてマージするなどの手間がかかった。今はすぐにビルドしてECRにアップロードし、Webコンソールでデプロイまででできて楽になった。ロールバックもサービスのリビジョン変更で終わるので非常に楽。
- デプロイ時、ヘルスチェックで失敗しても、サーキットブレーカー機能で自動的にロールバックされるので安心感がある。
- 環境変数を1ファイルにまとめて切り分けられるのが非常に嬉しい。コンテナのビルドと環境変数の設定変更を別々に行えるので、楽になった。環境変数のファイルは専用のS3バケットで管理しているので、扱いが楽になった。
- (FireLensを利用して)ログを切り分けて出力できるので、エラーの原因究明が素早くできるようになった(これまでは、VMのログファイルをダウンロードしてから検索、などの地味な方法を採っていたので)。ログの検索もコンソールからできるので、ものすごく楽。
- 開発環境、サーバー環境ともに以前の環境が雑過ぎた感があるが、これを機に刷新できたのが非常に嬉しい。
半分ぐらいは、ECSのメリットというよりは、コンテナ化によるメリットになりますが、今回チャレンジしたことで、ご満足いただけたようで何よりです。
デプロイ、CI/CDまわりは、今回はとりあえず不要とのことで構築していませんが、必要となる場面も出てくるかもしれませんね。
運用についてはまだ始まったばかりですが、心理面を含め、運用管理コストはかなり下げられると期待しています。
Fargateを使用する分、サーバー費用は少しお高いかもしれませんが、サーバー負荷を注視して、コスト最適化を支援できればと思います。
おわりに
ECS on Fargateの構築で出てきた課題と解決策、そして、アプリケーション開発担当の方にうかがった ECS on Fargateを採用してよかった点をまとめました。
今回は初めてだったので、少し手間がかかってしまいましたが、ネット上に情報がたくさん公開されていることもあり、ひとつひとつ、解決することができました。
ありがとうございます!
次回以降はスムーズに構築できるのではないかと思います。
(関連記事)
・AWS FireLens Fluent BitでAND条件のログ振り分け
https://inaba-serverdesign.jp/blog/20230524/firelens-fluent-bit-and-condition.html