ECSのクラスター、タスク、サービスの役割を確認しながらWEBサーバーを構築する

## はじめに
### 本記事を書くきっかけ
最近コンテナを学び始めました。

Docker Desktopや物理サーバーでコンテナを動かすことをしたあと、AWSのECS（Elastic Container Service）に挑戦したのですが、どこにどのような設定を入れるのか少し混乱しました。

ECSの仕組みや構成、設定項目（クラスターの設定、タスク定義、サービスの設定、コンテナの定義）の役割を理解しようと考え、本記事を書くことにしました。

### 自分のレベル
コンテナ初心者です。

本記事の対象読者はECSをこれから使う方で、コンテナの基礎的な知識は持っている方を想定しています。

### 今回やること（構成図）
ECSではどのようにコンテナを展開するのか、設定項目の役割を確認しながら単純なWEBサーバーを構築します。

<font color="red">__詳細な手順は記載しません。__</font>あくまでもECSの設定項目が果たす役割を確認することが目的です。

AWS Fargateではなく、ホストはEC2を使用します。また、ALBは使用しません。

完成形の構成図としては下のようになります。後半でも記載しています。

マルチAZ構成のパブリックサブネットに、1台ずつホスト（EC2）を展開し、その中にコンテナを一つずつ動かします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/94020bc0-1b77-2e4f-1086-7dea74dcb2d9.png)


## コンテナの基本
本題に入る前に、コンテナの基本的な構造をおさらいしたいと思います。

### 物理構成
ECSではなく、コンテナを物理環境で動かす場合、以下のパターンが考えられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/e677a5a3-1978-c665-8a05-dfd23873abec.png)
それぞれの物理構成について説明します。

### 物理サーバー/物理サーバー＆仮想環境
「物理サーバー」と「物理サーバー＆仮想環境」の違いは、仮想環境の有無のみです。

どちらにおいても、Linuxをインストールしてその中にDocker Engineをインストールします。そしてDocker Engineの上でコンテナが動作します。

### Docker Desktop
Docker社が提供するDocker Desktopを使用すれば、ノートPCなどでコンテナを動かすことができます。

MacでもWindowsでも、Docker Engineを動かすLinuxはパッケージ（インストーラー）から提供されます。

Windowsでは仮想環境にHyper-V（Microsoftが提供）を使用しますが、Macでは仮想環境（HyperKitと呼ばれるもの）もパッケージから提供されます。

### ECSでは物理環境をほぼ意識する必要がない
ECSにおいては、コンテナサービスをAWSがフルマネージドで提供しているので、物理構成を意識することはほぼありません。そのためとっつきにくくなっていると感じます。

## やってみる
ではECSでのコンテナ構築を行っていきます。ですがコンテナイメージが無ければ話にならないので、ECSの前にまずECR（Elastic Container Registry）にイメージをプッシュすることから始めます。

### 事前準備（ECRへのコンテナ登録）
ここでは、[httpd](https://hub.docker.com/_/httpd)のコンテナイメージを使用します。

AWSコンソールで、ECRにリポジトリを作成します。”httpd”という名前にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/2ffecfa7-1ad0-d133-8804-e1aad7857efd.png)
コンテナイメージのPullとPushはクライアントPC（手元のPC）での作業です。

httpdのコンテナイメージをPullします。
```terminal
### httpdコンテナイメージを手元のPCにダウンロード
PS > docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
a603fa5e3b41: Pull complete
4691bd33efec: Pull complete
ff7b0b8c417a: Pull complete
9df1012343c7: Pull complete
b1c114085b25: Pull complete
Digest: sha256:f2e89def4c032b02c83e162c1819ccfcbd4ea6bdbc5ff784bbc68cba940a9046
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest
PS > 
### Pullしたイメージの確認
PS > docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
httpd        latest    8653efc8c72d   2 weeks ago   145MB
```
コンテナイメージにタグを付けてECRへPushします。
```terminal
### AWSクレデンシャル設定
PS > $env:AWS_PROFILE="プロファイル名"
PS >
### ECRレジストリ認証
PS > aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin [AWSアカウントID].dkr.ecr.us-east-1.amazonaws.com
Login Succeeded
PS > 
### コンテナイメージにタグ付け
PS > docker tag [コンテナイメージID] [AWSアカウントID].dkr.ecr.us-east-1.amazonaws.com/httpd:latest
PS > 
### ECRにプッシュ
PS >  docker push [AWSアカウントID].dkr.ecr.us-east-1.amazonaws.com/httpd:latest
The push refers to repository [[AWSアカウントID].dkr.ecr.us-east-1.amazonaws.com/httpd]
a4a57da7ddfc: Pushed
a0b242781abd: Pushed
29657939e55a: Pushed
7603afd8f9aa: Pushed
ec4a38999118: Pushed
latest: digest: sha256:f2f8e87c61c78701277b59757b6e43d531571e4426b512f5248729811508d2de size: 1366
```

ECRにアップロードされたことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/3e1049f3-59bb-b9ab-88fb-4968b7764a55.png)
それではECSの設定項目について見ていきながら、コンテナ環境を構築していきます。

### クラスターの設定
クラスターは<font color="red">__タスクとサービスの集まり__</font>です。
※タスクとサービスについては後述しますが、簡単に言うと<font color="red">タスクはコンテナをインスタンス化する</font>もので、<font color="red">サービスはコンテナの数を一定に保つもの</font>です。

