コンテナ内のdocker-genからホスト環境のNginxを設定したい


DockerコンテナとしてWebアプリケーションを動かしてNginxをリバースプロキシとして利用する場合， [docker-gen](https://github.com/jwilder/docker-gen) や [nginx-proxy](https://github.com/jwilder/nginx-proxy) で設定を自動化していることが多いと思います．

バーチャルホストの設定を自動的に生成できて便利ですが，Nginxがコンテナではなくホスト環境で動作していると設定の更新が煩雑になりがちで悩ましいです．Nginxもコンテナ化してしまうのも手ですが，歴史的経緯や外的要因で既に動いているNginxをそのまま使いたい場合もあります．

# 暫定の解決方法

コンテナとして起動したdocker-genでNginxの設定ファイルを生成して，ホストのNginxに読み込ませます．

Docker と Nginx と docker-gen の使い方についてはこの文書では触れません．

docker-genでコンテナを監視し，設定をファイルを生成して，NginxのプロセスにSIGHUPを送るだけで良さそうです．([Nginxのドキュメント](https://nginx.org/en/docs/control.html)を見る限り `nginx -s reload` は `kill -HUP` 相当です)

dokcer-genに渡す必要があるもの：

- /var/run/docker.sock (ReadOnly)
- /var/run/nginx.pid (ReadOnly)
- /etc/nginx/conf.d/ (Read/Write)

NginxにSIGHUPを送るために，ホストのPIDと/var/run/nginx.pidにアクセスする必要があるので，`--pid=host`必須な上，`nginx.pid`はファイルが再作成される可能性があるので，ファイル自体ではなく `/var/run` をコンテナに渡す必要があります．nginx.pidを別のディレクトリ移動すればマシになりますが，ホストのNginxはあまり触らない方針．

そもそも論から言えば，docker.sockを渡すのも嫌な感じがしますが，これはdocker-genを使う以上仕方なさそうです．

## docker-gen用のテンプレートとdocker image

- https://github.com/binzume/nginx-conf-gen
- https://hub.docker.com/r/binzume/nginx-conf-gen

`nginx.tmpl`は SynologyのNASを買ったら最初からインストールされてたNginxに80番と443番ポートが握られてて困って書いたやつですが，他の環境でも使えるはずです．

```bash
docker run -d --restart=always --name nginx-conf-gen \
   --pid=host \
   -v /var/run:/tmp/run:ro \
   -v /etc/nginx/conf.d:/tmp/dst \
   binzume/nginx-conf-gen
```

使い方はnginx-proxyに入ってるテンプレートと似た感じですが，HTTP_PATH, HTTP_PROXY_PATH環境変数で同じバーチャルホストで複数アプリケーションを動かせます．

## サンプル出力

```bash
docker run -d --name app01_instance01 -p :8080 -e VIRTUAL_HOST=example.com -e HTTP_PATH=/app01/ -e HTTP_PROXY_PATH=/ mendhak/http-https-echo
docker run -d --name app01_instance02 -p :8080 -e VIRTUAL_HOST=example.com -e HTTP_PATH=/app01/ -e HTTP_PROXY_PATH=/ mendhak/http-https-echo
docker run -d --name app02 -p :8080 -e VIRTUAL_HOST=example.com -e HTTP_PATH=/app02/ -e HTTP_PROXY_PATH=/ mendhak/http-https-echo
```

こんな感じで色々起動すると，以下のようなconfが生成されます．`-p`で指定するホスト側のポートは省略して自動的に割り当てて大丈夫です．

```nginx
upstream example.com.app02. {
        server 0.0.0.0:32780; # app02
}
upstream example.com.app01. {
        server 0.0.0.0:32779; # app01_instance02
        server 0.0.0.0:32778; # app01_instance01
}
server {
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
        proxy_buffering off;
        server_name example.com;
        listen 80;
        listen [::]:80;
        listen 443 ssl;
        listen [::]:443 ssl;
        include /etc/nginx/default.d/*.conf;
        include /etc/nginx/sites-customizations/example.com.*.conf;
        location /app02/ {
                proxy_pass http://example.com.app02./;
                proxy_http_version 1.1;
                proxy_read_timeout 3600s;
                proxy_set_header Host $http_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 Connection "";
        }
        location /app01/ {
                proxy_pass http://example.com.app01./;
                proxy_http_version 1.1;
                proxy_read_timeout 3600s;
                proxy_set_header Host $http_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 Connection "";
        }
}
```

`default.d`と`sites-customizations`下のconfも存在すればincludeするようにして追加の設定を置いたりしています．nginx-conf-gen起動時に `-e HTTP_MODE=redirect` を付けるとHTTPをHTTPSにリダイレクトしたりします．


# 最後に

色々悩ましいので，ベストプラクティス的なものがあれば知りたいです．