Amazon EC2 Container Service(ECS)の概念整理

## 概念図

とりあえずECSに出てくるエンティティがそれぞれどんな多重度で関連しているのかをまとめてみました。ここからはそれぞれのエンティがどんな概念なのかを解きほぐしていきたいと思います。

図1 概念図
![EC2 Container Serviceの概念図](https://cacoo.com/diagrams/ucmqfEjJP08qqHkz-D4B8C.png)


## Serviceが中心

ECSは平たく言うと

* クラスター（＝複数EC2インスタンスの集合）の上で
* Dockerコンテナを使って、
* Serviceを動作させる

ものです。

図2 例えばの構成
![例](https://cacoo.com/diagrams/ucmqfEjJP08qqHkz-7DE8E.png)

上図は、

1. Front **Service**  
  (裏にいるAPIをCallしてWEB UIを提供するもの）
2. API **Service**  
  （ビジネスロジック、DBへの読み書きをRESTful APIで提供するもの）

と言う２つの**Service** で構成されるWEBアプリケーションの例です。

ECSで言うServiceは、Serviceは利用者から見た「サービス」よりも一段階か二段階細かいもので、APIサーバーとか、フロントサーバーとかそういった単位の単機能なものです。ロールとかそういう言い方が近いのかもしれません。
（作り方によってはサービス＝Serviceにもできるのですが、ECSのスケールアウトが容易であるという特性を使うためには先に述べたような単機能な単位になってくるかと思われます。）

### Serviceってなんなの？

最初の時点では非常に説明しづらい概念なのですが、以下の情報を管理するものです。

| 管理する情報           |指定する方法  |
|---------------------|---|
|どんなコンテナを起動するのか|どの**Task Definition**を使うかを指定する|
|何個起動するのか         |**Number of Tasks**として常時起動しておきたい個数を指定する|
|リクエスト分散するのか     |コンテナとELBの紐付けを指定する|
### Serviceの実体はDockerコンテナである

さて、実行中のServiceというのは複数個のDockerコンテナの集合体です。
上の図2の例では

* Front **Service**は2種類のコンテナから成り立つ
    * expressのアプリケーション のコンテナ
    * nginx(expressにプロキシする） のコンテナ
* API **Service**は１種類のコンテナから成り立つ
    * Djangoを使ったAPI実装 のコンテナ

という構造を表しています。（今回の例ではDBはRDSを利用するものとして、ECSの外の要素としています）

### Dockerコンテナを束ねるのがTask

図2の例では計3種類のコンテナが動作している図ですが、３種類それぞれがバラバラに管理されている訳ではありません。
**Task**と言う単位で２つのグルーピングがされています。

1. Front **Task**
    * express コンテナ
    * nginx　コンテナ
2. API **Task**
    * Django コンテナ

### Taskは複数個並列で動く

さて、図2は、

1. Front **Service** の中で Frontの**Task**が3個 起動している
2. API **Service** の中で 、APIの**Task**が4個 起動している

と言う状態を示しています。

図2のような構成を物理で組んだ場合には当然ながら対障害性、負荷分散を目的として冗長化をするかと思います。

このような冗長化をする場合、ELBを使って分散する事になるかと思いますが、ECSでも同様にELBを使用します。
ECSではServiceに一つELBを指定します。
ELBの作成自体は事前に済ませておき、既存のELBを指定する感じになります。
（ELBを使わないNo ELBも選択可能です。その場合はDNSラウンドロビンなどの方法を取ることになるでしょう）

### Taskの定義はTask Definition（ってそんまんま）

オブジェクト指向の喩えでタスクをインスタンス（オブジェクト）とするとTask Definitionはクラスになります。
Task DefinitionはほぼほぼDockerコンテナの起動方法を指定するもので、[Docker Composeのcompose.ymlファイル](http://qiita.com/y_hokkey/items/d51e69c6ff4015e85fce)とだいたい同じです。

Task DefinitionはJSON形式で記述する設定ファイルで、書き方についてはとりあえずこちらを見てねという形でお茶を濁しておきます。
http://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html

### ということでServiceの作り方

まずServiceはTask Definitionを元にして作ります。
![Screenshot from 2015-10-13 23:11:14.png](https://qiita-image-store.s3.amazonaws.com/0/31010/42ac7981-3419-a033-45a3-b63b80f9a2ef.png)


![createservice.png](https://qiita-image-store.s3.amazonaws.com/0/31010/71cda919-bdbe-15f7-01fa-a1b78dae8180.png)

|項目| なんとなく説明   |
|---|----------------|
|Task Definition|Serviceの元となるTask DefinitionでTask Definitionは事前に作成されている必要があります|
|Cluster|Serviceを動作させるクラスタを指定します（後述）|
|Service name|Serviceの識別子で適当にわかりやすい名前をつけてください|
|Number of tasks|このService上でTaskをいくつ動かすかを定義します。あとから変更可能です。|
|Load Balancer|前述の通り負荷分散するためのELBの名前と使うポート番号を指定します。|

## クラスター

クラスターはContainer Instanceの集合体で、要するにEC2インスタンスの集合体です。
クラスター内のそれぞれのインスタンスはただのリソース量（CPU,メモリ,ポートを供給するリソース量）として捉えられ、個々はあまり重要ではありません。

* 足りなくなればインスタンスを足せリソース量が増える
* 状況に対してリソース量が過剰であればインスタンスを減らす

というもので、**どのインスタンス上で何のタスクを動かすかはECSの采配**で自動的に決まります。

### Container Instance

Container InstanceとなるEC2インスタンスは、

* Docker入り
* Amazon ECS Container Agent 入り

である必要がありますが、通常はECS最適化されたAMIからインスタンスを作るので特に気にする必要はないでしょう。
2017/11時点での最新のAMIは` amzn-ami-2017.09.b-amazon-ecs-optimized `ですが、[ECS Container Agent](https://github.com/aws/amazon-ecs-agent)のアップデートとともにAMIもアップデートされていくので下記の公式ページを時々チェックすると良いでしょう。

http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html

また、Container Instanceを構成するためには以下も必要になります。

* インスタンスにアタッチするIAMロール
* ECSクラスタの名前（インスタンスのuser-dataに以下のように設定する）

```shell-session:user-data
#!/bin/bash
echo ECS_CLUSTER=MyCluster>> /etc/ecs/ecs.config
```


See: 

* http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ECS_instances.html
* http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/launch_container_instance.html

### 普通はAuto Scalingグループを使う

上記のようなEC2インスタンスをポコポコ作っていけばクラスタのメンバーを増やしていく事ができますが、結構面倒です。普通はAuto Scalingグループを使うことになるでしょう。
ECSの管理画面を初めて起動した時に出てくるチュートリアルを実施すると上記のような起動設定のAuto Scalingグループが作られるので、その起動設定をコピーしてAuto Scalingグループを作ってインスタンス管理をするのが良いでしょう。

## クラスタの上でService、タスクがどう動くのか

図3:クラスターの上でタスクが動いているの図
![](https://cacoo.com/diagrams/ucmqfEjJP08qqHkz-272C2.png)

* Frontサービスはタスクを2つ起動
* APIサービスはタスクを3つ起動

**上図3-AはHogeクラスター上でFront、APIの両サービスを稼働**させている例です。
お財布都合でインスタンス数を抑えたい時にはこのようなクラスタ構成になるでしょう。
ただし状況によっては逆にリソースが有効に使われていないインスタンスがあるかもしれません。（図3-パターンAの003の例）

**図3-Bはfront用とapi用でクラスターを分けている**例です。
ピーク特性などが違う場合にはこちらのパターンのようにクラスタを分けて構成したほうがインスタンス数の調整はし易いでしょう。この例では1インスタンスに一つのTaskとなることでそれぞれのインスタンスタイプは下げる事ができて、結果的にお財布に優しくなるかもしれません。

さて、上図3(A,Bとも）ではfrontのタスクが別インスタンスに分散されていますが、CPU,メモリといっったリソースの状況が許せば一つのインスタンス上で複数タスクが動作するという事も有り得ます。

ECSが出た頃の2015年当時はELBと組み合わせるために`タスクのポート`を`ホストのポート`にマッピングする必要があり、上記の構成ではホストの80番ポートの衝突をするという理由でfrontタスクが同じインスタンス上で複数動作することはかないませんでしたが、2016年に登場したALBによってこの縛りはなくなりました。ALBと動的ポートを使ったロードバランスについてはこちらを参照してください。

* https://qiita.com/taishin/items/eb759a8ec0c583fc5ebd

## ちょっと嘘つきました

> ECSは平たく言うと
>
>* Dockerコンテナを使って、
>* クラスターの上で
>* Serviceを動作させる
>
>ものです。

実はクラスターの上ではServiceだけではなくて、タスクを直接動作させることもできます。
バッチジョブなどがこれに該当するでしょう。

----

Google Kubernetes Engine(GKE)も大体似たような概念になっています。
余力があれば別途GKEについても書きたいと思います。