クラスターの設定では、コンテナを動かすホスト（EC2）の設定と、ネットワーク（VPCやサブネット）の設定を行います。前述した物理構成で言えば、「物理マシン」〜「Linux」の設定といえます。

Fargateの場合はホストはAWSによって管理されるため、ネットワークの設定のみとなります。

下はクラスターを作成開始するときの初期画面です。FargateにするかEC2を使用するか、選択を促されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/b9fca4fe-021d-ac55-d0a3-4c4a603712e4.png)
ホストがEC2の場合で話を進めます。クラスターの設定での主な項目は以下になります。

* インスタンスの設定
    * インスタンスタイプ
    * インスタンス数
    * AMI
* ネットワークの設定
    * VPC
    * サブネット
    * セキュリティグループ
* コンテナインスタンスの設定
    * コンテナインスタンスのIAMロール

下はクラスターの作成が完了した画面です。クラスターリソースの箇所に、作成されたリソースが表示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/1f0e132b-a5aa-fefe-a80b-fcc55488637d.png)
各AZのパブリックサブネットを選択し、インスタンス数は”2″としました。これでマルチAZ構成になります。

補足ですが、ECSのクラスターの作成によって、EC2の”起動設定”と”Auto Scalingグループ”も作成されます。”起動設定”と”Auto Scalingグループ”により、ホストのEC2インスタンスが起動することになります。

クラスターだけ作成が完了すると、構成図は概ね以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/e7a7d39f-90fe-a8c5-726f-12219b54ffdd.png)

### タスク定義
タスク定義は一つ以上の<font color="red">__コンテナを定義する設定項目__</font>です。タスクからコンテナが実行されます。

タスク定義で設定する主な項目は以下です。

* タスクロール
* ネットワークモード
* タスクサイズ
* コンテナの定義

タスクロールはコンテナインスタンスに割り当てるIAMロールです。

ネットワークモードは”bridge”、”ホスト”、”awsvpc”から選びます。ここではポートマッピングを使用しますが、bridgeでのみ実現可能です。
※ネットワークモードの詳細については[こちら](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/bestpracticesguide/networking-networkmode.html)へ。

タスクサイズはタスクに割り当てるメモリとvCPUのハードリミットです。ホストのメモリを超えないように注意します。

コンテナの定義はその名の通りコンテナの設定で、どのイメージを使うかや、ポートマッピングの設定などを行います。

タスク定義を作成して、クラスターに割り当てることで、コンテナが実行されます。

下はタスク定義を作成して、クラスターに登録した画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/1af16d91-c9b4-e537-ca88-628e6d582aa6.png)
タスク定義に登録したコンテナは１つです。タスク登録のときに「配置テンプレート」という項目を”AZバランススプレッド”（複数のAZまたは複数のインスタンスにタスクを分散配置）に設定したので、各AZにコンテナが1台ずつ動いています。

ここまでで動作確認をします。タスクの定義画面からコンテナの詳細画面を見ると、”外部リンク”という項目があります。ここに「[EC2インスタンスのパブリックIP]:8080」のリンクがあるのでクリックします。（ポートマッピングしているので「〜:8080」となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/152828f7-2133-27f6-ab0a-00ae0e368939.png)
コンテナのWEBサーバーにアクセスできて、コンテナ内のindex.htmlの内容が返されます。（デフォルトでは「It works!」）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/3235ae87-6e20-5a72-6c06-8c2356cdd66c.png)

ここまでの状態を構成図に書くと、概ね以下のようになると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/a1cb27a9-277c-07db-1a0c-d46be8846308.png)
後述しますが、サービスを使用せずタスクのみでコンテナを起動すると、コンテナが障害などで停止してしまった場合、<font color="red">__停止したままとなります__</font>。

コンテナの実行数を一定に保つための、サービスの定義を見ていきたいと思います。

### サービスの定義
タスク定義の登録だけでコンテナを動かすことはできましたが、<font color="red">__コンテナ数を一定に保つためにはサービスの定義が必要__</font>です。

たとえば、コンテナの実行数を”2″と定義しておけば、障害でひとつのコンテナがダウンしてコンテナ実行数が”1″になっても、自動的に新しいコンテナを起動されコンテナ実行数が”2″に戻ります。

下はクラスターにサービスの定義を作った画面となります。タスクの数（必要なタスク）は”2″としました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/aa80231e-43c0-9d13-762d-b452091115d0.png)
タスクを確認してみると、2つのタスクが起動しています。起動元がecs-svs（ECSサービス）となっており、サービスからタスクが起動されたことがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/d53fd811-7726-4213-8813-3ed7adf12081.png)
ここで片方のタスクを停止してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/2efd4815-bc15-c02f-6448-92a257e0928d.png)
すぐに新しいタスクが起動しました。”2″個のコンテナ実行数が保たれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/584e3924-0711-3b90-aac8-a8121b140d39.png)
構成図は以下となります。サービスがタスクを管理するイメージとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/4bf5de45-dd41-85f8-e935-135724fc8481.png)


## 参考サイト
[ネットワークモードの選択](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/bestpracticesguide/networking-networkmode.html)
[Amazon ECS コンポーネント](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/welcome-features.html)
[Docker イメージをプッシュする](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/docker-push-ecr-image.html)