Dockerコンテナ活用！ AWS ECSでDockerコンテナ実行する【Dockerコンテナ・グレートジャーニー⑦】

# はじめに
本記事は、2段階構成で執筆しています。
1. コンテナをECS上で実行する手順
1. ECSタスクの詳細解説

すぐにECSを動かしたい！　という方はステップ１([ECS上でコンテナを実行する手順](https://qiita.com/drafts/90df3c90353ed6fb3fd8/edit#ecs%E4%B8%8A%E3%81%A7%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E6%89%8B%E9%A0%86))から読んで手を動かしていただき、動かしたことはあるけれど仕組みはよくわかっていない、という方はステップ２([解説編](https://qiita.com/drafts/90df3c90353ed6fb3fd8/edit#%E8%A7%A3%E8%AA%AC%E7%B7%A8))を見ていただくといいでしょう。

# Dockerコンテナグレートジャーニー（過去ログ）
本記事は、シリーズものの第7回の記事となります。単体としても見ていただけますし、コンテナ技術のもっと基礎的なところから知りたいという方は、最初の記事もぜひご覧ください。
本記事はAWSECSにフォーカスしていますが、他の記事ではDockerコンテナの基礎を解説しています。

### 旅路（インデックス）
1. [そもそも仮想化とは？　仮想化ではないシステムとは？](https://qiita.com/Toyo_m/items/3060fc5961d62a840bee)
1. [Dockerコマンド基礎（環境構築～ubuntu/httpdコンテナでのコマンド実行）](https://qiita.com/Toyo_m/items/94de894a8558d94b02d1)
1. [Dockerのストレージについて](https://qiita.com/Toyo_m/items/95a2543c07dc5b126208)
1. [Dockerのネットワークについて](https://qiita.com/Toyo_m/items/52fa81948d5746dd2afc)
1. [Docker-compose でコンテナをまとめる](https://qiita.com/Toyo_m/items/ef14658629d3118003ea)
1. [Docker image と Dockerfile](https://qiita.com/Toyo_m/items/5713f85a9d5ff8d9cc0d)
1. AWS ECS で Dockerコンテナを走らせる <font color=orange>【⇦本記事】</font>

# ECS上でコンテナを実行する手順
今回は、Dockerfileをビルドできる環境が揃っている前提で話を進めます。
Dockerfileの仕組みがわからない方や、ビルドができないという方は、過去の記事や、別のDockerコンテナ記事を参照ください。
## 1. ECRにイメージをPUSHする
まずは、AWSクラウド上にDockerイメージをPushします。
最初はリポジトリ（イメージを保管する場所）がないので、作成しましょう。
### 1-1. サービス画面へ
1. 「Elastic Container Registry」と検索してサービス画面に遷移
1. Private registry -> Repositories をクリック
1. 右上の「リポジトリを作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/681c381a-90d4-49f2-801d-8f3503937124.png)
### 1-2. リポジトリに名前を付ける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/a0973455-5c4c-40ec-b4fc-c04844fe7b50.png)

### 1-3. それ以外に設定変更する項目はありません。「作成」を押してリポジトリを作成しましょう。以下のようにリポジトリができていればOKです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/d3f48551-70a0-443d-a872-57f8ce35c346.png)

### 1-4. 次にローカルのDockerイメージをリモート側にPushします。先ほど作成したリポジトリをクリックすると、右上に「プッシュコマンドを表示」というボタンがあるので、それをクリックします
すると4つのコマンドが出てくるので、それを上から順に実行していきましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6034fc1f-83ad-4a35-b80e-79195e0ce3e7.png)

※ローカルのターミナルがAWSの認証情報を持っていないとエラーになります。権限エラーが出る場合は、別の記事で確認してみてください。

https://zenn.dev/longbridge/articles/800fd8c4e272aa

https://qiita.com/Neville/items/b1a00cb18292b0f18846

成功すれば、Pushが開始されます。ECRの画面を更新し、「latest」のイメージができていれば成功です
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/d5e65f0c-c10b-4c37-ac82-8a0116791a0b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/1c51f563-c735-4ea7-93d9-412b620d00b2.png)


## 2. タスク定義を作成する
これでイメージのアップロードができたので、次はタスク定義を作成します。
まずは「Amazon Elastic Container Service」に移動して、左側のペインの「タスク定義」をクリック、新しいタスク定義を作成します。

### 2-1. タスク定義ファミリー
タスク定義ファミリーに、わかりやすい名前を付けます
起動タイプはFargateでいいでしょう。メモリ容量なども、テスト実行ならそのままで十分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6528bfec-8251-4ce7-8dc1-2fb9dcf8dd3c.png)

### 2-2. IAMロールの設定
設定は2つあります。
「タスクロール」は、テスト実行では他のAWSリソースにアクセスしないのでひとまずいりません。
「タスク実行ロール」は必要になるので、IAMロールが存在しない場合は新しく作ります。
作成画面に遷移し、「AWSのサービス」⇒「Elastic...」で検索し、「Elastic Container Service」を選択しましょう。「ECS」と検索しても出てこないので注意。
ユースケースには「Elastic Container Service Task」にチェックを入れてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/f5e7b1f0-ca51-4205-b1e1-5b93af1c0944.png)

### 2-3. 許可ポリシー
[AWS公式](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html)が出しているドキュメントを見ると、「AmazonECSTaskExecutionRolePolicy 」というポリシーを付けろと言われています。
検索して、チェックを付けて次へ行きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/26344e33-8327-4859-8fca-bd8f00073ee4.png)

あとはRole名を入力して完了です。
今回は「qiita-practice-run-ecs-task-role」という名前にしました。
画面は戻ってタスク定義。
IAMRoleが作成されると、それが選択画面に現れるので選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/80d3164d-0683-404a-8c3d-e0f8ba78d86f.png)

### 2-4. コンテナ設定
続いてコンテナ設定をします。
コンテナ名は任意の名前で大丈夫ですが、わかりやすい名前にしましょう。今回は「test-worker」とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/07509b21-9d12-479d-8ab9-3dd12cfd9c77.png)
イメージURIの枠も必須です。
この文字列は、先ほど作成したECRのlatestイメージ画面から取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6fee1c94-1e52-4449-811e-a203f0e58c25.png)
必要なら環境変数も設定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/797d7bb9-f85a-4994-b45d-eb887d1df859.png)

