AWSのコンテナサービスECSをALBで分散処理する on Fargate

## はじめに
AWSエコシステムの中で、コンテナを使ったシステム開発・構築をするための全部入りです。
KubernetesベースなEKSもありますが、執筆時点では東京リージョンで利用不可なことと、Kubernetesを使うのであればGKEのほうがいいと思いますので本記事では割愛します。

## 何を作るのか
さて、この記事の目標ですが、

- AZ safetyで
- http to httpsで
- 独自ドメインな

Webサービスを、なるべくAWS内で完結するように構築します。
また、AWS management consoleの画面の行ったり来たりを極力減らしたいと思います。
本記事では、セキュリティグループとネットワーク構成については触れませんので、別途ご用意ください。

## 大まかな構成について
![alb-ecs-fargate.png](https://qiita-image-store.s3.amazonaws.com/0/143534/9a9b598e-ae45-d8dd-e0dd-9a6844f38533.png)

おおまかにはApplication load balancerからのトランザクションをデプロイされているコンテナ群に振り分けるような構成で、このコンテナ群はFargateというマネージドな仮想リソース上で動かします。

## だいたいの料金について
今回利用するAWSのサービスの概要と、東京リージョンでの利用にどのくらいお金がかかるのかについて、簡単にまとめておきます。しかし、AWSの料金についてはリージョンや利用される状況によって非常に計算式が複雑ですので、各子セクションには公式へのリンクを貼っておきます。

### Route53
参考: https://aws.amazon.com/jp/route53/pricing/

DNSサービスです。他の業者に比べるとほーんの少しだけ高いですが、ドメインの取得なんかもここでできます。
料金は設定に対して係るものと、クエリに対して係るものがあります。設定に対しては最初の25個までは\$0.50で、クエリに対しては100万クエリにで\$0.4です。通常DNSクエリのリクエスト数はアクセス数よりは少なくなりますので想定するアクセス数の5掛けくらいで計算しておけばよかろうと思います。__月間100万アクセスくらいならば月に80円くらい__の試算となります。

### Certificate Manager
参考: https://aws.amazon.com/jp/certificate-manager/pricing/

SSL証明書を発行してくれるサービスです。外向けの証明書発行は__無料__です。特段の理由がない限りSSL通信にしておくべきです。

### Application load balancer
参考: https://aws.amazon.com/jp/elasticloadbalancing/pricing/

Elastic load balancingというロードバランササービス群があり、今回は、その中のApplication load balancer(ALB)を利用します。
基本料金は\$0.0243/hなので、ずーっとつけっぱなしだと、だいたい__1日65円くらい、月で2000円くらい__ですね。
従量料金は計算の仕方が難しいのですが、ざっくりと説明すると次の4つの指標を計算します。

- 1秒あたりの新たに確立された接続の数
- 1分あたりのアクティブな接続の数
- 処理されたトラフィック量
- 処理されたルールの数とリクエストレートの積

これらを単位あたりの数に変換したものを1LCUとして、4つのLCU値のうちもっとも大きい値に対して課金されます。(東京リージョンで\$0.008/LCU)

### ECS
参考: https://aws.amazon.com/jp/vpc/pricing/

コンテナ関連のマネージドサービスで、コンテナレジストリもここに含まれます。課金は、AWSの同一リージョン内で利用するのであれば、Elastic Container Registory(ECR)の容量分で1GBあたり月額\$0.1です。たいしたことないように思うかもしれませんが、結構ガツガツpushしてると意外と金食い虫の原因になるやつです。

### Fargate
参考: https://aws.amazon.com/jp/fargate/pricing/

コンテナが実際に動くリソースを仮想してくれているサービスです。Fargateを利用することでEC2を複数立ち上げてコンテナが動くクラスタを構築する必要がなくなります。
各コンテナに対してCPUリソース(vCPUという単位)と、メモリリソースを割り当て、コンテナで消費するリソースに対して秒単位で課金されます。vCPUあたりの料金は1時間あたり\$0.0632で、メモリは1GBで1時間あたり\$0.0158ですので、__0.25vcpuで1GBのコンテナを4つ、1ヶ月動かしっぱなしで1万円くらい__ですね。
当然のことながら、最初は最小構成で構築してスケールアップもしくはスケールアウトしつつ調整していくのが良いです。


## アプリケーションの開発
さて、おおまかな構成や料金については目を通しましたので、今回デプロイするアプリケーションを開発しましょう。

### Node.js + Express
とりあえず簡単に作るのであればNode.js+ExpressかGo+GinかPython+Flaskがいいと思っています。今回はNode.js+Expressで作ってみます。

#### パッケージを追加する
まずは、package.jsonとして、

```json:ws/package.json
{
  "private": true,
}
```
を用意します。外部パッケージはexpressとmorgan(loggerです)を導入します。

```shell-session
hoge:ws$ npm install --save express morgan
```
このコマンドで更新されたpackage.jsonはコンテナに封じ込めますので大事にとっておきます。

#### コードを書いてみる
次にコードを書いてみましょう。今回はリクエストを受け取ったら、単に`{ msg : 'hello world' }`をjsonにして返すだけのアプリを作ります。また、http宛へのアクセスをhttpsにリダイレクトする機能も同梱してしまいましょう。
ここでは、アプリ側にはポート3000を、リダイレクト側にはポート3001を割り当てています。

```js:ws/server.js
const express = require('express');
const morgan = require('morgan');

const app = express();
app.use(morgan('combined'));
app.get(
    '*',
    (_, response) => {
        response.setHeader('Content-Type', 'application/json');
        response.send(JSON.stringify({ msg: 'hello world' }));
    }
);
app.listen(
    3000,
    () => { console.log('start application server.') }
);

const http2https = express();
http2https.get(
    '*',
    (request, response) => {
        response.redirect(301, `https://${request.hostname}${request.url}`);
    }
);

