メインコンテンツまでスキップ

Docker SwarmでユーザーのIPアドレスを取得できない問題

snake

DockerのSwarm modeには、ルーティングメッシュと呼ばれる機能があります。この機能により、外部からのアクセスをロードバランシングすることができます。ルーティングメッシュにより、接続先のノード自身がタスクを実行していなくても、タスクが利用可能なノード上へリクエストを転送してくれるようになります。 Use swarm mode routing mesh

この機能は便利であり、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アドレスを取得できるようになるらしいですが、使ったことがないので詳しいことはよく分かりません。少なくとも、私は複雑になり過ぎるので使おうと思いませんでした。

GitHub - newsnowlabs/docker-ingress-routing-daemon: Docker swarm daemon that modifies ingress mesh routing to expose true client IPs to service containers