Docker Swarmクラスタ上で、リバースプロキシとしてTraefikコンテナを立て、nodeコンテナでコンテンツを配信する構成で運用しようとしたところ、どうにもREMOTE_ADDRが取得できなくて困りました。
そのときの対処方法をメモしておきます。
Dockerのバージョン:19.03.12
当初のdocker-compose.ymlファイルは以下のとおりです。
version: '3.4'
services:
socket-proxy:
image: tecnativa/docker-socket-proxy
networks:
- internal
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- SERVICES=1
- TASKS=1
- NETWORKS=1
- LOGSPOUT=ignore
deploy:
placement:
constraints:
- node.hostname == manager
traefik:
image: traefik:1.7-alpine
command:
- "--logLevel=error"
- "--entryPoints=Name:http Address::80 Redirect.EntryPoint:https"
- "--entryPoints=Name:https Address::443 TLS"
- "--defaultentrypoints=http,https"
- "--web"
- "--web.address=:8080"
- "--acme"
- "--acme.storage=certs.json"
- "--acme.entrypoint=https"
- "--acme.httpchallenge.entrypoint=http"
- "--acme.onHostRule=true"
- "--acme.email=hashimoto@bagooon.com"
- "--docker"
- "--docker.endpoint=tcp://socket-proxy:2375"
- "--docker.swarmMode"
- "--docker.watch"
ports:
- 80:80
- 443:443
networks:
- internal
- overlay
volumes:
- ./certs/certs.json:/certs.json
deploy:
placement:
constraints:
- node.hostname == web
restart_policy:
condition: on-failure
web:
image: hoge/fuga:dev
networks:
- overlay
deploy:
replicas: 1
placement:
constraints:
- node.hostname == web
labels:
- "traefik.enable=true"
- "traefik.backend=web"
- "traefik.frontend.rule=Host:sample.com"
- "traefik.frontend.entryPoints=https"
- "traefik.frontend.passHostHeader=true"
- "traefik.docker.network=overlay"
- "traefik.protocol=http"
- "traefik.port=3000"
networks:
overlay:
external: true
internal:
internal: true
今回の本題ではありませんが、上記の設定では「docker-socket-proxy」を使って、Traefikのendpointを別のコンテナに割り振っています。一般的にTraefikはマネージャノードで実行し、endpointにdocker.sockを指定して使用するのですが、もしTraefikにセキュリティホールが見つかったりすると、クラスタ全体を乗っ取られてしまう危険性が指摘されています。docker-socket-proxyを使用することで、ワーカーノード上でTraefikを実行でき、さらにdocker-socket-proxyで権限を絞ることでセキュリティを高めた運用が可能になります。
以前あるサイトを運用していて、悪質なアクセスに悩まされた経験がありますので、是非ともREMOTE_ADDRはログとして残しておきたいのですが、どうにも取得できません。
Traefikは、各コンテナで「traefik.frontend.passHostHeader=true」を指定すれば、バックエンドにヘッダー情報等をそのまま送り届けてくれるはずです。User-Agent等は正しく取得できますので、どうやらこの設定は生きているようです。
原因
いろいろ調べていたところ、海外の掲示板で、Docker SwarmのオーバーレイネットワークがREMOTE_ADDRを運ばないらしいという情報を見つけました。そして、Nginxの事例ではありましたが、ポートをhostモードでマウントするといいよ、という解決策も示されていました。
解決策
そこで、Traefikのポートの設定を以下のように書き換えてみました。
ports:
- mode: host
protocol: tcp
published: 80
target: 80
- mode: host
protocol: tcp
published: 443
target: 443
テストしたところ、無事にREMOTE_HOSTを取得できるようになりました。
Nginx等でも同じ対応で解決できるものと思います。
同じ問題でお困りの方がいらっしゃいましたら、是非お試しください。