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

Docker SwarmでユーザーのIPアドレスを取得できない問題
Photo by D koi / Unsplash

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

Use swarm mode routing mesh
Use the routing mesh to publish services externally to a swarm

この機能は便利であり、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
Output of docker version: Client: Version: 1.12.0 API version: 1.24 Go version: go1.6.3 Git commit: 8eab29e Built: Thu Jul 28 22:00:36 2016 OS/Arch: linux/amd64 Server: Version: 1.12.0 API version:...

Proxy Protocolのサポートも同様にissueは立ち上がっているものの、すぐに解決される問題ではなさそうです。

Proxy Protocol support in Swarm ingress · Issue #39465 · moby/moby
This issue is closed related to issue - #25526 Swarm ingress should support injecting proxy protocol headers to preserve client information. Proxy protocol is a fully open standard and supported by...

対処法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
Docker swarm daemon that modifies ingress mesh routing to expose true client IPs to service containers - GitHub - newsnowlabs/docker-ingress-routing-daemon: Docker swarm daemon that modifies ingres...