Docker Swarm でユーザーの IP アドレスを取得できない問題
Docker の Swarm mode には、ルーティングメッシュと呼ばれる機能があります。この機能により、外部からのアクセスをロードバランシングすることができます。ルーティングメッシュにより、接続先のノード自身がタスクを実行していなくても、タスクが利用可能なノード上へリクエストを転送してくれるようになります。
この機能は便利であり、Swarm を冗長構成にするのであれば、必要不可欠な機能の一つです。ただし、ルーティングメッシュが有効になっている場合、アクセス元の IP アドレスを取得できない可能性がある問題が存在しています。正確には取得できますが、接続元のIPアドレスではなく、アクセスを受け付けたノードの IP アドレスが取得されます。
最初に提示した画像を元に説明を行います。
仮に 203.0.113.10
から Node-03(192.168.10.102:8080
)へアクセスがあったとします。Node-03 上に利用可能なタスクがないため、全てのリクエストは利用可能なタスクの存在する異なるノードへ転送されます。そのため Node-01 もしくは Node-02 の nginx.1 がアクセスを受けます。この時、アクセスを受けた nginx.1 でアクセスログを確認すると、接続元の IP アドレスは Node-03 の IP アドレスである 192.168.10.102
が記録されています。
という訳で、ルーティングメッシュによりリクエストが転送された場合、転送元のノードの IP アドレスが記録されてしまいます。
一般的に元のIPアドレスを取得場合、http では X-Forwarded-For
と呼ばれるプロキシを通した際に元の IP アドレスを保持する仕組みを利用し、L4 では Proxy Protocol
と呼ばれるロードバランサを通した際に元の IP アドレスを保持する仕組みを利用します。
しかしながら、ルーティングメッシュによって転送されたリクエストは、X-Forwarded-For や Proxy Protocol が対応してないんですね。類する代替手段が用意されている訳でもないため、Docker Swarm のみで元の IP を取得することは不可能です。
この問題はすでに把握されており、issue は立ち上げられています。しかし、2016年から開かれ続けており、すぐに解決される問題ではない気がします。
Unable to retrieve user’s IP address in docker swarm mode · Issue #25526 · moby/moby
Proxy Protocol のサポートも同様に issue は立ち上がっているものの、すぐに解決される問題ではなさそうです。
Proxy Protocol support in Swarm ingress · Issue #39465 · moby/moby
対処法1
この問題に対する対処法として、ルーティングメッシュの無効化があります。しかし、Overlay Network の利点を失うことになる点は注意してください。
Docker-compose の場合、long syntax でポートを以下のように指定することでルーティングメッシュを無効化することができます。
- target: 80
published: 80
protocol: tcp
mode: host
- target: 53
published: 53
protocol: udp
mode: host
mode
を省略した場合、もしくは、ingress
を設定した場合はルーティングメッシュが有効になります。
Docker Swarm を使う意味がよく分からなくなりますが、クラスタの外側に HAProxy のようなロードバランサを設置してあげることで、ルーティングメッシュみたいなことはできるようになります。
対処法2
docker-ingress-routing-daemon なるものを利用することで、元のIPアドレスを取得できるようになるらしいですが、使ったことがないので詳しいことはよく分かりません。少なくとも、私は複雑になり過ぎるので使おうと思いませんでした。