他に設定する項目はないので、「作成」を押して進みましょう。

## 3. ネットワークを整備する
（すでにVPCを作成済の場合はスキップしても大丈夫です）
ほかの多くのAWSサービスの例にもれず、ECSFargateもVPC内に設置する必要があります。
VPC画面に遷移し、「VPC」⇒「VPCを作成」をクリックしてください。
作成方法を「VPCなど」にすると、必要なリソースを一括作成できて楽です。

### 3-1. ネットワーク構成
今回はテスト用なのでAZは1つ、パブリックサブネット1つ、プライベートサブネットはなしの、小さな構成にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/6a9bea73-062e-4b0e-86a8-2be191162662.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/479fe1ba-ebfb-45f2-9402-b473cd6b5315.png)

ネットワーク的なセキュリティを担保したい場合は、
* プライベートサブネット＋NATゲートウェイ作成
* プライベートサブネット＋ACL設定変更＋IPアドレス付与設定オン（ECSタスク起動時）
のような設定が必要になります。詳しくは解説編で話します。


## 4. タスクを実行する
### 4-1. クラスターの作成
「Elastic Container Service」⇒「クラスター」を選択します。
まだクラスターが存在しない場合、右上のボタンから新しく作成しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/3ba73291-00e8-4207-b8a9-95f0b023aa26.png)
ECSタスク を実行するだけであれば、クラスター名だけ任意の名称にして、そのまま作成すればOKです。
私の環境では、前に作っていたものがあったのでそれをそのまま使います。

### 4-2. タスクの実行
クラスターが作成できたら、そのクラスターをクリックしてください。その後「タスク」⇒「新しいタスクの実行」をクリックします。
タスク定義ファミリーを設定できるので、先ほど作成した「qiita-practice」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/46a287cf-3e40-481a-8d31-e554a4bf6622.png)

次に、「ネットワーク」⇒「VPC」から、任意の作成したVPCと付随するSubnetを指定します。セキュリティグループはDefaultで大丈夫です。
PublicIPの割り当てについてはどちらでもよいですが、少額ですが課金対象なのでOffにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/00307c1f-0852-40dc-94ba-9c2416ce06b5.png)

ここまでできれば、「作成」をクリックしてECSタスクの作成完了です。
タスク画面でログを確認できるので、正常に動作しているかチェックしましょう。