http2https.listen(
    3001,
    () => { console.log('start redirect server.'); }
);
```

これでアプリケーションは完成です。普通に、

```shell-session
hoge:ws$ node ./server.js
```

で動きます。

### Dockerfileの作成
さて、こいつらをコンテナに封じ込めるために`Dockerfile`を用意しましょう

```dockerfile:ws/Dockerfile
FROM node:alpine
WORKDIR /tmp
ADD package.json ./
ADD server.js ./
RUN npm install

EXPOSE 3000
EXPOSE 3001

CMD ["node", "./server.js"]
```

ビルド時に`package.json`と`server.js`をつっこんで、パッケージのインストールをするという内容です。ポートの3000と3001を露出してあります。

## AWS側の設定
ここからはAWSの設定です。大まかな作業の流れですが、

1. IAMの設定
    - ECRへのリポジトリへのpush権限を作る
    - ECSでのタスク実行権限を作る  
1. ECRの設定
    - Registoryの作成
    - イメージ作成とプッシュ
1. Route53の設定
    - ドメインの購入
1. ACMの設定
    - 証明書の作成
1. ALBの設定
    - ターゲットグループの設定
    - ALBの設定
1. ECSの設定
    - Task definitionの作成
    - Clusterの作成
    - Serviceの作成
1. Route53の設定(再)

という感じになります。

### IAMの設定
みんな大好きIAMの設定です。読みは「アイアム」らしいです。ここでは、リポジトリへのプッシュ権限を作成しaws cliの設定を行うのと、ECSでタスクを実行するためのロールの作成を行います。

#### リポジトリへプッシュするためのグループを作る
IAM運用においてユーザーを作ったほうがいいのか、グループを作ったほうがいいのかは、ケースバイケースです。私はユーザーは実際の人単位や、Travis CIやGithubというような外部サービス単位ではユーザーを作り、今回のようなケースではデプロイ用のグループを作成し、このグループに必要なユーザーを追加していくのが好みです。

ということで、まずグループを作ります。ServicesからIAMを探し出して、IAM Management Consoleを開きます。左カラムにあるGroupsを選び、Create New Groupをクリックしグループ作成画面を表示します。
Step 1ではグループ名を入力します。特段これといった命名ルールがあるわけではないのですが、グループ名は複数形のほうが気持ち良いので、今回はDeployersとします。
Step 2ではポリシーを追加します。今回はとりあえずECRにイメージのpushができればよいので、AmazonEC2ContainerRegistryPowerUserを追加しておきましょう。
グループが作成できたら、今回作ったDeployersにpushするIAMユーザーアカウントを追加しましょう。今回は、自分のアカウントをDeployersに追加します。

#### ecsTaskExecutionRoleの追加
マネージドサービスのための権限管理のためにロールという概念が存在します。ECSには各タスクのロールとタスクを実行するためのロールの2つのロールがアタッチできます。今回はタスクから、その他のAWSリソースには触りにいかないため前者は設定せず、タスクを実行するためのロールだけを追加しておきましょう。

Create roleをクリックすると、どのマネージドサービスのためのロールなのかを選ばされますので、Elastic Container Service > Elastic Container Service Taskと選択しましょう。
ポリシーは`AmazonECSTaskExecutionRolePolicy`をアタッチしておきましょう。これをアタッチしておけばCloudWatch logsへの書き込みや、ECRからのプル権限が付きます。
最後にRole nameをきめることとなりますが、タスクロールと違って、タスクを実行するためのロールは1つだけがあればあまり問題ないはずですので、`ecsTaskExecutionRole`という一般的な名前をつけておきましょう。

参考: https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html

### AWS CLIのインストールと初期設定
次にAWS CLIをインストール & 設定します。いろいろなことに使えるCLIですが、今回はECRへのpushに使います。

#### インストール
AWS CLIはpythonベースですので、`pip install awscli`でできます。

参考: https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/installing.html

#### 初期設定
設定には、IAMユーザーに払い出される`Access key ID`と`Secret access key`が必要ですので、まずはこの両方を作成します。

Usersから先程Deployersに追加したユーザー(この記事どおり進めていれば自分)の画面を表示し、Security Credentialタブから、Create access keyボタンを押すと両IDが作成できます。Secret access keyはマスクされていますがshowボタンをクリックすると表示されますが、この瞬間にしか表示されないので気をつけてください。

両IDを表示したまま、シェル上で、

```shell-session
hoge:~$ aws configure
```
もしくは、

```shell-session
hoge:~$ aws configure --profile your-alias
```

を実行してください。

前者はAWS CLIのデフォルトプロファイルとして設定され、後者は名前付きのプロファイルとして設定されます。複数のアカウントを使い分けないのであれば前者でいいのですが、おそらく複数扱うケースのほうが多いと思いますのでよくわからなければ後者のほうが良いと思います。本記事でも特段断らなければ、アカウントエイリアスで名前付きプロファイルを設定した前提でコマンドなどは記載していきます。

さて話を戻して、先のコマンドを実行すると、対話的にAccess key IDとSecret access keyが求められますので入力してあげます。また、Default region nameとDefault output formatの設定を求められますが、こちらは入力してもしなくても構いませんが、ひとまず東京リージョンである`ap-northeast-1`と`json`を設定しておきます。

これでAWS CLIの設定は完了です。

### ECRの設定
次はコンテナレジストリの設定です。このセクションではレジストリを作成し、先程のDockerfileをベースに作成したイメージをレジストリにプッシュしてみます。

#### レジストリの作成
まずはコンテナレジストリの作成です。

ECRはECSのサブサービスですので、ServicesからElastic Container Serviceを選んだら、画面左カラムのRepositoriesをクリックしましょう。リージョンがきちんとTokyoになっているか確認し、Get Startedを押します。

Step1では要求されるRepository nameですが、エイリアス名を名前空間にしてyour-alias/sample-appという名前にしておきましょう。画面を進めていけば、これだけでリポジトリは作成完了です。

#### リポジトリにイメージをプッシュする
作成が終わると、コンテナのビルドからプッシュまでの方法が表示されますので、そこに書いてあるとおりに作業しましょう。
最初の`aws ecr get-login --no-include-email`は実行して出力された文字列を、コマンドとして実行するのが必要がありますので注意してください。↓では、`aws ecr get-login --no-include-email`自体をバッククオートで囲って直接実行しています。

```shell-session
hoge:ws$ `aws ecr get-login --no-include-email --profile your-alias`
hoge:ws$ docker build -t your-alias/sample-app .
hoge:ws$ docker tag your-alias/sample-app:latest xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/your-alias/sample-app:latest
hoge:ws$ docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/your-alias/sample-app:latest
```

your-alias/sample-appのImagesにきちんとプッシュされてるのを確認してください。もし、うまくいってなければAWS CLIのはいているエラーをよく読み、トラブルシュートしてください。

参考: https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/common-errors-docker.html

### Route53でドメインを買う
実はRoute53でのドメイン購入は若干(ものによっては相当)割高なのですが、Route53で購入したドメインは勝手にHosted zoneにも登録されますので、設定がめちゃくちゃ楽ですのでおすすめです。では、Route53でドメインを買ってみます。

ServicesからRoute53を選択肢、左カラムのRegistered domainsをクリックします。Register domainから自分の好きなドメインを買いましょう。購入についての設定項目がいくつかありますが、ここでは割愛しますので、詳細は公式を参照してください。
購入が完了すると、しばらくはPending requestの状態で、しばらくするとRegistered domainsとなり使えるようになります。

参考: https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/domain-register-values-specify.html

### ACMでSSL証明書を作成する
次にSSL証明書を発行します。Servicesから、Certificate Managerを開きましょう。外向けの証明書発行ですので、Provision certificatesの方をGet startedしましょう。
Step 1ではDomain nameに対象のドメインを入力する必要があります。プロコンありますが、私は`*.your-own-domain.com`というような感じで、ワイルドカードで指定することが多いです。
Step 2では、本当にStep 1で入力したドメインの所有者かを確かめる方法を指定します。本記事でのようにRoute53でドメインを買ってるならばDNSで行うほうが圧倒的に楽ですので、DNS validationを選択肢します。
Step 4では、実際に所有権を確かめるフェーズが走ります。Domainのボックス内の▶をクリックしてCreate record in Route53するだけで、必要なDNSレコードが対象のHosted zoneに追加されます。
しばらく待っていると、証明書が作成されますので、ACMはこれで完了です。

### ALB・ECSの設定の前に概念の簡単な説明
ここまででコンピューティング以外の部分は、ほぼ設定完了です。ここから、肝心のロードバランシングやコンテナ部分について設定していく前に、この周辺の設定に出てくる概念について、少しだけ整理しておきます。私見ですが、各サービスや概念が、なんの設定を保持するためのものなのかを意識すると理解しやすいと思います。

#### ALB
ALBは一番外側からのリクエストを受けとって、アクセスをうけたポートごとにTarget groupという単位にトランザクションを流すものです。ALBが保持する設定は、

- エンドポイントのURL
- ポートとTarget groupへのマッピング

です。

#### Target Group
ターゲットグループはコンテナ群をぶらさげておき、ALBから流れてきたトランザクションをいい感じに分散してコンテナに流すものです。Target Groupが保持する設定は、

- ぶらさがってるコンテナがいきてるかどうかの確認方法
- どのコンテナ群にひもづいているか

です。

#### Service
サービスはコンテナ群のことです。実際にコンテナを起動したり殺したりします。Serviceが保持する設定は

- ALBとTarget Groupの組
- どんなTaskをどのくらい起動するか

です。

#### TaskとTask definition
Taskは実際にRunしてるやつです。Task definitionは、そのタスクの雛形です。docker runのときのオプション+αです。

### ALB・ECSの設定の順序
これらの設定ですが、初めてやったときはめちゃくちゃ行ったり来たりで四苦八苦しました。なにしろ、あっちの設定が終わってないと、こっちの設定が完了しないみたいなのが、めちゃくちゃ多くて手戻りがエグかった覚えがあります。ですので、ややしつこいですが手順について、ここで予め議論しておきましょう。

#### パターン1: Task definition → Target group → ALB → Cluster → Service
実は、Task definitionは個々のコンテナをどうやって動かすの？という設定の単位でしかないので、ALBの設定とは独立に行うことができますので、最初にしてもかまいません。その後、Target group→ALBとロードバランサ側の設定を終わらせて、ECSに戻ってくるパターンです。

#### パターン2: Task definition → Cluster → Target group → ALB → Service
パターン1でTask definitionが終わった段階で、Clusterの設定までやってしまうパターンです。ALBを動かした時点からお金がかかりますので、試行錯誤での料金垂れ流しを後ろに固めているパターンです。

#### パターン3: Target group → ALB → Task definition → Cluster → Service
ALB側の設定を最初に固めてしまうパターンです。ルーティングのエンティティを作って、エンドポイント側につないで、サーバサイドにつないでという流れがリーズナブルに感じるので、私はこれが一番で好きです。本記事では、このパターンに沿って解説していきます。

### ALBの設定
さて、やっとALBの設定です。Target groupの設定→ALBの設定の順序で設定していきます。

#### Target groupの設定
今回は、エンドポイントになるALBからhttpとhttpsを受け取りますので、ターゲットグループは、

- httpからフォワードされて3001が露出されてるコンテナ群にアタッチされるグループ
- httpsからフォワードされて3000が露出されてるコンテナ群にアタッチされるグループ

の2つが必要です。

まずは、httpからフォワードされる方のグループを作成します。ServicesからEC2を選びます。左カラムの真ん中あたりにLOAD BALANCINGという塊があり、そのなかにTarget Groupsというのがありますので、そこを開きCreate target groupというボタンを押しましょう。
Target group nameはsample-app-http、Target typeをipにすればOKです。ヘルスチェックは、少し注意が必要で、3001に流れてきたときは200ではなく301を返すようにリダイレクト処理しましたので、ヘルスチェックのAdvanced health check settingsのSuccess codesを301にしておきましょう。
次にhttps側です。同様にCreate target groupというボタンを押し設定しましょう。Target group nameはsample-app-httpsとし、Target typeをipにして作成しましょう。

あれ？これ3000とか3001に流す設定はどこにするの？と思うかもしれませんが、実際はコンテナポートの3000や3001はServiceを通じてホストポート側には動的にマッピングされるため、Target groupにはこれらの設定をすることは不可能です。ServiceとTarget groupの紐づけはECSの設定で行います。

#### ALBの設定
続いてALBの設定です。設定自体は簡単でALBを作成済みのTarget groupに流すだけです。

LOAD BALANCINGという塊の中のTarget Groupsの上に、Load Balancersがありますので、そこからCreate Load Balancerをクリックします。一番左のApplication Load BalancerをCreateしましょう。

まずNameにはsample-app-albを設定し、Listenersの欄でadd listenerしHTTPSのListenerを追加しましょう。AZは複数のAvailability Zoneにまたがるように選んで次のページへ行きます。

httpsを追加した場合はここで証明書を選べと言われます。Certification Managerで作成したものを選択します。Security policyは要件次第ですが、私はELBSecurityPolicy-TLS-1-2-2017-01を選ぶことが多いです。それぞれの違いについては、公式を参照してみてください。

Security Groupを指定したら、最後にターゲットグループの設定です。Target groupからExisting target groupを選択し、sample-app-httpを選びましょう。最後までページを進めてCreateしてください。

このままでは、sample-app-httpsがhttpsのListenerに紐付いていませんので、今作成したALBのListenersタブからHTTPSを選択しEditを押し、表示された画面にあるDefault actionのForward toをsample-app-httpsに変更しましょう。鉛筆マークから編集画面を表示し、sample-app-httpsを選択し直し、画面左上のUpdateボタンを押せば、ALBの設定は完了です。

参考: https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/elb-security-policy-table.html

### ECSの設定
さて、あと一息です。ServicesからElastic container serviceに移動します。

#### Task Difinitionを作成する
Task Definitionsをクリックして、Create New Task DefinitionでTask Definitionの作成画面に進みましょう。FargateベースにするかEC2ベースとするか選ぶのですが、今回はタイトルにもある通りFargateで構築しますのでFargateを選びます。
Task Definition Nameが意外と悩ましいのですが、私はxxx-templateというふうに名前をつけることが多いです(いい命名ルールがあれば誰か教えて…)。今回はsample-app-templateとしておきます。
Task Roleは、先述しましたがコンテナ内から他のサービスにアクセスするためのロール設定なので、今回は特に設定する必要はありません。Task execution roleには最初に設定したIAMロールのecsTaskExecutionRoleを設定、Task sizeの設定はひとまず小さく、Task memoryを0.5GB、Task CPUを0.25vCPUで設定しておきます。

最後に、コンテナの設定をadd containerから追加しましょう。container nameはDockerのコンテナ名と同じ概念です。これは、私は本当に適当につけていて、好きなアニメのキャラクターとかの名前とかつけていたりします。
imageには先程のECRにアップしたimageのURIに`:latest`をつけた、`xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/your-alias/sample-app:latest`を指定、Port mappingは露出するコンテナ側のポートを指定しますので3000と3001を露出しておきます。
その他細かい設定もできますがオプショナルですので今回は割愛します。ページを進めてCreateすれば完了です。

#### Clusterの作成
Clusterの作成はFargate利用の場合は、名前をつけるだけですね。
左カラムからClustersを選択し、Create clusterします。Networking onlyを選び、cluster nameにはsample-app-clusterと名付けてcreateで完了です。

#### Serviceの作成
Clusterの作成完了画面のView clusterボタンをクリックすると、今作ったクラスタのページにいけますので、続けてServiceを作ってしまいます。

まずは、httpからフォワードされてくるTarget groupにぶら下げる方を作ります。Servicesタブ内のcreateボタンを押しましょう。
Launch typeをFargateにして、service nameをsample-app-http-serviceとします。Number of tasksに設定した数が実際に立ち上がりますので、この数字を調整することでスケールイン・スケールアウトできます。今回は2にしておきます。
サブネットは1aと1cをまたぐように選び、Security GroupはECRやALBからの通信が通るものを選んでおきましょう。
最後にLoadBalancingの設定です。Application Load Balancerを選択し、3001が露出されてる方のコンテナを選び、ここにAdd to Load balancderしましょう。Target group nameに、先ほど作成したsample-app-httpを選びます。あとは、ページを進めてCreateすれば終わりです。

https側の作業もほぼ同様で、作業差分はservice nameをsample-app-https-serviceにするということと、LoadBalancingの設定で3000が露出されているコンテナを選び、sample-app-httpsをアタッチすることです。

### ALBにドメインを紐付ける
最後に、ALBに今回取得・設定したドメインをひっつけましょう。
Route53のHosted zoneで対象のドメインをクリックしてCreate Record Setします。AliasをYesにして、Alias targetのフィールドをクリックするとALBのARNが表示されますので、これを選んでCreateしましょう。
証明書をワイルドカードで指定している場合は、ルートドメインに流すとアンマッチを起こすと思いますので、なにか適当なサブドメインを指定してください。

## さいごに
お疲れ様です。これで作業は完了です。`curl https://www.your-domain.com`すると`{"msg":"hello world"}`が返ってくるはずです。感動。

