【问题标题】:How to get client IP from request inside haproxy docker container?如何从 haproxy docker 容器内的请求中获取客户端 IP?
【发布时间】:2016-07-14 00:28:55
【问题描述】:

我正在尝试从我的 nodejs 服务器中的请求对象中获取客户端的 IP 地址。

我的技术结构是: 我运行两个 docker 容器。一个用于haproxy,另一个用于nodejs,它使用expressjs 框架。所有传入流量首先由我用于代理和负载平衡的 haproxy 接收。 Haproxy 根据配置文件中的ACLs 将请求转发到适当的后端。

我尝试在我的 nodejs 中访问 x-forwarded-for 请求标头,但它只返回了 docker 网络网关接口 172.17.0.1 的 IP。

转到haproxy 配置并在defaults 块中使用option forwardfor header X-Client-IP 还将x-client-ip 标头设置为docker 网络网关接口ip。调试日志也记录了相同的 ip。

所以这就是问题所在。由于haproxy 在容器内运行,它认为 docker 网络网关接口是客户端。

我如何才能将实际客户端的 IP 获取到容器内的 haproxy 以便它可以将其转发给 nodejs?

这是我的haproxy 配置文件:

global
    debug
    maxconn 4096

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    timeout http-keep-alive 50000ms
    option http-keep-alive
    option http-server-close
    option forwardfor header X-Client-IP

frontend http-in
    bind *:80
    acl is_api hdr_end(host) -i api.3dphy-dev.com

    use_backend api if is_api

    default_backend default

backend default
    server s0 "${DOCKER_INTERFACE_IP}:3000"

backend api
    balance leastconn
    option httpclose
    option forwardfor
    server s1 "${DOCKER_INTERFACE_IP}:17884"

我使用以下方式运行我的 haproxy 容器:

docker run -d --name haproxy_1 -p 80:80 -e DOCKER_INTERFACE_IP=`ifconfig docker0 | grep -oP 'inet addr:\K\S+'` -v $(pwd)/config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro haproxy:1.6

注意:我没有使用任何防火墙。另外,请随时建议对我的配置进行任何改进。 Keep-alive 也被证明是一个问题。

【问题讨论】:

  • 我花了几天时间来完成这项工作。我已经看到了恐怖......请参阅下面的答案。我也有一个可用的 haproxy:1.6 版本,但这不会是这里的问题。

标签: node.js networking docker haproxy


【解决方案1】:

翻遍了docker论坛,终于找到了解决办法。

解决方案是一个两步过程。

首先我需要将我的haproxy 配置更新为:

global
    debug
    maxconn 4096

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    timeout http-keep-alive 50000ms
    option http-keep-alive
    option http-server-close

frontend http-in
    bind *:80
    option forwardfor
    acl is_site hdr_end(host) -i surenderthakran-dev.com

    use_backend site if is_site

    default_backend default

backend default
    server s0 "${DOCKER_INTERFACE_IP}:3000"

backend site
    balance leastconn
    option httpclose
    option forwardfor
    server s1 "${DOCKER_INTERFACE_IP}:17884"

注意option forwardforfrontend http-in 块中的添加。这告诉 haproxy 的前端部分将客户端 IP 添加到请求标头中。

其次,docker run 命令应该更新为:

docker run -d --name haproxy_1 -p 80:80 -e DOCKER_INTERFACE_IP=`ifconfig docker0 | grep -oP 'inet addr:\K\S+'` -v $(pwd)/config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro --net=host haproxy:1.6

注意在 docker run 命令中添加了--net=host 选项。它告诉 docker 启动新容器并使用与主机相同的网卡。

现在原始客户端 IP 已添加到请求标头中,并且可以在请求转发到的任何应用程序中的 x-forwarded-for 请求标头中访问。

【讨论】:

  • 感谢您的解决方案。请注意,如果您使用主机网络,则不需要 -p 80:80 选项。根据docs.docker.com/network/host 使用主机模式网络时,... 和 -p, ... 被忽略,而是产生警告:此外,如果您使用 docker service --net=host 应该替换为 --network host
【解决方案2】:

这不可能是 haproxy 的工作方式,因为当您在启动时不连接主机时它会不断抛出异常,因为它需要完全解析地址。我尝试了很多解决方法(也许有可能),但我放弃了并使用docker-compose

我在前面的帖子中发布了一个运行示例,可能会有所帮助。

要点是将容器与实际已经存在的主机链接。这是通过 docker 链接完成的。

docker-compose.yml

api1:
  build: .
  dockerfile: ./Dockerfile
  ports:
    - 3955
  links:
    - mongo
    - redis
  environment:
    - REDIS_HOST=redis
    - MONGO_HOST=mongo
    - IS_TEST=true
  command: "node app.js"

api2:
  build: .
  dockerfile: ./Dockerfile
  ports:
    - 3955
  links:
    - mongo
    - redis
  environment:
    - REDIS_HOST=redis
    - MONGO_HOST=mongo
    - IS_TEST=true
  command: "node app.js"

mongo:
  image: mongo
  ports:
    - "27017:27017"
  command: "--smallfiles --logpath=/dev/null"

redis:
   image: redis
   ports:
     - "6379:6379"

haproxy:
  image: haproxy:1.5
  volumes:
     - ./cluster:/usr/local/etc/haproxy/
  links:
    - "api1"
    - "api2"
  ports:
    - 80:80
    - 70:70
  expose:
    - "80"
    - "70"

haproxy.cfg

global
  log 127.0.0.1 local0
  log 127.0.0.1 local1 notice

defaults
  log global
  mode http
  option httplog
  option dontlognull
  timeout connect 5000
  timeout client 10000
  timeout server 10000

listen stats :70
  stats enable
  stats uri /

frontend balancer
  bind 0.0.0.0:80
  mode http
  default_backend aj_backends

backend aj_backends
  mode http
  balance roundrobin
  option forwardfor
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request add-header X-Forwarded-Proto https if { ssl_fc }
  option httpchk HEAD / HTTP/1.1\r\nHost:localhost
  default-server inter 3s fall 5
  server api1 api1:3955
  server api2 api2:3955

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多