初心者でも試せる！DockerをECRにpushしてECS上で動かしてみた 

# 概要
### 前提
Macですすめる
仮想化やDocker概要など基本的な内容は割愛
### 目的
DevOpsの勉強のため、DockerやECR, ECSをざっくり把握する
### ゴール
以下ができるようになること

- Dockerでのコンテナ起動
- Docker imageをECRへpush
- ECSにDockerコンテナのデプロイ



# Docker で Nginx を動かしてみる
### 1. Docker for Macをインストール
- [DockerHub](https://hub.docker.com/editions/community/docker-ce-desktop-mac)からダウンロードしてインストール
- インストールされたかの確認

```terminal:terminal
$ docker version
Client: Docker Engine - Community
 Version:           19.03.2
 API version:       1.40
 Go version:        go1.12.8
 Git commit:        6a30dfc
 Built:             Thu Aug 29 05:26:49 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.2
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.8
  Git commit:       6a30dfc
  Built:            Thu Aug 29 05:32:21 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```
### 2.  Dockerfile 作成

今回はnginxを稼働させるコンテナを作るので、下記を行います

 1. nginxの設定・WEBサーバで実行するコンテンツを作成
 1. Dockerfileの作成

最終的に以下のようなディレクトリになります

```tree:ディレクトリ構成
.
├── Dockerfile
├── app
│   └── index.html
└── nginx.conf
```


#### Webサーバ設定を作成
 - `listen`: IPアドレス:ポート番号の指定
 - `default_server` オプション:複数のバーチャルサーバがあるときにデフォルトサーバとなる
 - `root`: ドキュメントルートを指定する(nginxのデフォルトは`/usr/share/nginx/html`)

```conf:nginx.conf
server {
        listen *:80 default_server;

        location / {
            root /app;
        }
}
```

#### WEBサーバで実行するコンテンツを作成

表示する index.html を作成

```html:app/index.html
<html>
    <head>
        <title>hello</title>
    </head>
    <body>
        <p>HELLO WORLD!</p>
    </body>
</html>
```

#### Dockerfile作成

- `FROM`: ベース・イメージを指定。ここでは軽量化のため、alpine linux をベースにする
- `RUN`: イメージ上でコマンド実行。ここではnginx 導入/起動
- `COPY`: コンテナ内にホストのファイル/ディレクトリをコピー。ここではWEBページ表示に必要なソースを配置
- `EXPOSE`: コンテナ起動時に公開するポートを記載。
    - イメージ作成者とコンテナ実行者の間の一種のドキュメンテーションとして機能し、これだけではホストからコンテナにアクセスできない。
    - コンテナの実行時(`docker run`)に`-p`オプション等でポートを公開する必要がある
- `CMD`: Docker起動時に実行するコマンド。nginxはデフォルトはデーモンで動くので、フォアグラウンドで動くようにする


```Docker:Dockerfile
FROM alpine:3.10.3

# nginxのインストール
RUN apk update && \
    apk add --no-cache nginx

# 必要なファイルを配置
COPY app /app
COPY nginx.conf /etc/nginx/conf.d/default.conf

# ポート設定：
EXPOSE 80

RUN mkdir -p /run/nginx

# フォアグラウンドでnginx実行
CMD nginx -g "daemon off;"
```

### 3.  docker image  作成/確認
#### 作成した Dockerfile からdocker image を作成
 - `docker build -t <IMAGE:TAG> <コンテキスト指定>`
 - `-t`: 名前とオプションのタグを指定
 - `コンテキスト指定`: Dockerデーモンへ送信するディレクトリ指定（Dockerfileは必ず含まれるようにする）

```bash:terminal
$ docker build -t test_nginx . 
```

#### 作成されたimageを確認

```bash:terminal
$ docker images
REPOSITORY     TAG        IMAGE ID        CREATED           SIZE
test_nginx     latest     d4a1d35ad05f    10 seconds ago    6.5MB
```

ここまででDockerコンテナの作成ができました🎉

### 4.  コンテナ起動/動作確認
#### コンテナの起動
 - `docker run -d -p <ホストのポート番号：コンテナのポート番号> --name <コンテナ名> <イメージ名>`
 - `--name`: 任意のイメージ名を指定
 - `-d`: デタッチド・モードで起動（バックグラウンドでコンテナを実行）
 - `-p`: ポートの割当(ホスト側ポート：コンテナ側ポート）

```bash:terminal
$ docker run -d -p 80:80 --name sample_container test_nginx
```

#### コンテナの起動を確認

```terminal:termina
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
4da8512fc776        test_nginx          "/bin/sh -c 'nginx -…"   11 minutes ago      Up 11 minutes       0.0.0.0:80->80/tcp   sample_container
```

#### ブラウザでWEBページ表示確認
 [ローカルホスト](127.0.0.1:80)(`127.0.0.1:80`)で下記ページが表示されていることを確認できればOK！

<img width="505" alt="スクリーンショット 2019-11-12 午後5.32.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75813/cacc3d45-f350-af7c-4f27-58edbdd0aaa1.png">

### ※（必要に応じて）コンテナ削除/イメージ削除
コンテナやイメージが不要になった場合は削除しておく
#### コンテナ削除

```terminal:terminal
$ docker rm -f <コンテナ名>
```
#### イメージ削除

```terminal:terminal
$ docker rmi -f <イメージ名>
```
#### コンテナへの接続

```terminal:terminal
$ docker exec -it <コンテナ名> <実行コマンド>
```
例) shellに接続する場合（shell接続後は、exitで抜ける）

```terminal:terminal
$ docker exec -it <コンテナ名> sh
```

# AWS設定（AWS CLI）
### 1. IAM でユーザー作成
- IAMユーザー作成
- [[AmazonEC2ContainerServiceFullAccess](https://console.aws.amazon.com/iam/home?#/policies/arn:aws:iam::aws:policy/AmazonEC2ContainerServiceFullAccess$serviceLevelSummary)],[[AmazonEC2ContainerRegistryFullAccess](http://console.aws.amazon.com/iam/home?#/policies/arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess$serviceLevelSummary)] のアクセス権限追加

### 2. CLIインストール

#### インストール

pipをインストールしてからaws-cliをインストールする方法もあるが、homebrewでinstall

```bash:terminal
$ brew install awscli
```

#### インストールされたか確認

```bash:terminal
$ aws --version
aws-cli/1.16.9 Python/2.7.16 Darwin/17.7.0 botocore/1.11.9
```

#### 環境変数の設定（pathを通す）

```bash:terminal
$ vi ~/.bash_profile

以下を追記
export PATH=~/.local/bin:$PATH
```

### 3. AWS CLIアカウント設定

- IAMでアクセスキーを作成
- 認証情報登録 -> 設定ファイル（~/.aws/config, ~/.aws/credentials ）が作成される

```terminal:terminal
$ aws configure
AWS Access Key ID [None]: 作成したアクセスキー ID
AWS Secret Access Key [None]: 作成したアクセスキーのシークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: 
```

#### profile確認/設定

設定を確認すると、default とうプロファイルで定義されている

```terminal:terminal
$ less ~/.aws/config
[default]
output = json
region = ap-northeast-1
```
AWS CLIコマンドを利用する際、プロファイルを指定しなければ、[default]設定が利用される。
defaultのプロファイルではない場合、何度もプロファイルを指定するのが面倒なので、
以下のようにシェルセッションの終了時まで変数に持たせておくと便利

```terminal:terminal
$ export AWS_PROFILE=利用したいプロファイル名
```

# Amazon ECR へ Docker image を Push
  
### 1. リポジトリ作成
- リージョンやリポジトリは何度も利用するので変数に持たせておく

```terminal:terminal
$ REPOSITORY_NAME=sample-docker
$ AWS_REGION=ap-northeast-1
```

- リポジトリ作成

```terminal:terminal
$ aws ecr create-repository --repository-name ${REPOSITORY_NAME} --region ${AWS_REGION}
```

- リポジトリ確認

```terminal:terminal
$ aws ecr describe-repositories --region ${AWS_REGION}
{
    "repositories": [
        {
            "repositoryArn": "arn:aws:ecr:ap-northeast-1:***********:repository/sample-docker",
            "registryId": "***********",
            "repositoryName": "sample-docker",
            "repositoryUri": "***********.dkr.ecr.ap-northeast-1.amazonaws.com/sample-docker",
            "createdAt": 1573385404.0,
            "imageTagMutability": "MUTABLE"
        }
    ]
}
```

- レジストリIDは頻繁に利用するので、環境変数に保持

```terminal:terminal
$ REGISTRY_ID=作成したリポジトリID
```

### 2. デフォルトレジストリに対して Docker を認証する
aws cli コマンドと、Dockerコマンドは別のコマンドなので、それぞれの認証が必要。
aws cli コマンドでDocker認証情報を取得し、DockerコマンドでECRへログインする

#### ECR レジストリに対し Docker クライアントで認証
 - 下記コマンドのレスポンスに、**ログイン用コマンドが表示されるので、それをそのまま実行**するとログイン完了
 - 対象レジストリで**12時間有効な認証トークン**が提供される

```terminal:terminal
$ aws ecr get-login --region ${AWS_REGION} --no-include-email
```

※以下のように実行結果を出力し展開するようにすると一回で完了

```terminal:terminal
$ $(aws ecr get-login --region ${AWS_REGION} --no-include-email)
```

### 3. コンテナ名変更/image に tag つけ
- イメージ名は、使用する Amazon ECR レジストリ、リポジトリ、オプションのイメージタグ名の組み合わせによって変わり、名称を一致させる必要がある
- コンテナ/tag命名規則: `{レジストリID}.dkr.ecr.{リージョン}.amazonaws.com/{リポジトリ名}:{タグ名}`

```terminal:terminal
$ docker tag test_nginx ${REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPOSITORY_NAME}}:v1.0
```
### 4. image を push

```terminal:terminal
$ docker push ${REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPOSITORY_NAME}:{タグ名}
```

### 5. ECR にアップした image を確認

```terminal:terminal
$ aws ecr list-images --repository-name ${REPOSITORY_NAME} --region ${AWS_REGION}
{
    "imageIds": [
        {
            "imageDigest": "sha256:*********************************************",
            "imageTag": "v1.0"
        }
    ]
}
```
### 6. image を pull

```terminal:terminal
$ docker pull ${REGISTRY_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPOSITORY_NAME}:{タグ名}
```
### 7. イメージを削除

```terminal:terminal
$ aws ecr batch-delete-image --repository-name ${REPOSITORY_NAME} --image-ids imageTag={タグ名}
```
### 8. リポジトリを削除
- リポジトリに保存するデータとインターネットに転送するデータの量で料金発生なので、不要な場合は削除しておく

```terminal:terminal
$ aws ecr delete-repository --repository-name ${REPOSITORY_NAME} --region ${AWS_REGION}
```
### 補足
#### aws cli で docker image を push するために必要な条件
 - docker の最小バージョン 1.7 がインストールされている
 - Amazon ECR 認証トークンが docker login で設定されている
 - Amazon ECR リポジトリが存在し、リポジトリにプッシュするアクセス権がユーザーにある

ここまでで、ECRに対して docker pull/push/delete ができました🎉

# Amazon ECS で Docker を起動
  
### 1. キーペア作成
ECS では、EC2 起動タイプを使用する場合にのみキーペアが必要
※UI以外から接続することでエラーでインスタンスが落ちたときなどにログを追うこともできます

### 2. ECSクラスターの作成
#### EC2インスタンスの作成
| 項目 | 内容 |
|---|---|
| AMI | Amazon ECS-Optimized Amazon Linux 2 AMI |
| インスタンスタイプ | t2.micro |
| IAMロール |  新しい IAM ロールを作し、Amazon EC2 Container Service Role中のAmazonEC2ContainerServiceRoleのロールを割り当てる |
| セキュリティグループの設定 | HTTP TCP 80 0.0.0.0 |

#### クラスターの作成
| 項目 | 内容 |
|---|---|
|クラスターテンプレート|EC2 Linux + ネットワーキング|
|クラスター名|sample-cluster|
|プロビジョニングモデル|オンデマンドインスタンス|
|EC2 インスタンスタイプ|t2.micro|
|インスタンス数|1|
|EC2 Ami Id|Amazon Linux 2 AMI|
|キーペア|手順1で作成したキーペアを利用|
|コンテナインスタンス IAM ロール|EC2インスタンス作成時に割り当てたロールを選択|

### 3. タスク定義作成
| 項目 | 内容 |
|---|---|
|起動タイプの互換性|EC2|
|タスク定義名|sample-task|

コンテナ追加箇所の設定

| 項目 | 内容 |
|---|---|
|コンテナ名|sample-container|
|イメージ|ECRにpushしている対象イメージのURIを入力|
|メモリ制限(MB)|ハード制限 128|
|ポートマッピング|ホストポート 80, コンテナポート 80, プロトコル tcp|


### 4. タスクの実行
- 作成したクラスター(`sample-cluster`)をクリック
- [タスク]の項目から[新しいタスクの実行]をクリック
- タスク定義

| 項目 | 内容 |
|---|---|
|起動タイプ|EC2|
|タスク定義|作成したタスク(`sample-task`)|
|クラスター|作成したクラスター(`sample-cluster`)|
|タスクの数|1|
- [タスクの実行]をクリック
- ステータスが[RUNNING]になっていることを確認
<img width="1628" alt="スクリーンショット 2019-11-13 23.17.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75813/8350a830-35fd-ea98-29a0-324f61055940.png">

### 5. 表示確認
- コンテナインスタンスのパブリック DNSに接続してうまく表示されればOK！


# まとめ
- Dockerでのコンテナ起動 -> 環境構築をお手軽に！
- Docker imageをECRへpush
-> チーム内で開発環境を共有可能に！　
- ECSにDockerコンテナのデプロイ -> クラウド上でDockerコンテナを利用しサービスを稼働させられる！

# 参考
[AWS CLIを使ってECRにDockerイメージを登録する](https://dev.classmethod.jp/cloud/aws/push-docker-image-to-ecr-with-cli/)
[Amazon ECS入門 〜公式のDockerイメージを使って10分で構築してみる〜](https://dev.classmethod.jp/cloud/aws/amazon-ecs-entrance-1/)
[Amazon ECR で AWS CLI を使用する](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/ECR_AWSCLI.html)