### アップデートについて
システムは作ってハイ終わりではなく、どんどんと更新していくことになります。ですので、コンテナのアップデートをしてみましょう。

実は、今回の記事のとおりに設定していると、コンテナでの標準出力はすべてCloudwatch Logに吐かれています。
どんなログが吐かれているのか見てみると、`ELB-HealthChecker/1.0`からのアクセスログが凄い数出力されているはずです。これは名前の通り、ALBが各コンテナが死んでないかどうかのヘルスチェックのアクセスです。こんなログで埋め尽くされると困るので、これを解消しましょう。

`morgan`にはskipという、ログ出力をスキップするための条件を入れることができるので、それを追加します。

```src/server.js
const express = require('express');
const morgan = require('morgan');

const app = express();
const skip = (req: Express.Request, res: Express.Response) => {
    const userAgent = req.headers['user-agent'];
    if (userAgent == null) {
        return false;
    }
    return userAgent.includes('ELB-HealthChecker');
};
app.use(morgan('combined', { skip })); // skipを追加
app.get(
    '*',
    (_, response) => {
        response.setHeader('Content-Type', 'application/json');
        response.send(JSON.stringify({ msg: 'hello world' }));
    }
);
app.listen(
    3000,
    () => { console.log('start application server.') }
);

const http2https = express();
http2https.get(
    '*',
    (request, response) => {
        response.redirect(301, `https://${request.hostname}${request.url}`);
    }
);

