AWS Elastic Beanstalk に Docker マルチコンテナで複数アプリケーションをデプロイ

この記事は [CureApp Advent Calendar 2016](http://qiita.com/advent-calendar/2016/cureapp) 7日の記事です。

今日は [Elastic Beanstalk](https://aws.amazon.com/jp/elasticbeanstalk/) に [Docker](https://www.docker.com/) ([ECS](https://aws.amazon.com/jp/ecs/)) で複数アプリをデプロイする話です。


## 目的

ひとつの Elastic Beanstalk に複数のアプリケーションを入れたら、
インスタンスの節約になったりするかも。

という動機のもとで、以下のような構成を検討しました。
 
<img width="788" alt="Screen Shot 2016-11-16 at 18.12.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/94449/33c9434d-356e-3354-77af-efa8a605b2fa.png">

## 前提条件

* aws アカウント
* node, docker, docker-compose インストール (ローカル確認用なので必須ではないです)

## 手順

### 1. テスト用アプリケーション作成
node で表示するだけのアプリケーションを作成。

```json:node-app1/package.json
{
  "name": "node-app1",
  "version": "1.0.0",
  "description": "sample node app 1",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC"
}
```

```js:node-app1/server.js

var http = require('http')
var server = http.createServer()

server.on('request', function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'})
    res.write('<h1>Hello my first application!</h1>')
    res.end()
})

server.listen(80)
console.log('server listening...')
```

```
$ sudo npm start
```
同様に表示を少し変えて node-app2 も作成しました。

### 2. nginx の設定

```nginx:proxy/conf.d/default.conf
server {
    listen 80;
    server_name localhost;
 
    location / {
        proxy_pass http://node-app1;
    }

    location /second/ {
        proxy_pass http://node-app2;
    }
}
```

通常は server_name とかで分けると思いますが、テストを簡単にするため、url で分岐させています。

### 3. docker compose で動かしてみる

試しに docker compose を使ってローカルで動かしてみました。

```yaml:docker-compose.yml
version: '2'
services:
  node-app1:
    image: node:6.9.1-slim
    volumes:
    - ./node-app1:/usr/src/app
    working_dir: /usr/src/app
    command: npm start

  node-app2:
    image: node:6.9.1-slim
    volumes:
    - ./node-app2:/usr/src/app
    working_dir: /usr/src/app
    command: npm start

  nginx-proxy:
    image: nginx
    volumes:
    - ./proxy/conf.d:/etc/nginx/conf.d
    ports:
    - "80:80"
    links:
    - node-app1
    - node-app2
```

```
.
├── docker-compose.yml
├── node-app1
│   ├── package.json
│   └── server.js
├── node-app2
│   ├── package.json
│   └── server.js
└── proxy
    └── conf.d
        └── default.conf
```

```
$ docker-compose up
```

ブラウザから確認。

```
http://localhost/first
http://localhost/second
```

今回は [node:6.9.1-slim](https://github.com/nodejs/docker-node/blob/4a722c29c0e52624af8b72b4711ebeba8ea39463/6.9/slim/Dockerfile) を使いましたが、実際は Docker Hub にあげて使う感じでしょうか。
Elastic Beanstalk 複数コンテナ環境では Dockerfile は使えないらしい。。([公式サイト](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_ecs.html))

### 4. アップロードファイル作成

[公式サイト](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker_v2config.html)と docker-compose.yml を見比べながら、Dockerrun.aws.json を作成しました。

```json:Dockerrun.aws.json
{
  "AWSEBDockerrunVersion": 2,
  "volumes": [
    {
      "name": "node-app1-src",
      "host": {
        "sourcePath": "/var/app/current/node-app1"
      }
    },
    {
      "name": "node-app2-src",
      "host": {
        "sourcePath": "/var/app/current/node-app2"
      }
    },
    {
      "name": "nginx-proxy-conf",
      "host": {
        "sourcePath": "/var/app/current/proxy/conf.d"
      }
    }  
  ],
  "containerDefinitions": [
    {
      "name": "node-app1",
      "image": "node:6.9.1-slim",
      "essential": true,
      "memory": 128,
      "mountPoints": [
        {
          "sourceVolume": "node-app1-src",
          "containerPath": "/usr/src/app",
          "readOnly": true
        }
      ],
      "workingDirectory" : "/usr/src/app",
      "command": ["npm", "start"]
    },
    {
      "name": "node-app2",
      "image": "node:6.9.1-slim",
      "essential": true,
      "memory": 128,
      "mountPoints": [
        {
          "sourceVolume": "node-app2-src",
          "containerPath": "/usr/src/app",
          "readOnly": true
        }
      ],
      "workingDirectory" : "/usr/src/app",
      "command": ["npm", "start"]
    },
    {
      "name": "nginx-proxy",
      "image": "nginx",
      "essential": true,
      "memory": 128,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80
        }
      ],
      "links": [
        "node-app1",
        "node-app2"
      ],
      "mountPoints": [
        {
          "sourceVolume": "nginx-proxy-conf",
          "containerPath": "/etc/nginx/conf.d",
          "readOnly": true
        },
        {
          "sourceVolume": "awseb-logs-nginx-proxy",
          "containerPath": "/var/log/nginx"
        }
      ]
    }
  ]
}
```

このあと作成する zip がホストの /var/app/current に解凍されます。
`essential`は、true に設定されたコンテナが止まると他のコンテナもすべて止まるという設定。([公式サイト](http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html))



```
.
├── Dockerrun.aws.json
├── node-app1
│   ├── package.json
│   └── server.js
├── node-app2
│   ├── package.json
│   └── server.js
└── proxy
    └── conf.d
        └── default.conf
```

圧縮します。

```
$ zip -r apps.zip Dockerrun.aws.json node-app1 node-app2 proxy
```



### 5. デプロイ

AWS Elastic Beanstalk コンソールからアプリケーションを作成。

| 項目 | 入力値 |
|:-:|:-:|
|環境|ウェブサーバー環境|
|アプリケーション名|任意|
|プラットフォーム|Multi-container Docker|
|アプリケーションコード|作成した apps.zip をアップロード|

表には記載していませんが、[さらにオプションを設定] → [セキュリティ] → [キーペア]を設定すると、
ホストに SSH で入れるようになります。

### 6. 完成！

作成後、無事ブラウザからアクセスできました。

```
http://******.elasticbeanstalk.com/first
http://******.elasticbeanstalk.com/second
```

EC2 Container Service のコンソール画面から確認。

<img width="893" alt="Screen Shot 2016-11-28 at 13.58.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/94449/692f67c0-d9b1-3296-77ce-e8acf353f8f2.png">

SSH でホストに入って確認。

```
$ sudo docker ps
```
<img width="1030" alt="Screen Shot 2016-11-28 at 14.00.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/94449/ccdba2b8-eefa-aeda-adf5-c18339865c51.png">




## 感想
以外にすんなり動きました。
最初に作成する際は、docker compose でやってみると、前もって動作確認ができるのと、Dockerrun.aws.json も作成しやすくなるのでいいです。
リソースの問題などいろいろ考慮する必要があると思うので、実際に使えるかどうかは、まだなんとも。。


明日は、@kt3k@github さんです。

## 　
* 参考資料　[amazon 開発者ガイド](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create_deploy_docker.html)
* 作成したコード　[github](https://github.com/fitzr/eb-ecs-sample)