こんにちは、普段PR TIMES STORY(以下STORY)の開発リーダーをしている岩下(@iwashi623)です。
本記事ではPR TIMESで稼働しているAWS ECSやFargateで採用中のおすすめの設定について書いていきます。なお、PHPカンファレンス 2022のスポンサーLT で話した内容と重複しますので、興味がある方はスライドやYouTubeの動画も覗いてみてください。
おすすめ設定1 ECS Exec
まずおすすめしたいのは、ECS Execです。
以下AWSドキュメントより抜粋
Amazon ECS Exec を使用すれば、最初にホストコンテナのオペレーティングシステムとやり取りしたり、インバウンドポートを開いたり、SSH キーを管理したりすることなく、コンテナと直接やり取りできます。ECS Exec を使用して、Amazon EC2 インスタンスまたは AWS Fargate で実行されているコンテナでコマンドを実行したり、シェルを取得したりできます。これにより、診断情報を収集し、エラーを迅速にトラブルシューティングすることが容易になります。たとえば、開発コンテキストでは、ECS を使用して、コンテナ内のさまざまなプロセスと簡単にやり取りし、アプリケーションのトラブルシューティングを行うことができます。また、実稼働シナリオでは、これを使用して、コンテナへのブレークグラスアクセスを行って問題をデバッグできます。
ECS Execを有効にしておくと、コマンド実行のための踏み台サーバーを用意する必要がなくなります。普段ローカルでdocker exec
をしているのと同じような感覚でコンテナに対して直接コマンドを実行できるので、セキュリティ上のリスクをおかしてまでSSHのポートを開放する必要や、煩わしいキーの管理などから開放されるため重宝しています。
注意点としてはこちらの設定はAWS CLI上やTerraformなどのIacツールからしか有効にできません。コンソールから気軽にできるようになると嬉しいです。
設定の方法はクラスメソッドさんのブログがかなりわかりやすく解説されていました。
Terraform上では、

の様な感じにenable_execute_command = true
と記述すると、ECS Service単位で有効になります。(別途、タスク実行ロールをにSSMのポリシーを付与する必要があるので注意)
おすすめ設定2 マイグレーション用のFargateタスク
次におすすめしたいのは、マイグレーションを自動で実行してくれるためのECSタスクの設定です。
弊社のECS/Fargateで動作しているアプリケーションはLaravelで書かれており、DBのマイグレーションにはphp artisan migrate
コマンドを実行することで反映されます。こちらのコマンドは前述したECS Execでコンテナの中に入って手で実行することもできますが、ヒューマンエラーを防ぐためにも自動化していきたいです。
そこで弊社ではマイグレーション実行にはデプロイワークロードの中でマイグレーション専用のECSタスクを自動で実行するオペレーションをとっており、エンジニアは正しいマイグレーションファイルを書くことだけに注力できる環境を整えています。

上記は弊社のデプロイワークロードを表した図で、黄色い丸で囲まれている部分がマイグレーションのタスクの流れになります。
- GitHubのデプロイブランチにコードがマージされることをトリガーにCodePipelineが実行される。
- CodePipelineのBuildステージでCodeBuildが実行される。コンテナイメージをBuildする。
- コンテナイメージのBuild、Push後に、Buildしたイメージを使用するマイグレーションタスクを実行。
3のマイグレーションタスクの実行は、CodeBuildの設定ファイルであるbuildspec.yml
の中でaws ecs run-task
コマンドを走らせることで実現しています。このタスクはphp artisan migrate
を実行すると完了後に自動でVPCやサブネット上から消滅してくれます。
アプリケーション側では、Dockerのエントリーポイントでコンテナ起動時タスク定義の引数に応じて処理を変えるようなBashスクリプトが実行され、その中でphp artisan migarate
が実行されています。
# PHPアプリケーションのDockerfile
# 〜〜〜中略〜〜〜
// entrykitのセットアップ
RUN wget -q --no-check-certificate https://github.com/uzulla/progrium_entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_$(uname -m).tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_$(uname -m).tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_$(uname -m).tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink
# 〜〜〜中略〜〜〜
ENTRYPOINT [ \
"switch", \
"shell=/bin/bash", \
"--", \
"render", \
"etc/entrypoint.sh", \
"--", \
"bash", "etc/entrypoint.sh" \
]
#!/bin/bash
# Dockerfileエントリーポイントで実行されるBashスクリプト
APP_ENV={{ var "APP_ENV" | default "development" }}
# 〜〜〜中略〜〜〜
case "$1" in
# 〜〜〜中略〜〜〜
'migrate' )
php artisan migrate --env=$APP_ENV --force
;;
# 〜〜〜中略〜〜〜
* )
echo "Unknown Command: $1"
exit 1
;;
esac
おすすめ設定3 タスクごとのリソース監視
コンテナはサーバーよりもライフサイクルが短く、カジュアルに作成→消滅を繰り返すものですが、どんな処理が負荷が高いかなどを検討する際にはやはりCPUやメモリのリソースの数値は有効で、モニタリングできるようにしておきたいです。
しかしながら、デフォルトのECSの設定では、ECSサービスごとのメトリクスしか閲覧できず、これでは指定したある一定のECSタスクのみで行っている処理などの数値が他のタスクの数値と合わさって表示されてしまいます。

これらはContainer Insightsやタスクのサイドカーとしてサードパーティの管理ツールのエージェントを起動することで解消できます。
Container Insights
Container Insightsはタスクごとの各種メトリクスをCloudWatchに転送することで、AWSコンソールの中でタスクごとのリソースについてモニタリングできるようになります。これらはカスタムメトリクスとして課金の対象となります。
設定を有効にするには、ECSクラスターを作成時にコンソール上から有効にするか、TerraformやAWS CLIを使ってコマンドを実行します。
resource "aws_ecs_cluster" "hoge" {
name = "hoge-cluster"
## 以下を追記
setting {
name = "containerInsights"
value = "enabled"
}
}

サイドカーとしてサードパーティの管理ツールのエージェントを起動
弊社ではmackerelやNewRelicを使用しているため、それらのツールにもメトリクスを流したい要望がありました。それらは各社が用意している設定をECS上で実行するだけで簡単に各サービスにメトリクスを流すことができます。
mackerelとNewRelicはサイドカーをタスク定義の中に入れることで各メトリクスを収集する方法をとっていて、設定方法はよく似ています。

上記画像はMackerelにメトリクスを流しているSTORYのECSの構成図ですが、黄色い丸で囲った部分がサイドカー部分となります。mackerel-agentを動作させるためのコンテナをECSタスク内に用意することで、メトリクスを取得しています。
まとめ
最近はECS・Fargateについての記事もかなり増えてきて、各設定についての詳細な記事はヒットしやすくなってきたのですが、ある程度まとまったおすすめ設定についての記事などがなかなか見つからなかったので本記事を作成してみました。
マネージドサービスであるECSやFargateをうまく使っていければ、社内のエンジニアリソースをさらにアプリケーションに集中できます。マネージドサービスに乗っかりつつ、設定についてはカスタムして自分たちに最適なAWSリソースを構築していきたいと思います。