### 4-3. エラーが起きたとき
コンテナが立ち上がらない、特にログが出る前にエラー終了してしまっている場合、要因は幅広くデバッグが難しいこともあります。
自分で探してみて原因が全く分からない場合、AWS公式の診断ツールを使用するのも手です。
先日記事で投稿したので、こちらをご覧ください。

https://qiita.com/Toyo_m/items/af747932cea05de5dc44

# 解説編
ECSを起動させるだけであれば、指示に従ってポチポチしていれば何とかなります。
ただその中身を知らなければ、エラーに対応できなかったり、細かいところで詰まってしまうことも多くなります。特にECS周りでは謎のエラーに悩まされることも多いので、エンジニアとしては知っておくに越したことはありません。

というわけで、本節ではECSタスクの設定にどんな意味があるのかを、図解付きで分かりやすく解説します。

## タスクとはなにか？
そもそもECSタスクとはなんでしょうか。
まず前提として、ECSでコンテナを使用する場合に使える機能は「ECSサービス」と「ECSタスク」の２つになります。
それぞれについて軽く説明すると、ECSサービスはコンテナを起動させ続けるためのものです。エラーが起きてコンテナが停止してもすぐに代わりのコンテナが立ち上がったり、デプロイの際もローリングアップデートで機能を途切れさせずに更新することが可能です。ECSサービスは常駐サービスのサーバー等によく使われます。
対してECSタスクは、既定の処理を完了したら即座にコンテナを停止させるものです。実行時間や処理容量がカスタマイズできるLambda、といった感じでしょうか。バッチ処理などによく使われます。

## タスク定義とはなにか？
タスク定義の設定画面を覗いてみると、設定項目は5つに分かれています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/82321aa5-fc8c-44b5-99fd-2e5334e53c2f.png)

ストレージやモニタリングはオプション色が強いので、実質的にタスク定義が管轄しているのは大きく分けて２つになります。
1. インフラストラクチャの要件　⇒　クラウドリソースの設定
1. コンテナ　⇒　コンテナ設定

それぞれ見ていきます。

## 1 クラウドリソースの設定
前回までの記事では、ローカル環境でDockerfileやDokcercompose を `docker run`のようなコマンドで動かしてきました。
このときコンテナが動作するのは、『ローカルPCを対象にしてコンテナを立ち上げた』ためです。
ただAWS上ではローカルPCが存在しないので、コンピューティングリソースを用意する必要があります。
その時に使用できるサービスが、よく聞く2つです。
* EC2
* Fargate

Fargateは、EC2では自己管理する必要があったサーバー管理責任を、AWSが肩代わりしてくれるサーバーレスサービスです。特別な目的が無ければ、Fargateを使用するのが良いかと思います。

### 1.1 実際の設定画面
* 起動タイプの選択
* OSの設定
* 使用するメモリの設定
* IAM ROLE の設定

と、クラウドリソースに関する設定が続きます。コンテナコンピューティング環境の定義そのものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/20c87943-270f-43e2-99b8-0650f2919fa1.png)

### 1.2 2つのIAMRole
IAMRoleの設定箇所が2つあるのはなぜでしょうか？
* タスクロール
* タスク実行ロール

タスクロールは、起動するコンテナ（ECSタスク）に対して付与するIAMRoleです。S3アクセスやログ出力権限を付与することで、コンテナが他のAWSサービスと連携できるようになります。これは他のAWSリソースと同じなので、想像しやすいかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/abc0a858-89df-444a-b929-456c9542df50.png)

タスク実行ロールは、ECSタスクを立ち上げる際に使われる権限です。
速い話が、EcsRunTaskの権限と、その他こまごまとした権限が付与されます。起動時しか使用されないので、このRoleにS3アクセス権限を付けてもECSタスクは何もできません。
ただ、ECRからイメージをpullしたり、SecretsManagerから環境変数としてシークレットを取得したりするときには必要です。

デバッグの際は、権限がコンテナ起動前と後のどちらのものかを切り分けるようにしましょう。

## 2. コンテナの設定とは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/5788a8dd-69c1-4414-a1eb-e0f468c62cb4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/a8594f5d-992a-41f0-a458-edd455e005ce.png)

タスク定義の設定を追っていくと、「コンテナ - 1」のような設定項目が出てきます。
これは立ち上がるコンテナの設定です。
ローカルでの実行に照らし合わせると、`docker run`や`docker compose`実行時に設定できることを、ここで設定することになります。

