docker-compose + リバースプロキシ + ロードバランシングの環境構築手順

## 概要
docker-composeで構築した環境に、nginxのコンテナを作成し、リバースプロキシとロードバランシング(scaleを使います)の機能を持たせた手順。

下記の記事に、**リバースプロキシ**と**ロードバランシング**の機能を加えたものです。
[関連記事] [docker-compose on CentOS7構築手順メモ](http://qiita.com/Esfahan/items/284d49e20648fde92639)

## 環境
- CentOS 7.1
- docker-engine (Docker 1.12.3)
- docker-compose 1.8.1

## docker-engine, docker-composeのインストール
下記を参照
[docker(docker-engine), docker-composeのインストール](http://qiita.com/Esfahan/items/aa5802548ee42d8a98de)
 
## コンテナ構成

| コンテナ名 | 役割 | リンク先 | イメージ元 |
| :-- | :-- | :-- | :-- |
| db | dbサーバ | - | 既存のmysql:latest |
| web | webサーバ | - | 既存のnginx:latest |
| api | webサーバ | db | 自作のDockerfileをbuild |
| proxy | リバースプロキシ | web, api | 自作のDockerfileをbuild |

## ファイル構成

```
└── ~/servicename
    ├── api/
    │   ├── Dockerfile
    │   └── index.html
    ├── docker-compose.yml
    ├── proxy/
    │   ├── Dockerfile
    │   └── etc/
    │       └── nginx/
    │           └── conf.d/
    │               └── proxy.conf
    └── web/
        └── index.html
```

## ディレクトリを作成
先に必要なディレクトリを作成しておきます。
TOPディレクトリ名や階層の位置は、適宜変えるなり自由にしてください。

```
$ mkdir ~/servicename
$ mkdir -p ~/servicename/proxy/etc/nginx/conf.d/
$ mkdir -p ~/servicename/api
$ mkdir -p ~/servicename/web
```

## docker-compose.yml

scaleする際のポート重複を避けるため、ホスト側のポートは指定せず自動で割り当てられようにする

`$ vi ~/servicename/docker-compose.yml`

```lang:~/servicename/docker-compose.yml
version: '2'
services:
    # dbコンテナ
    db:
        image: mysql:latest
        # DBのアカウント
        environment:
            MYSQL_DATABASE: sample_db
            MYSQL_USER: sample
            MYSQL_PASSWORD: sample_password
            MYSQL_ROOT_PASSWORD: root

    # webコンテナ
    web:
        image: nginx:latest
        # パスをボリュームとしてマウント {ホスト側:コンテナ側}
        volumes:
            - ./web:/usr/share/nginx/html
        ports:
            - 80

    # apiコンテナ
    api:
        build: ./api
        # リンク先コンテナ {コンテナ名:エイリアス名}
        # ver2ではこのディレクティブがなくてもリンクされます
        links:
            - db:db-server
        ports:
            - 80

    # リバースプロキシコンテナ
    proxy:
        build: ./proxy
        # リンク先コンテナに振り分けるコンテナを設定 {コンテナ名:エイリアス名}
        # こっちはないとダメ
        links:
            - web:web
            - api:api
        ports:
            - 80:80
```

## proxyコンテナ
リバースプロキシ用のnginxの設定

### Dockerfile
`$ vi ~/servicename/proxy/Dockerfile`

```lang:~/servicename/proxy/Dockerfile
From nginx
MAINTAINER Iam <xxxx@xxxx.xx>
COPY etc/nginx/conf.d/* /etc/nginx/conf.d/
ENTRYPOINT /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

### nginxの設定ファイル
`$ vi ~/servicename/proxy/etc/nginx/conf.d/proxy.conf`

```lang:~/servicename/proxy/etc/nginx/conf.d/proxy.conf
# webコンテナのロードバランシング設定 まずは1台だけ設定する
upstream web {
    least_conn;
    server servicename_web_1:80 weight=5 max_fails=3 fail_timeout=30s;
}

# apiコンテナのロードバランシング設定 まずは1台だけ設定する
upstream api {
    least_conn;
    server servicename_api_1:80 weight=5 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name 192.168.33.10;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://web/;

        location /web {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $http_host;
            proxy_pass http://web/;
        }

        location /api {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $http_host;
            proxy_pass http://api/;
        }

    }
}
```

least_conn メソッド : リクエストはアクティブな接続とserverの重みが考慮された数がもっとも少ないサーバに送られます。

## apiコンテナ
### Dockerfile

`$ vi ~/servicename/api/Dockerfile`

```lang:~/servicename/api/Dockerfile
FROM centos:7
MAINTAINER Iam <xxxx@xxxx.xx>
RUN yum install -y http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y nginx
RUN yum install -y http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
RUN yum install -y mysql-community-client
ADD index.html /usr/share/nginx/html/
ENTRYPOINT /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```


### htmlファイル作成
あとで表示確認用に使います。

```
$ vi ~/servicename/api/index.html
Hello, docker-compose from api.
```

## webコンテナ
### htmlファイル作成
あとで表示確認用に使います。

```
$ vi ~/servicename/web/index.html
Hello, docker-compose from web.
```

## コンテナ起動
docker-compose.ymlのあるディレクトリで実行

```
$ cd ~/servicename
$ docker-compose up -d
```


この時点で、下記のページが表示されるはずです。

http://192.168.33.10/
`Hello, docker-compose from web.`

http://192.168.33.10/web
`Hello, docker-compose from web.`

http://192.168.33.10/api
`Hello, docker-compose from api.`

ロードバランシングをしない場合は、これで完了です。

## ロードバランシングするために、コンテナを複製する
### webコンテナとapiコンテナを２台にスケールする

```
$ cd ~/servicename
$ docker-compose scale web=2 api=2
```

コンテナが増えたのを確認

```
$ cd ~/servicename
$ docker-compose ps

       Name                      Command               State                        Ports
-------------------------------------------------------------------------------------------------------------
servicename_api_1     /bin/sh -c /usr/sbin/nginx ...   Up       0.0.0.0:32793->80/tcp
servicename_api_2     /bin/sh -c /usr/sbin/nginx ...   Up       0.0.0.0:32795->80/tcp
servicename_db_1      docker-entrypoint.sh mysqld      Up       3306/tcp
servicename_proxy_1   /bin/sh -c /usr/sbin/nginx ...   Exit 1
servicename_web_1     nginx -g daemon off;             Up       443/tcp, 0.0.0.0:32792->80/tcp
servicename_web_2     nginx -g daemon off;             Up       443/tcp, 0.0.0.0:32794->80/tcp
```

### nginxのconfを修正
web, apiとも、scaleして増えた分を追記する。

`$ vi ~/servicename/proxy/etc/nginx/conf.d/proxy.conf`

```lang:~/servicename/proxy/etc/nginx/conf.d/proxy.conf
# webコンテナのロードバランシング設定
upstream web {
    least_conn;
    server servicename_web_1:80 weight=5 max_fails=3 fail_timeout=30s;
    # この行を追記 scaleして増えた分を書く
    server servicename_web_2:80 weight=5 max_fails=3 fail_timeout=30s;
}

# apiコンテナのロードバランシング設定
upstream api {
    least_conn;
    server servicename_api_1:80 weight=5 max_fails=3 fail_timeout=30s;
    # この行を追記 scaleして増えた分を書く
    server servicename_api_2:80 weight=5 max_fails=3 fail_timeout=30s;
}

(snip)
```

### proxyコンテナをリスタート

```
$ cd ~/servicename
$ docker-compose restart proxy
```

## 表示確認
それぞれ下記が表示されれば成功

http://192.168.33.10/
`Hello, docker-compose from web.`

http://192.168.33.10/web
`Hello, docker-compose from web.`

http://192.168.33.10/api
`Hello, docker-compose from api.`

## dbがリンクされているか確認
apiコンテナに入って、mysqlコマンドでdbコンテナに入れるか確認

```
$ sudo docker exec -it {apiコンテナID} bash
# mysql -u sample -p -h db sample_db
```

### コンテナの停止と削除
stopとrmを両方やってくれるコマンド

```
$ docker-compose down
```

## コンテナが起動しない場合は
- [コンテナが起動しない場合のdebug方法](https://qiita.com/Esfahan/items/52141a2ad741933d7d4c#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%8C%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88%E3%81%AEdebug%E6%96%B9%E6%B3%95)

以上

[参考]
[Docker で nginx を Reverse Proxy として使う](http://itomoyasu.com/docker-nginx-reverse-proxy/)
[Dockerを使ってロードバランシング環境構築](http://qiita.com/astap/items/08834cee14aad18c5f10)
[入門！ nginx](http://shim0mura.hatenadiary.jp/entry/20120110/1326198429)
[nginxでロードバランス設定メモ](http://og732.hatenadiary.com/entry/2013/05/08/225939)
[Docker Compose で複数コンテナを管理](http://christina04.hatenablog.com/entry/2016/05/09/002010)