Dockerサービスの簡単リバースプロキシ

#はじめに
Docker使ってますか？同一ホストに簡単に複数のアプリケーション環境を起動することができて便利ですよね。

しかし、複数のコンテナを起動した際に悩ましいのがポートマッピング問題・・・。

- ホスト側の空きポートを明示的に指定してあげる必要がある
- ブラウザからアプリケーションへのアクセスは、可能な限り80番を使いたい

これらの問題を、nginxで（動的に）リバースプロキシを設定し、任意のサブドメイン等からコンテナにアクセス可能にさせてくれるDockerイメージが **[jwilder/nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy/)** です。

#アプリケーションのポートマッピング例

以下のような、docker-compose.ymlファイルの場合

```yml:docker-compose.yml
version: '3'

services:
  web:
    image: web
    ports:
      - "80:80"

  web2:
    image: web2
    ports:
      - "8080:80"

  web3:
    image: web3
    ports:
      - "9000:80"
```

※わかりやすいように、オプションは一部割愛

webコンテナへのアクセスは、ポート80番なので `http://ホスト` 等でアクセスすることができるが、例えばweb2へアクセスする際は `http://ホスト:8080` のように、ポート番号を付与する必要が出てくる。


#リバースプロキシ設定
実際にリバースプロキシを設定していきます。設定は簡単で、docker-compose.ymlに、jwilder/nginx-proxy をイメージに指定したリバースプロキシ用コンテナを追加し、リバースプロキシを設定した各コンテナに対し、環境変数**VIRTUAL_HOST**を与えてあげるだけです。

```yml:docker-compose.yml
version: '3'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  web:
    image: web
    environment:
      - VIRTUAL_HOST=web.localhost

  web2:
    image: web2
    environment:
      - VIRTUAL_HOST=web2.localhost

  web3:
    image: web3
    environment:
      - VIRTUAL_HOST=web3.localhost
```

上記のように設定し、docker-compose up すれば、VIRTUAL_HOSTに設定したサブドメインでブラウザからアクセス可能になります。これならポートを意識する必要がありません。


#ポイント

##同一ネットワーク上に起動させる
設定例ではネットワークを指定していませんので、docker-composeによって生成されるネットワークに属することになりますが、nginx-proxyコンテナと、リバースプロキシ対象のコンテナを別々で起動させる場合は、明示的に同じネットワークを指定してあげる必要があります。

##exposeでアプリケーションが利用されるポートを指定
各サービスのexposeオプションを指定しなかった場合は、デフォルトで80番ポートに接続しようとするので、80番以外で接続させる場合は、exposeで該当ポートを指定してあげる必要があります。


#その他
##追加でコンテナを起動してもリバースプロキシしてくれる
nginx-proxyコンテナがネットワーク上のDockerコンテナの起動を検知し、後から追加されたコンテナに対してもしっかりリバースプロキシを設定してくれます。

##アクセス制限
環境変数に`NETWORK_ACCESS=internal`が指定されているコンテナに対し、nginx-proxyコンテナ内の`/etc/nginx/network_internal.conf`に記述された設定に基づいてアクセス制限を設定することができます。

```conf:network_internal.conf
allow 10.0.2.2;
deny all;
```

ホスト側で用意した上記ファイルをnginx-proxyコンテナへマウントします。

```yml:docker-compose.yml
version: '3'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./network_internal.conf:/etc/nginx/network_internal.conf

  web:
    image: web
    environment:
      - VIRTUAL_HOST=web.localhost

  web2:
    image: web2
    environment:
      - VIRTUAL_HOST=web2.localhost
      - NETWORK_ACCESS=internal
```

以上のように設定すると、web2は`10.0.2.2`からのみアクセス可能なコンテナになります。


##その他、転送可能データサイズの変更やタイムアウト時間設定
nginx-proxyコンテナ内`/etc/nginx/conf.d`ディレクトリ配下に置かれた設定ファイルを読み込むので、ホスト側で設定ファイルを用意して以下のようにコンテナ内にマウントしてあげれば良いです。


```yml:docker-compose.yml
version: '3'

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf

  web:
    image: web
    environment:
      - VIRTUAL_HOST=web.localhost
```

#おわりに
ローカルでの検証にはもちろん、Dockerコンテナが動的に増減するようなサービスで大いに役立ちます。感謝です。上で紹介した機能以外にもいくつか便利な機能を有していますので、GitHubのREADMEをご参照ください。

###参考ページ
- [jwilder/nginx-proxy](https://github.com/jwilder/nginx-proxy)
- [nginxを使ったリバースプロキシ on Docker](https://qiita.com/ka2asd/items/372d30be64c57a8a81b1)