http2https.listen(
    3001,
    () => { console.log('start redirect server.'); }
);
```

コードの修正をしたら、これをまたDocker containerとしてビルドし、pushして、Serviceのアップデートをします。

```shell-session
hoge:ws$ `aws ecr get-login --no-include-email --profile your-alias`
hoge:ws$ docker build -t your-alias/sample-app .
hoge:ws$ docker tag your-alias/sample-app:latest xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/your-alias/sample-app:latest
hoge:ws$ docker push xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/your-alias/sample-app:latest
hoge:ws$ aws ecs update-service --cluster sample-app-cluster --service corporate-sample-app-http --force-new-deployment
hoge:ws$ aws ecs update-service --cluster sample-app-cluster --service corporate-sample-app-https --force-new-deployment
```

前半4行でpushし、後半2行でサービスのアップデートをしてます。`--force-new-deployment`をつけることで、新しくpushされたイメージがデプロイされます。
一連をスクリプト化してマージされたらデプロイみたいにすると、開発がはかどります。

### この記事を書いたきっかけ(おまけ)
さて本記事ですが、とある人にAWSでDockerしたいんだけど教えてよくらいのふわっとした機会がありまして、いざ説明しようとすると意外と難しいなと思ったのがきっかけです。

普段、個人で作業していると、だいたいどこになんの設定があるかわかっているので多少手順が逆転しても、うまくやれるのですが、人に説明するケースに於いては最悪で、手戻りのあるハンズオンは教えられてる側はありえないくらい混乱します。
個々別々にドキュメントをググりながら自分で設定できるよという水準には比較的簡単にたどり着けると思うのですが、人に説明するというレベルには一段壁があり、この壁を乗り越えることには価値があると考えています。

あと、間違ってると強い人が訂正してくれるかもしれないので、こういうのはとりあえず書いて世の中に解き放ちまくったほうがいいです。