Docker 上の nginx で Mattermost と Knowledge と Joomla をリバースプロキシさせてみた Ver.2

## はじめに

[前回の Ver.1](https://qiita.com/k1tajima/items/4e7e66980fa5ed83bbda) では、各 Docker コンテナから公開したポートを nginx の転送先に指定してリバースプロキシを実現してみたが、本来は nginx のポートだけを公開して nginx から Docker ホスト内のコンテナに転送すべき。

一つの docker-compose から開始したコンテナ間であれば、ホスト名の代わりにサービス名でもコンテナ名でもアクセスできるのだが、別の docker-compose で開始したコンテナは別ネットワークに分離されて名前解決もされない。

どうするのかと調べてみたら、次の投稿とドキュメントから **user-defined bridge** を利用すればよいことがわかった。

* [docker-compose で別の docker-compose.yml で作ったコンテナとリンクする (ネットワークを繋げる)](https://qiita.com/reneice/items/20e981062b093264cd0a)
* [Differences between user-defined bridges and the default bridge](https://docs.docker.com/network/bridge/#differences-between-user-defined-bridges-and-the-default-bridge)

## ポイントと落とし穴

* user-defined bridge を利用すれば DNS が機能して名前解決もできる。
* `network_mode: bridge` で default bridge の `docker0` に接続しても名前解決ができない。
* nginx のコンフィグでは転送先にコンテナ名とコンテナ側のポート番号を指定。
  ex. `proxy_pass: http://<container_name>:<port_no>`
* 転送先のコンテナでポートを公開する必要はないが、nginx のコンフィグ中はデバッグ用に開けておき、後から非公開にすれば問題切り分けもやりやすい。
* Web アプリに応じて proxy_set_header ほかの調整が必要。
  Mattermost については [こちら](https://docs.mattermost.com/install/config-proxy-nginx.html)を参照。
* 共通の proxy_set_header を server ディレクティブに記述できるが、location ディレクティブ内に proxy_set_header を1行でも追加すると、server ディレクティブに記述されている proxy_set_header は無視され、location ディレクティブ内の proxy_set_header だけが有効になる（つまり、共通設定を無効にしてlocationごとに再定義できる）。

## 設定

### 構成

* Docker 上で nginx と各種サービスを実行
    + [nginx](https://hub.docker.com/_/nginx/)
    + [Mattermost](https://hub.docker.com/r/mattermost/mattermost-preview/)
    + [Knowledge](https://hub.docker.com/r/koda/docker-knowledge/)
    + [Joomla!](https://hub.docker.com/r/bitnami/joomla/)
* [docker-compose](https://docs.docker.com/compose/install/#install-compose)
* OS: Debian

### nginx

* Docker Image - <https://hub.docker.com/_/nginx/>
* conf ファイル設定の基本形は次の通り。

    ```configuration
    server {
      listen       80;
      server_name  docker.example.com;

      proxy_set_header        Host                $host;
      proxy_set_header        X-Real-IP           $remote_addr;
      proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto   $scheme;
      proxy_set_header        X-Frame-Options     SAMEORIGIN;

      location /app_name/ {
        proxy_pass              http://<container_name>:<port_no>/;
      }
    }
    ```

    > 参考：
    > * <http://nginx.org/en/docs/beginners_guide.html#proxy>
    > * <http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass>
    > * <https://docs.docker.com/v17.09/engine/userguide/networking/#default-networks>

* proxy_pass 末尾のスラッシュ `/` 有無による違いを理解しておく。
    > 参考： <https://qiita.com/master-of-sugar/items/6dc7c03a1f600c1ec24a>

* location に正規表現を用いる場合、記述順だけでなく優先順位に留意。
    > 参考： <https://server-setting.info/centos/nginx-location-check.html>

* proxy_pass Host には \$http_host の代わりに \$host を指定
    > 参考： <https://qiita.com/no1zy_sec/items/dfccd91cf76bf73b7754>

* docker-compose.yml の基本形は次の通り。

```configuration
version: '2'
services:
    app:
        image: <container_image[:tag]>
        container_name: <container_name>
        volumes:
            - /path/to/local:/path/to/volume
        networks:
            - app_bridge    # 任意のネットワーク名
        restart: always

networks:
    app_bridge:
        external: true
```

* user-defined bridge を手動で作成しておく。

```
docker network create app_bridge
```

### Mattermost

* Docker Image - <https://hub.docker.com/r/mattermost/mattermost-preview/>
* 次のドキュメントを参考に、proxy_set_header ほかを設定する。
    * <https://docs.mattermost.com/install/config-proxy-nginx.html>

### Knowledge

* Docker Image - <https://hub.docker.com/r/koda/docker-knowledge/>
* (任意) コンテキストパス（正確にはサーブレットパス）付き URL のとするために、アプリケーションのファイル名を変更。
    * /usr/local/tomcat/webapps/ROOT.war -> knowledge.war

    > 参考： <https://qiita.com/nk-tamago/items/52932e2106c9f493754c#起動後のカスタマイズ>

### Joomla!

* Docker Image - <https://hub.docker.com/r/bitnami/joomla/>
* (任意) コンテキストパス付き URL とするために、アプリケーション格納先フォルダを変更。
    * /opt/bitnami/joomla -> /opt/bitnami/joomla/joomla
    
    > 参考： <https://docs.joomla.org/J3.x:Installing_Joomla/ja#Downloading_and_Uploading_Joomla.21_Package_Files>

---

## サンプル: reverseproxy.conf

```configuration

# ref=http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
server {
    listen       80;
    server_name  docker.example.com;

    proxy_set_header        Host                $host;
    proxy_set_header        X-Real-IP           $remote_addr;
    proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto   $scheme;
    proxy_set_header        X-Frame-Options     SAMEORIGIN;

    # ref=https://docs.mattermost.com/install/config-proxy-nginx.html
    location ~ /mattermost/api/v[0-9]+/(users/)?websocket$ {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        client_max_body_size 50M;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Frame-Options SAMEORIGIN;
        proxy_buffers 256 16k;
        proxy_buffer_size 16k;
        client_body_timeout 60;
        send_timeout 300;
        lingering_timeout 5;
        proxy_connect_timeout 90;
        proxy_send_timeout 300;
        proxy_read_timeout 90s;
        proxy_pass http://mattermost:8065;    # V5 でコンテキストパス付き URL 対応の場合
    }

    location /mattermost/ {
        client_max_body_size 50M;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Frame-Options SAMEORIGIN;
        proxy_buffers 256 16k;
        proxy_buffer_size 16k;
        proxy_read_timeout 600s;
        proxy_http_version 1.1;
        proxy_pass http://mattermost:8065;    # V5 でコンテキストパス付き URL 対応の場合    
    }

    location /knowledge/ {
        proxy_pass              http://knowledge:8080/;
      # proxy_pass              http://knowledge:8080;    # コンテキストパス付き URL 対応の場合
    }

    location /joomla/ {
        proxy_pass              http://joomla:8080/;
      # proxy_pass              http://joomla:8080;    # コンテキストパス付き URL 対応の場合
    }

    # ref=http://nginx.org/en/docs/beginners_guide.html
    location / {
        root    /var/www;
    }
}
```