AWS Fargateにコンテナを構築してみた

#はじめに
　昨今、コンテナ技術の普及やクラウドサービスの発展により、AWSのマネージドサービスを提供する、コンテナ向けサーバーレスコンピューティングエンジンである[AWS Fargate](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/what-is-fargate.html)が注目されているようです。

　今回、AWS Fargate上にコンテナを構築し、AWSのサービスを利用して運用・監視してみました。本記事では、AWS Fargateにコンテナ適用を検討している方向けに、今回利用したAWSのサービスやAWS Fargate上のコンテナの構築・運用・監視について簡単にまとめます。

　以降では、対象としたAWSの構成と、その構成を構築してコンテナを運用・監視する際に利用したAWSのサービスや気になるポイントを説明していきます。（※Dockerやコンテナ、AWSの基本的な知識を有することを前提とします。）

　AWSのサービスの具体的な利用手順は説明していないので[公式サイト](https://aws.amazon.com/jp/)などを参照してください。

#AWS Fargateとは
　AWS Fargateは、[Amazon ECS](https://aws.amazon.com/jp/ecs/?whats-new-cards.sort-by=item.additionalFields.postDateTime&whats-new-cards.sort-order=desc&ecs-blogs.sort-by=item.additionalFields.createdDate&ecs-blogs.sort-order=desc)とともに使用して、サーバレスでコンテナを管理します。そのため、コンテナを実行するサーバ（[Amazon EC2](https://aws.amazon.com/jp/ec2/?ec2-whats-new.sort-by=item.additionalFields.postDateTime&ec2-whats-new.sort-order=desc)のインスタンス）の管理・運用を意識する必要がなくなるといった利点があります。

#AWSの構成
　次のAWSの構成を構築してみました。
　　　　　　　<img width="550" alt="AWS構成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397391/519f3cc3-110d-29d7-6b77-cf177d897ccb.png">

　上の図では、クライアント端末からAWS Fargate上のコンテナ（APサーバを構築）に、リバースプロキシサーバとロードバランサ（[ELB](https://aws.amazon.com/jp/elasticloadbalancing/?elb-whats-new.sort-by=item.additionalFields.postDateTime&elb-whats-new.sort-order=desc)）を経由してHTTPリクエストを送信します。コンテナのデータはDBサーバに格納します。

#コンテナ構築
　AWS Fargate上にコンテナを構築するために、次の①から⑤の流れを行いました。
　※Dockerfileの作成やコンテナイメージの作成には、別途、作業用のAmazon EC2を用意しました。

　① Dockerfileの作成
　② コンテナイメージの作成
　③ コンテナレジストリにコンテナイメージの格納
　④ AWS Fargateの[クラスター](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/clusters.html)、[サービス](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs_services.html)、[タスク定義](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definitions.html)の作成
　⑤ タスクの実行（コンテナの起動）

　コンテナ構築では、セキュア設計やコンテナ管理を容易にするために、シークレットな情報をコンテナから切り離したり、コンテナイメージからソフトウェアの設定ファイルなどを切り離すことなどがポイントになります。

##■ IPアドレス
　AWS Fargateでは、コンテナを再起動するたびに、コンテナに動的にIPアドレスが割り当てられます。そのため、直接コンテナにローカルIPアドレスやAWSのElastic IPアドレスを設定できません。
　起動したコンテナに適切にリクエストを送るために、AWS Fargateの前段にロードバランサ（ELB）を配置し、次のAWS Fargateのサービスのロードバランシングの設定画面からロードバランサ（ELB）を設定[^1]しました。

|<img width="650" alt="AWS構成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397391/846000c1-12ad-3042-522a-2b7ffa32a6ca.png">|
|:--:|

　これにより、コンテナ起動時に、対象のロードバランサ（ELB）のターゲットグループ（ターゲットとなるIPアドレス）が自動的に更新され、起動したコンテナに適切にリクエストを振り分けることができるようになります。

##■ Amazon ECR
 　AWS Fargateでは、コンテナレジストリ（[Docker Hub](https://hub.docker.com/)や[Amazon ECR](https://aws.amazon.com/jp/ecr/)）に格納されたコンテナイメージを元にコンテナを構築します。今回は、自作したDockerfileからコンテナイメージを作成し、Amazon ECRに格納しました。

　Amazon ECRは、格納されたコンテナイメージを自動でスキャンして脆弱性を検査します。さらに、コンテナイメージを暗号化することもできるため、セキュリティ面も安心です。

##■ Amazon EFS
 　コンテナの管理を容易にするために、AWSのファイルシステムを提供する[Amazon EFS](https://aws.amazon.com/jp/efs/)を利用[^2]しました。
　コンテナイメージには、コンテナ上で動作するソフトウェアの設定ファイルなどを含めることができますが、設定を変更するたびにコンテナイメージを作り替える必要があり、コンテナの管理に手間がかかります。Amazon EFSを利用することで、設定ファイルなどをコンテナイメージから切り離すことができ、設定変更時にコンテナイメージの作り替えが不要になるため、コンテナ管理が容易になります。

　あらかじめ、ソフトウェアの設定ファイルなどをAmazon EFSに格納し、AWS Fargate上でコンテナ起動時に自動で読み込ませることができます。

　　　　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397391/50593655-8ff6-6f3c-39d7-dc5735d09a6b.png" width="500" alt="Amazon EFSの利用">

##■ AWS Systems Manager
 　DB接続情報（ユーザ名やパスワードなど）は、[AWS Systems Manager パラメータストア](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html)に保存することで、ソフトウェアの設定ファイルからシークレットな情報を切り離しました。

　AWS Systems Manager パラメータストアは、設定データの管理と機密管理のためのストレージです。パスワード、データベース文字列、Amazon マシンイメージ (AMI) ID、ライセンスコードなどのデータをパラメータ値として保存でき、パラメータ値はプレーンテキストまたは暗号化されたデータとして保存できます。次はパラメータの保存例です。

|<img width="550" alt="AWS構成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397391/d9e2cf28-89bc-4a9f-0664-13511b1126b3.png">|
|:--:|

　上記のパラメータ値を環境変数としてAWS Fargate上のコンテナで利用するには、次に示すように、タスク定義の「コンテナの編集」画面から「環境変数」欄にAWS Systems Manager パラメータストアのパラメータ名を設定します。

|<img width="800" alt="AWS構成図" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397391/f410aa01-908c-b7c6-42c9-1a17a97815e4.png">|
|:--:|

#コンテナ運用
　コンテナ運用では、システム安定稼働のための設定がポイントになります。今回はコンテナの負荷に応じたスケーリングで可用性を向上するために、AWS FargateにAuto Scalingを設定しました。

##■ Auto Scaling
　Auto Scalingは、常時必要なコンテナの起動台数や、負荷に応じてスケールイン/スケールアウトしたときのコンテナの最小/最大起動台数を設定します。さらに、自動タスクスケーリングポリシーを作成すると、リソース使用量のしきい値に応じたスケーリングやスケーリングのクールダウン期間(※)などを設定できます。

　AWS Fargateでは、コンテナの負荷に応じて柔軟にコンテナ台数をスケーリングすることが可能です。また、ホストを管理する必要がないため、スケールアウト時にサーバリソースを増強する必要などがなく、コストの削減になります。

※秒単位でスケーリングの間隔を指定します。

#コンテナ監視
　コンテナ監視では、運用時と同様にシステム安定稼働のための設定がポイントになります。今回はコンテナの保守性を向上するために、コンテナのログやリソース使用状況を監視しました。また、[Prometheus](https://prometheus.io/)に対応した形式の、コンテナの詳細なリソース状況やソフトウェアの統計情報、Java VMのパフォーマンスなどといったメトリクス[^3]（Prometheusメトリクス）も監視しました。

##■ Amazon CloudWatch
　コンテナのログやリソース使用状況を監視するために、[Amazon CloudWatch](https://aws.amazon.com/jp/cloudwatch/)を使用しました。AWS Fargateでは、デフォルトでコンテナのログやリソース使用状況をAmazon CloudWatchに送信します。

　Amazon CloudWatchでは、アラームを作成することで、コンテナのエラーメッセージの出力やリソースの異常値を検知したときにメール通知(※)することも可能です。

※メール通知には[Amazon SNS](https://aws.amazon.com/jp/sns/?whats-new-cards.sort-by=item.additionalFields.postDateTime&whats-new-cards.sort-order=desc)の設定が必要です。

##■ Amazon CloudWatch エージェント
　Prometheusメトリクスを監視するために、[Amazon CloudWatch エージェント](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html)を構築しました。Amazon CloudWatch エージェントを利用すると、自動でコンテナのPrometheusメトリクスを監視し、Amazon CloudWatchに送信してくれます。

　Amazon CloudWatch エージェントの構築には、[Amazon CloudFormation](https://aws.amazon.com/jp/cloudformation/)の[テンプレート](https://aws.amazon.com/jp/cloudformation/resources/templates/)を使用しました。テンプレートを使用すると、Amazon CloudWatch エージェントの構築に必要なIAMロール、AWS Systems Manager パラメータ、AWS Fargateのサービスやタスク定義などのAWSリソースが自動的に生成されるため、構築が容易です。

　AWS Fargateの場合、テンプレートは次のコマンドを実行して取得します。

```
# curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/master/ecs-task-definition-templates/deployment-mode/replica-service/cwagent-prometheus/cloudformation-quickstart/cwagent-ecs-prometheus-metric-for-awsvpc.yaml
```

　主なテンプレート編集箇所は「task_definition_list」で、以下の例のように定義することで監視したいタスク（コンテナ）を指定できます。

```json
"task_definition_list": [
  {
    "sd_job_name": "prometheus", // メトリクスを出力したいAmazon CloudWatchのログストリーム名
    "sd_metrics_ports": "8080", // メトリクスが出力されるポート
    "sd_container_name_pattern": "^container$", // 正規表現を使用したコンテナ名
    "sd_task_definition_arn_pattern": ".*:task-definition/.*sample-task.*:[0-9]+", // 正規表現を使用したタスク定義名
    "sd_metrics_path": "/metrics" // メトリクスが出力されるエンドポイント
  }
]
```

　上記を設定するとAmazon CloudWatchのロググループ「aws/ecs/containerinsights/<クラスター名>/prometheus」の、"sd_job_name"に設定した名前のログストリーム配下にPrometheusメトリクスが出力されます。

#まとめ
　AWS Fargate上にコンテナを構築して、運用・監視してみました。
　AWS Fargateは、オンプレやAmazon EC2のDocker上にコンテナを適用する場合と比べて、ホストとなるサーバ（インスタンス）やDockerを意識する必要がないので、コンテナ管理が容易で、AWSマネジメントコンソールの操作で簡単にコンテナを構築できました。また、AWSのサービスと連携することにより、効率的にコンテナを構築・運用・監視できそうです。
　次は、他クラウド環境との比較や、運用費などのコスト面についても調べて比較してみたいです。

・Amazon Web Services、“Powered by AWS”ロゴ、［およびかかる資料で使用されるその他のＡＷＳ商標］は、米国その他の諸国における、Amazon.com, Inc.またはその関連会社の商標です。
・Docker は、Docker Inc. の米国およびその他の国における登録商標もしくは商標です。
・その他記載されている会社名、製品名はそれぞれ各社の商標および登録商標です。

[^1]: 単一のロードバランサを設定する場合はAWSマネジメントコンソールの操作で設定できますが、複数のロードバランサを設定する場合は、[サービス定義パラメータ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/service_definition_parameters.html)を使用してAWS CLIで設定する必要があります。
[^2]: AWS Fargateのプラットフォームバージョンが1.4.0の場合のみ利用できます。
[^3]: ソフトウェアによっては、Prometheusメトリクス出力のために別途プラグインなどを適用する必要があります。