柴田さんがUbuntu Weekly RecipeでLXDの記事を何度も書いているのを見て、nginx-proxy環境をLXD上で動かしてみました。nginx-proxyは、他のDockerコンテナを認識して自動でリバースプロキシ設定をしてくれる便利なコンテナです。letsencrypt-nginx-proxy-companionとあわせて使えば、Let’s Encryptの証明書の取得・更新も自動でやってくれます。この組み合わせは、2018年に書いた「Ubuntuスタートアップバイブル」でも紹介しました。
LXD上でnginx-proxyや他のWebアプリ(WordPressやNextcloudなど)を動かすようにすれば、他のサーバーへの引っ越しや日々のバックアップが簡単にできるはずです。
lxcコンテナにDockerをインストールし、nginx-proxyコンテナを動かした後、http・httpsを転送するために、以下のコマンドを実行しました(docker-hostはコンテナ名)。
lxc config device add docker-host http proxy listen=tcp:[::]:80 connect=tcp:127.0.0.1:80 lxc config device add docker-host https proxy listen=tcp:[::]:443 connect=tcp:127.0.0.1:443
IPv6も転送したいので「tcp:[::]」と書きましたが、「tcp:0.0.0.0」と書いてもIPv4・IPv6の両方が転送されました。
設定を確認すると、以下のようになります(この例では、eth0が事前に追加・設定済みでした)。
$ lxc config device show docker-host eth0: ipv4.address: 10.10.10.2 name: eth0 nictype: bridged parent: lxdbr0 type: nic http: connect: tcp:127.0.0.1:80 listen: tcp:[::]:80 type: proxy https: connect: tcp:127.0.0.1:443 listen: tcp:[::]:443 type: proxy
しかし、これだとnginx-proxyのログにクライアントのIPアドレスが出ず、以下のようになってしまいます(IPv6でアクセスした場合も同じ)。
nginx-proxy | nginx.1 | whoami.local 172.19.0.1 - - [19/Aug/2020:15:11:27 +0000] "GET / HTTP/1.1" 200 17 "-" "curl/7.58.0"
「172.19.0.1」は、nginx-proxyのDockerコンテナ自身のIPアドレスです。LXCのproxy deviceがLXCコンテナに転送し、さらにそれをDockerのuserland-proxyがDockerコンテナ上でリバースプロキシとして動くnginxに転送しています。
そこでクライアントのIPアドレスをログに出せないか調べたところ、「Proxy Protocol」という仕組みを使えばできることが分かりました。
まず、以下のコマンドでlxcのプロキシ設定を作りなおしました。
lxc config device remove docker-host http lxc config device remove docker-host https lxc config device add docker-host http proxy listen=tcp:[::]:80 connect=tcp:127.0.0.1:80 proxy_protocol=true lxc config device add docker-host https proxy listen=tcp:[::]:443 connect=tcp:127.0.0.1:443 proxy_protocol=true
これでProxy Protocolで転送されるようになるので、LXCコンテナ内のDockerコンテナ内で動くnginxの設定ファイルにある「Listen」ディレクティブに「proxy_protocol」を追記する必要があります。
nginx-proxyは動的に設定ファイルを生成するのですが、proxy_protocolには対応していなかったので、対応するよう変更を加えました。
変更後のコードは以下のコマンドで取得できます。
git clone -b accept-proxy-protocol https://github.com/jkbys/nginx-proxy.git
この変更により、環境変数に「ACCEPT_PROXY_PROTOCOL=true」を設定することで、すべてのListenディレクティブに「proxy_protocol」が追加されます。さらに、「SET_REAL_IP_FROM」にIPアドレス範囲(,区切りで複数指定可)を設定すれば、「set_real_ip_from アドレス範囲;」と「real_ip_header proxy_protocol;」が追記されます。これで、Proxy Protocolで送られてきたIPアドレスがログに記録されるようになります。
以下は、docker-compose.ymlの記述例です。
version: '3.8' services: nginx-proxy: container_name: nginx-proxy build: nginx-proxy restart: always ports: - "80:80" - "443:443" volumes: - ./_data/nginx/certs:/etc/nginx/certs:ro - ./_data/nginx/conf.d:/etc/nginx/conf.d - ./_data/nginx/vhost.d:/etc/nginx/vhost.d - ./_data/nginx/html:/usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro environment: - ACCEPT_PROXY_PROTOCOL=true - SET_REAL_IP_FROM=172.16.0.0/12 letsencrypt-companion: container_name: letsencrypt-companion image: jrcs/letsencrypt-nginx-proxy-companion restart: always volumes: - ./_data/nginx/certs:/etc/nginx/certs - ./_data/nginx/conf.d:/etc/nginx/conf.d - ./_data/nginx/vhost.d:/etc/nginx/vhost.d - ./_data/nginx/html:/usr/share/nginx/html - /var/run/docker.sock:/var/run/docker.sock:ro environment: - NGINX_PROXY_CONTAINER=nginx-proxy depends_on: - nginx-proxy whoami: container_name: whoami image: jwilder/whoami environment: - VIRTUAL_HOST=whoami.local depends_on: - nginx-proxy
これで、以下のようにクライアントのIPアドレスがログに出力されるようになりました。
nginx-proxy | nginx.1 | whoami.local 10.0.2.2 - - [20/Aug/2020:06:10:32 +0000] "GET / HTTP/1.1" 200 17 "-" "curl/7.58.0" nginx-proxy | nginx.1 | whoami.local fd01:2345:6789::1 - - [20/Aug/2020:06:10:45 +0000] "GET / HTTP/1.1" 200 17 "-" "curl/7.58.0"
しかし、この構成だと「LXDのプロキシ(Proxy Protocol有効)」→「Dockerのプロキシ」→「nginx(リバースプロキシ)」→「各Dockerコンテナで動くWebサーバー」となり、パフォーマンス面を考えるとなんとも気持ち悪いので、結局使わなくなりました。LXDやDockerのプロキシを使わずにクライアントのIPアドレスをログに残す方法は、違うノートにまとめたいと思います。
コメント