例えば以下のような設定です。
* `docker rum --name ***`や、docker compose内でコンテナに名前を付けたりしていたのと同様、コンテナに名前を付けられる
* `docker run` コマンド時に設定するポートマッピング
* ビルド時に設定した環境変数の上書き
* docker-compose で設定していた、コンテナの立ち上がる順序の制御

さらに下の方に行くと、「コンテナを追加」という設定項目もあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/ec8bd791-5346-42be-891e-fc28fba13228.png)

docker compose では複数のコンテナを一括で管理していましたが、タスク定義ではここで複数のコンテナを定義します。

大雑把に言ってしまえば、タスク定義とは「docker compose」と同じようなものだと理解すればよさそうです。

## 3. ネットワーク設定
ネットワーク設定について大事なことは1つです。

**コンテナが立ち上がるECSと、イメージを保存するECRは全く別のネットワーク上に存在する別のサービスである。**

まずはこれを頭に入れましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/9aa89560-ec61-4356-9c28-0a95300c13cf.png)

そして別のネットワークに存在するということは、
* VPC外へアクセスできる設定が必要
* ACLで通信の制御が必要
* ECRにアクセスするIAM権限が必要

など、そもそも通信が正常にできることと、別サービスであるECRにアクセスする権限（これはベーシックなタスク実行ロールを付与していれば問題ない）ことが必要になります。
よくあるミスとしては、
* サブネットがプライベートで、IGWに接続されていない
* ACLでアウトバウンドが拒否されている
* ACLでインバウンドの32768~65535 が拒否されている

などがあります。
特に、ECRからの戻り通信に対して動的ポートを解放していないと通信が成立しないようです。
もちろん必要なセキュリティに合わせてカスタマイズする必要はあるので、通信ができないからとバカバカ開けないように注意は必要です。

## ネットワーク構成の例
### 検証用、とりあえず起動したい場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/0441b3e4-2a61-46f1-aba3-73b13aeb4c7f.png)

この設定にしておけば、ひとまずコンテナを起動できます。
プライベートサブネットにコンテナを配置すると、デフォルトではIGWに接続できないので別途設定が必要になりますが、パブリックサブネットに配置しておけばその必要もありません。

また、コンテナ内部のプログラムが外部と通信をする際には、IPアドレスが必要になります。ECSTaskの起動時に「Enable IP」みたいな設定がありますが、その設定をONにしましょう。
（注）この構成では、外部からアクセスし放題になってしまうので、重要なデータをコンテナ内に保存したり、同一VPC内に対して踏み台サーバーとして利用できる場合などは、避けるべき構成です。

### プライベートサブネット＋NATGW
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/67517620-9e11-47e7-a14f-f16d00206561.png)
よくある構成かと思います。
NATGWを使用することでコンテナを外部に露出させず、かつIP通信で外部ネットワークとも通信できます。
インバウンドも最低限必要に絞っています。80, 443はWeb通信で、32768~65535はLinuxにおける動的ポートです。動的ポートを開けておかないと、ECRとの通信における戻り通信が受信できないのだとか。
（注）NATGWは定額課金＋通信料課金なので、月々7000円~ の金額がかかります。

### プライベートサブネット＋VPCEndpoint
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2618997/614ddbb0-8842-4d35-b2e3-452f9c8bbed7.png)
外部ネットワークに接続しないのであれば、VPCEndpointを使用して直接ECRに接続するのが良さそうです。
この設定であれば、NATGWよりも安上がりで、かつ外部とは完全に隔絶されたネットワーク上で処理ができるのでセキュリティ上も堅牢です。


# 終わりに

だいぶ時間が空いてしまいましたが、Dockerコンテナグレートジャーニーと名付けて始めた本シリーズもひとまず完結です。最初から読んでくださった方は本当にお疲れ様でした。「Dockerコンテナとはなんぞや？」という疑問が解決し、根底からの理解が進み、さらに実際に運用できるレベルまで来られているのであれば幸いです。

筆者自身も勉強しながら記事を書いていたのですが、Dockerは各種開発環境のスタンダードとなっていることもあり、改めて学んでよかったと思っています。仮想化の技術はいろいろな場所に広がっており、応用範囲がとても広いです。
ですので他の記事をまだ読まれていない方は、是非ともご覧いただき、その概要を掴んでみてください。

それでは、よいコンテナライフを！