Amazon EC2にコンテナを使ってExpress+nginxを導入した話


ゴールはec2のパブリックipにアクセス(ポート指定なしで)すると、Expressのサンプルアプリ(Welcome to Express)が表示されることがゴールです。
Dockerでコンテナを作成して、実現していきます。

</br>
**<作成手順>
１、EC2インスタンスの作成
２、EC2にDockerをインストール
３、ローカルでexpressアプリを作成
４、docker-compose.ymlを記述する
５、リバースプロキシを設定する**

</br>
</br>
##１、EC2インスタンスの作成
事前に公開鍵と秘密鍵は作成しておいてください。[→参照]
(https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair)


OSはAmazon Linux AMIを選択して、インスタンスタイプは無料枠のt2.microを選択します。
<img width="1107" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/c81b720f-0ba9-d5d7-111e-11ba554c6b82.png">

</br>
セキュリティグループの作成で、ポート番号にHTTP用の80とアプリケーション用の3000をルール追加しておきます。
</br>

<img width="1107" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/e85fa4c7-721e-567e-b91d-d73c6fd7a7db.png">

</br>
インスタンスが作成できたら、公開鍵を使って

```shell

#~/.ssh/key-name.pemの部分は自分が作った公開鍵の場所を指定
$ ssh -i ~/.ssh/key-name.pem ec2-user@パブリックipアドレス
```

でインスタンスにログインします。

</br>
</br>
##２、EC2にDockerをインストール
```shell

[ec2-user@ip-0-0-0-0 ~]$ sudo yum -y update
[ec2-user@ip-0-0-0-0 ~]$ sudo yum install -y docker
[ec2-user@ip-0-0-0-0 ~]$ sudo gpasswd -a $USER docker
[ec2-user@ip-0-0-0-0 ~]$ sudo service docker start
```

今回はdocker-composeコマンドでコンテナを作成するので、docker-composeコマンドもインストールします。

```shell

[ec2-user@ip-0-0-0-0 ~]$ sudo -i
[ec2-user@ip-0-0-0-0 ~]$ curl -L "https://github.com/docker/compose/releases/download/1.11.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[ec2-user@ip-0-0-0-0 ~]$ chmod +x /usr/local/bin/docker-compose
[ec2-user@ip-0-0-0-0 ~]$ exit
```
> 
[Amazon Linux に Docker Compose を install する方法](https://qiita.com/rysk92/items/e10f898abdc701e09e38)

</br>
</br>
##３、ローカルでexpressアプリを作成
以下の手順でローカル環境でexpressアプリを作成します。

```shell
#expressを使う準備
$ npm install -g express
$ npm install -g express-generator

#expressコマンドでアプリディレクトリを作成。テンプレートエンジンはお好みで
$ mkdir express && cd express
$ express --view=pug myapp
$ cd myapp && npm install

#nginx用の設定ディレクトリ、設定ファイルとdocker-compose.ymlを作成
$ mkdir conf.d && touch conf.d/default.conf && touch docker-compose.yml
```

ここまでで実行するとディレクトリ構造は以下のような感じに

<img width="261" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/b8ee92f3-688a-57d8-f735-8ae8e0cd7e6e.png">

</br>
</br>
##４、docker-compose.ymlを記述する
ウェブコンテナ(nginx)とアプリケーションコンテナ(node_express)を以下のように記述します。

ちなみに、volumesはコンテナ内のどの場所にディレクトリを配置するかを示しています。

```yaml:myapp/docker-compose.yml
version: '2'
services:

  web:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - "./conf.d:/etc/nginx/conf.d"
    links:
      - app

  app:
    image: node:8.9.3-alpine
    container_name: node_express
    volumes:
      - ".:/src"
    working_dir: /src
    command: [sh, -c, npm install && npm start]
    #コンテナのポート3000をホストのポート3000で公開する
    ports:
      - "3000:3000"
```

</br>
</br>
##５、リバースプロキシを設定する
この状態では、パブリックipにポート3000を指定すれば、expressのウェルカムページが表示されます。

ポート3000を指定してなくてもパブリックipだけ打てば、expressのウェルカムページが表示されるように、nginxの設定ファイルmyapp/conf.d/default.confを以下のようにします。

```conf:myapp/conf.d/default.conf
server {
    listen       80;

    charset utf-8;
    access_log /var/log/nginx/default.access.log main;
    error_log /var/log/nginx/default.error.log;

    proxy_redirect                      off;
    proxy_set_header Host               $host;
    proxy_set_header X-Real-IP          $remote_addr;
    proxy_set_header X-Forwarded-Host   $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;

    location / {
        #コンテナをプロキシにする際はコンテナ名:ポート番号を設定することで解決
        proxy_pass http://app:3000;
    }


    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
```

</br>
</br>
##アプリを動かすときがきた
ここまで作成できたら、実際にローカルで作成したディレクトリをEC2のtmp配下に転送してEC2でコンテナを起動します！
</br>
＊転送の部分は、Gitで管理してCodeBuildを使って自動化するとより今どきなのかもしれません。

```shell
#ローカルから作成したexpressディレクトリをEC2へ転送
$ rsync -e "ssh -i ~/.ssh/key-name.pem" -r express --exclude=/node_modules ec2-user@パブリックipアドレス:/tmp

#EC2内のdocker-compose.ymlがあるところまで移動して、実行
$ ssh -i ~/.ssh/key-name.pem ec2-user@パブリックipアドレス
[ec2-user@ip-0-0-0-0 ~] $ cd /tmp/express/myapp
[ec2-user@ip-0-0-0-0 ~] $ docker-compose up -d
```

</br>
</br>
nginxとnode_expressの２つのコンテナが実行され、ブラウザでパブリックipアドレスを打ち込めばサンプルアプリが表示されます。

<img width="760" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/be9ed10b-f4a5-2abb-7f14-210676dad4e8.png">


</br>
</br>
##Amazon ECSを使えばもっといい環境になるのかも...
コンテナのデプロイについてAWSではAmazon ECSを利用することを推奨しています。Amazon ECS環境ではEC2起動だけでなく、コンテナのアプリケーション構築に特化したAWS Fargateというエンジンで起動できるようです。Amazon ECSはコンテナのクラスター管理がしやすくなるので、コンテナの数が増えてきた場合には要検討かもしれません。もう少し勉強が必要そうです。

<img width="717" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/08295001-8b03-0450-296b-e62d2b75db3a.png">

>
https://github.com/awslabs/ecs-refarch-continuous-deployment