Moby: 无法在 docker swarm 模式下检索用户的 IP 地址

创建于 2016-08-09  ·  324评论  ·  资料来源: moby/moby

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:  1.24
 Go version:   go1.6.3
 Git commit:   8eab29e
 Built:        Thu Jul 28 22:00:36 2016
 OS/Arch:      linux/amd64

docker info

Containers: 155
 Running: 65
 Paused: 0
 Stopped: 90
Images: 57
Server Version: 1.12.0
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 868
 Dirperm1 Supported: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: host overlay null bridge
Swarm: active
 NodeID: 0ddz27v59pwh2g5rr1k32d9bv
 Is Manager: true
 ClusterID: 32c5sn0lgxoq9gsl1er0aucsr
 Managers: 1
 Nodes: 1
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot interval: 10000
  Heartbeat tick: 1
  Election tick: 3
 Dispatcher:
  Heartbeat period: 5 seconds
 CA configuration:
  Expiry duration: 3 months
 Node Address: 172.31.24.209
Runtimes: runc
Default Runtime: runc
Security Options: apparmor
Kernel Version: 3.13.0-92-generic
Operating System: Ubuntu 14.04.4 LTS
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 31.42 GiB
Name: ip-172-31-24-209
ID: 4LDN:RTAI:5KG5:KHR2:RD4D:MV5P:DEXQ:G5RE:AZBQ:OPQJ:N4DK:WCQQ
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Username: panj
Registry: https://index.docker.io/v1/
WARNING: No swap limit support
Insecure Registries:
 127.0.0.0/8

其他环境详细信息(AWS、VirtualBox、物理等):

重现问题的步骤:

  1. 运行以下发布端口 80 的服务
docker service create \
--name debugging-simple-server \
--publish 80:3000 \
panj/debugging-simple-server
  1. 尝试连接http://<public-ip>/

描述您收到的结果:
ipheader.x-forwarded-for都不是正确的用户 IP 地址。

描述您期望的结果:
ipheader.x-forwarded-for应该是用户的 IP 地址。 可以使用独立的 docker 容器docker run -d -p 80:3000 panj/debugging-simple-server来归档预期的结果。 您可以通过以下链接查看两个结果,
http://swarm.issue-25526.docker.takemetour.com :81/
http://container.issue-25526.docker.takemetour.com :82/

您认为重要的其他信息(例如问题仅偶尔发生):
这发生在global模式和replicated模式中。

我不确定我是否错过了可以轻松解决此问题的任何内容。

与此同时,我想我必须做一个解决方法,在 swarm 模式之外运行一个代理容器,并让它以 swarm 模式转发到已发布的端口(SSL 终止也应该在这个容器上完成),这打破了 swarm 的目的自我修复和编排模式。

arenetworking areswarm kinenhancement statuneeds-attention versio1.12

最有用的评论

在尝试以群模式运行 logstash(用于从各种主机收集 syslog 消息)时,我也遇到了这个问题。 logstash“主机”字段始终显示为 10.255.0.x,而不是连接主机的实际 IP。 这使它完全无法使用,因为您无法分辨日志消息来自哪个主机。 有什么办法可以避免翻译源IP吗?

所有324条评论

/ cc @aluzzardi @mrjana

@PanJ你能分享一些关于调试简单服务器如何确定ip细节吗? 此外,如果一项服务在多个主机(或全局模式)上扩展到 1 个以上的副本,那么期望是什么?

@mavenugo这是koa的请求对象,它使用来自net模块的节点的remoteAddress 。 对于可以检索远程地址的任何其他库,结果应该相同。

期望是ip字段应该始终是远程地址,而不管任何配置。

@PanJ你还在使用你的解决方法还是找到了一些更好的解决方案?

@PanJ当我将您的应用程序作为独立容器运行时..

docker run -it --rm -p 80:3000 --name test panj/debugging-simple-server

并从另一台主机访问已发布的端口,我得到了这个

vagrant@net-1:~$ curl 192.168.33.12
{"method":"GET","url":"/","header":{"user-agent":"curl/7.38.0","host":"192.168.33.12","accept":"*/*"},"ip":"::ffff:192.168.33.11","ips":[]}
vagrant@net-1:~$

192.168.33.11 是我运行 curl 的主机的 IP。 这是预期的行为吗?

@sanimej是的,这也是群模式下的预期行为。

@marech我仍在使用独立容器作为解决方法,效果很好。

就我而言,有 2 个 nginx 实例,独立实例和群实例。 SSL 终止和反向代理是在独立的 nginx 上完成的。 Swarm 实例用于根据请求主机路由到其他服务。

@PanJ在 swarm 模式下访问容器的已发布端口的方式是不同的。 在 swarm 模式下,可以从集群中的任何节点访问服务。 为了促进这一点,我们通过ingress网络进行路由。 10.255.0.x是群集中主机上ingress网络接口的地址,您尝试从中访问已发布的端口。

@sanimej当我深入研究这个问题时,我有点看到它是如何工作的。 但是用例(检索用户 IP 的能力)很常见。

我对如何实施修复程序的了解有限。 也许是一种不会改变源 IP 地址的特殊类型的网络?

Rancher 类似于 Docker swarm 模式,它似乎具有预期的行为。 也许这是一个很好的起点。

@sanimej好主意,如果可能的话,可以将所有 IP 添加到 X-Forwarded-For 标头,然后我们可以看到所有链。

@PanJ嗯,以及您的 nignx 独立容器如何通过服务名称或 ip 与 swarm 实例通信? 也许可以共享 nginx 配置部分,将其传递给 swarm 实例。

@marech独立容器侦听端口80 ,然后代理到localhost:8181

server {
  listen 80 default_server;
  location / {
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_pass          http://localhost:8181;
    proxy_read_timeout  90;
  }
}

如果您必须执行 SSL 终止,请添加另一个侦听端口443服务器块,然后执行 SSL 终止并代理到localhost:8181

Swarm 模式的 nginx 发布8181:80并根据请求主机路由到另一个服务。

server {
  listen 80;
  server_name your.domain.com;
  location / {
    proxy_pass          http://your-service:80;
    proxy_set_header Host $host;
    proxy_read_timeout  90;
  }
}

server {
  listen 80;
  server_name another.domain.com;
  location / {
    proxy_pass          http://another-service:80;
    proxy_set_header Host $host;
    proxy_read_timeout  90;
  }
}

在我们的例子中,我们的 API RateLimit 和其他函数取决于用户的 ip 地址。 有什么办法可以跳过群模式下的问题吗?

在尝试以群模式运行 logstash(用于从各种主机收集 syslog 消息)时,我也遇到了这个问题。 logstash“主机”字段始终显示为 10.255.0.x,而不是连接主机的实际 IP。 这使它完全无法使用,因为您无法分辨日志消息来自哪个主机。 有什么办法可以避免翻译源IP吗?

+1 以获得此问题的解决方案。

由于无法检索用户的 IP,我们无法使用 Prometheus 等监控解决方案。

也许 linux 内核 IPVS 功能在这里会有用。 我猜测 IP 更改正在发生,因为连接是在用户空间中代理的。 另一方面,IPVS 可以在内核空间中重定向和负载平衡请求,而无需更改源 IP 地址。 IPVS 也可以很好地构建更高级的功能,例如不同的负载平衡算法、浮动 IP 地址和直接路由。

对我来说,如果我能以某种方式找出虚拟 IP 和端点所属服务器的 IP 之间的关系就足够了。 这样,当 Prometheus 发送与某个虚拟 IP 相关的警报时,我可以找出受影响的服务器。 这不是一个好的解决方案,但总比没有好。

@vfarcic我认为现在的工作方式是不可能的。 所有客户端连接都来自同一个 IP,因此您无法将其转换回。 唯一可行的方法是,如果连接的代理/nat 保存了带有时间戳、源 ip 和源端口的连接日志。 即便如此,在大多数需要源 IP 的用例中也无济于事。

我可能没有很好地解释用例。

我使用配置为废弃作为 Swarm 全球服务运行的导出器的 Prometheus。 它使用任务。获取所有副本的IP。 因此,它不使用服务,而是使用副本端点(无负载平衡)。 我需要以某种方式找出每个副本 IP 来自的节点的 IP。

我刚刚意识到“docker网络检查" 提供有关单个节点的容器和 IPv4 地址的信息。这是否可以扩展,以便与节点一起存在网络的集群范围信息?

就像是:

       "Containers": {
            "57bc4f3d826d4955deb32c3b71550473e55139a86bef7d5e584786a3a5fa6f37": {
                "Name": "cadvisor.0.8d1s6qb63xdir22xyhrcjhgsa",
                "EndpointID": "084a032fcd404ae1b51f33f07ffb2df9c1f9ec18276d2f414c2b453fc8e85576",
                "MacAddress": "02:42:0a:00:00:1e",
                "IPv4Address": "10.0.0.30/24",
                "IPv6Address": "",
                "Node": "swarm-4"
            },
...

注意添加了“节点”。

如果这些信息可用于整个集群,而不仅仅是添加--filter参数的单个节点,我将拥有找出容器 IPv4 地址和节点。 这不会是一个很好的解决方案,但总比没有好。 现在,当 Prometheus 检测到问题时,我需要在每个节点上执行“docker network inspect”,直到我找到地址的位置。

我同意@dack ,鉴于入口网络正在使用 IPVS,我们应该使用 IPVS 解决这个问题,以便保留源 IP 并正确透明地呈​​现给服务。

该解决方案需要在 IP 级别工作,以便任何不基于 HTTP 的服务仍然可以正常工作(不能依赖 http 标头...)。

我不能强调这有多重要,没有它,有许多服务根本无法在群模式下运行。

这就是 HaProxy 解决这个问题的方式: http ://blog.haproxy.com/2012/06/05/preserve-source-ip-address-despite-reverse-proxies/

考虑到他在 DockerCon 上关于 IPVS 的演讲,

只是将我自己添加到列表中。 我正在使用 logstash 来接受 syslog 消息,并且它们都被推送到 elasticsearch 中,主机 IP 设置为 10.255.0.4,这使其无法使用,我将不得不恢复到我的非容器化 logstash 部署如果没有解决这个问题。

@mrjana 你能补充一下你必须解决这个问题的建议吗?

IPVS 不是用户空间反向代理,它可以修复 HTTP 层中的问题。 这就是像 HAProxy 这样的用户空间代理和 this 之间的区别。 如果您想使用 HAProxy,您可以通过在集群中放置一个 HAProxy 并让您的所有服务实例和 HAProxy 参与同一个网络来实现。 这样 HAProxy 就可以修复 HTTP header.x-forwarded-for 。 或者,如果 L7 负载均衡器在集群外部,您可以将即将推出的(1.13 版)功能用于名为Host PublishMode 的新PublishMode功能,该功能将公开服务的每个单独实例在它自己的单独端口中,您可以将外部负载均衡器指向该端口。

@mrjana使用 IPVS(而不是事情)的整个想法是避免首先转换源 IP。 添加 X-Forwarded-For 可能对某些 HTTP 应用程序有所帮​​助,但对于所有其他被当前行为破坏的应用程序毫无用处。

@dack我的理解是 Docker 入口网络已经在使用 IPVS。

如果您想使用 HAProxy,您可以通过在集群中放置一个 HAProxy 并让您的所有服务实例和 HAProxy 参与同一个网络来实现。 这样 HAProxy 就可以修复 HTTP header.x-forwarded-for

@mrjanadocker run 在入口网络之外运行或直接在主机上运行,​​但随后您无法使用任何服务,因为它们位于不同的网络上并且你不能访问它们。

简单地说,据我所知,一旦您使用 docker 服务和 swarm 模式,就绝对没有办法处理这个问题。

如果 docker ingress 网络的作者可以加入讨论会很有趣,因为他们可能会对 IPVS 在引擎盖下的配置/操作方式有一些了解(IPVS 有多种模式)以及我们如何修复问题。

@tlvenn你知道这是在源代码中的什么地方吗? 我可能是错的,但我不相信它根据我观察到的一些事情使用 IPVS:

  • 源端口已转换(此问题的全部原因)。 IPVS 不会这样做。 即使在 NAT 模式下,它也只转换目标地址。 您需要使用默认路由或策略路由将返回数据包发送回 IPVS 主机。
  • 当一个端口以 swarm 模式发布时,swarm 中的所有 dockerd 实例都会监听发布的端口。 如果使用 IPVS,那么它会发生在内核空间中,并且 dockerd 不会监听端口。

@dack

从他们的博客:

在内部,我们使用 Linux IPVS 来完成这项工作,这是一个内核中的第 4 层多协议负载平衡器,它已在 Linux 内核中使用超过 15 年。 通过内核内部的 IPVS 路由数据包,swarm 的路由网格提供高性能的容器感知负载平衡。

如果我没记错的话,代码源应该存在于 swarmkit 项目中。

我想知道@stevvooe 是否可以帮助我们了解这里的潜在问题。

好的,我已经简要浏览了代码,我想我现在对它有了更好的理解。 正如博客中所述,它确实似乎正在使用 IPVS。 SNAT 是通过在 service_linux.go 中设置的 iptables 规则完成的。 如果我理解正确,它背后的逻辑将是这样的(假设节点 A 收到一个客户端数据包,用于在节点 B 上运行的服务):

  • Swarm 节点 A 接收客户端数据包。 IPVS/iptables 转换 (src ip)->(node a ip) 和 (dst ip)->(node B ip)
  • 数据包转发到节点B
  • 节点 B 将它的回复发送给节点 A(因为这就是它所看到的 src ip)
  • 节点 A 将 src 和 dst 转换回原始值并将回复转发给客户端

我认为 SNAT 背后的原因是回复必须通过原始请求通过的同一节点(因为这是存储 NAT/IPVS 状态的地方)。 由于请求可能来自任何节点,因此使用 SNAT 以便服务节点知道将请求路由回哪个节点。 在具有单个负载平衡节点的 IPVS 设置中,这不是问题。

因此,问题是如何在仍然允许所有节点处理传入客户端请求的同时避免 SNAT。 我不完全确定最好的方法是什么。 也许有一种方法可以在服务节点上有一个状态表,以便它可以使用策略路由来直接回复,而不是依赖 SNAT。 或者某种封装可能会有所帮助(VXLAN?)。 或者,可以使用 IPVS 的直接路由方法。 这将允许服务节点直接回复客户端(而不是通过接收原始请求的节点),并允许为服务添加新的浮动 IP。 但是,这也意味着只能通过浮动 IP 而不是单个节点 IP 来联系服务(不确定这对任何用例是否有问题)。

非常有趣的发现@dack

希望能找到一个解决方案来一起跳过那个 SNAT。

与此同时,可能有一个不久前已经提交的解决方法,它引入了一个带有PublishMode的主机级端口发布,有效地绕过了入口网络。

https://github.com/docker/swarmkit/pull/1645

嘿,感谢您的大量反馈 - 我们将在周末后深入研究这个问题。

在此期间的一些信息:

@tlvenn@mrjana是入口网络功能背后的主要作者。 Source 主要存在于 docker/libnetwork 中,一些存在于 SwarmKit 中

@dack :它确实得到了 IPVS 的支持

@tlvenn据我所知,Docker Swarm 使用伪装,因为它是最直接的方式并且保证在大多数配置中工作。 此外,这是唯一允许伪装端口的模式 [re: @dack],这很方便。 理论上,这个问题可以通过使用IPIP封装的方式来解决——数据包流是这样的:

  • 一个数据包到达网关服务器——在我们的例子中是群的任何节点——并且该节点上的 IPVS 根据其目标 IP 地址和端口确定它实际上是一个虚拟服务的数据包。
  • 数据包被封装到另一个 IP 数据包中,并发送到基于负载平衡算法选择的真实服务器。
  • 真实服务器接收封闭的数据包,对其进行解封装,并将真实客户端 IP视为源,将虚拟服务 IP视为目的地。 所有真实的服务器都应该有一个带有虚拟服务 IP 的非 ARPable 接口别名,这样他们就会假设这个数据包实际上是发给他们的。
  • 真实服务器处理数据包并将响应直接发送回客户端。 在这种情况下,源 IP 将是虚拟服务 IP ,因此不涉及火星回复,这很好。

当然,有许多警告和可能出错的事情,但通常这是可能的,并且 IPIP 模式在生产中被广泛使用。

希望尽快找到解决方案,因为 IP 固定和其他安全检查需要能够接收正确的外部 IP。

观看。 我们的产品利用源 IP 信息进行安全和分析。

@aluzardi 有什么更新吗?

凹凸,我们需要这个来为我们明年年初开始的一个非常大的项目工作。

检查流程,它目前似乎是这样工作的(在这个例子中,节点 A 接收传入的流量,节点 B 正在运行服务容器):

  • 节点 A 执行 DNAT 将数据包定向到 ingress_sbox 网络命名空间(/var/run/docker/netns/ingress_sbox)
  • 节点 A 上的 ingress_sbox 在 NAT 模式下运行 IPVS,它执行 DNAT 将数据包定向到节点 B 上的容器(通过入口覆盖网络)以及 SNAT 将源 IP 更改为节点 A 入口覆盖网络 IP
  • 数据包通过覆盖路由到真实服务器
  • 返回数据包反向遵循相同的路径,将源/目标地址重写回原始值

我认为可以通过以下方式避免 SNAT:

  • 节点 A 在没有任何 NAT 的情况下将数据包传递到 ingress_sbox(iptables/策略路由?)
  • 节点 A ingress_sbox 以直接路由模式运行 IPVS,通过入口覆盖网络向节点 B 发送数据包
  • 节点 B 上的容器接收未更改的数据包(容器必须接受所有公共 IP 的数据包,但不为它们发送 ARP。有几种方法可以做到这一点,请参阅 IPVS 文档)。
  • 返回数据包直接从节点 B 发送到客户端(不需要通过覆盖网络或节点 A 返回)

作为额外的好处,不需要存储 NAT 状态并且减少了覆盖网络流量。

@aluzsardi @mrjana请问有什么更新吗? 来自 Docker 的一点反馈将不胜感激。

观看。 没有源IP信息,我们的大部分服务都无法按预期工作

那是怎么发生的 ?
unassign_bug

@tlvenn似乎是 Github 中的一个错误?

@PanJ @tlvenn @vfarcic @dack和其他人,PTAL #27917。 我们引入了启用服务发布模式 = host ,这将为服务提供一种绕过 IPVS 并恢复docker run -p类似行为的方法,并且将保留源 IP 的情况需要它。

请尝试 1.13.0-rc2 并提供反馈。

你很奇怪@mavenugo ..

关于发布模式,我已经从上面的 swarm kit 中链接了它,这可能是一种解决方法,但我真的希望 Docker 1.13 提供一个适当的解决方案来永久解决这个问题。

这个问题在很大程度上可以归类为一个错误,因为保留源 ip 是我们作为用户所期望的行为,并且它现在是 docker 服务的一个非常严重的限制。

我相信@kobolog@dack都已经就如何解决这个问题提出了一些潜在的线索,而且已经将近 2 周没有 Docker 方面的跟进。

我们能否对谁在 Docker 上调查这个问题和状态更新有一些了解? 提前致谢。

除了#27917,1.13 没有其他解决方案。 直接返回功能需要针对各种用例进行分析,不应轻易将其视为错误修复。 我们可以在 1.14 中查看此内容。 但是,这也属于可配置 LB 行为的范畴,包括算法(rr 与其他 10 种方法)、数据路径(LVS-DR、LVS-NAT 和 LVS-TUN)。 如果有人愿意为此做出贡献,请推动 PR,我们可以让它动起来。

很公平,我猜@mavenugo考虑到我们现在有一个替代方案。

至少,我们是否可以修改 1.13 的文档,以便明确说明在使用具有默认入口发布模式的 docker 服务时,源 ip 不会保留,并提示使用主机模式(如果这是运行服务的要求) ?

我认为这将帮助正在迁移到服务的人们不会被这种意外行为所困扰。

当然,是的,指示此行为的文档更新以及使用发布mode=host的解决方法对于在 LVS-NAT 模式下失败的此类用例非常有用。

只是回来看看有没有新的进展来弄清楚这个真实的事情? 这对我们来说当然也是一个巨大的限制

docker 1.14 的路线图上有解决方案吗? 部分原因是由于这个问题,我们使用 docker 部署了我们的解决方案。

希望看到添加到保留客户端 IP 的 http/https 请求的自定义标头。 这应该是可能的,不是吗? 我不介意 X_Forwarded_for 何时被覆盖,我只想有一个自定义字段,该字段仅在请求第一次进入群时设置。

希望看到添加到保留客户端 IP 的 http/https 请求的自定义标头。 这应该是可能的,不是吗? 我不介意 X_Forwarded_for 何时被覆盖,我只想有一个自定义字段,该字段仅在请求第一次进入群时设置。

负载均衡在 L3/4 完成。 添加 http 标头是不可能的。

修复将涉及删除源地址的重写。

@mavenugo我今天更新到mode=host 。 目前它可以工作,客户端 IP 被保留,但我希望有更好的解决方案 :) 感谢您的工作!

对不起,双重发布...
如何使用堆栈文件 (yml v3) 获得与通过 docker service create 使用--publish mode=host,target=80,published=80时相同的行为?

我试过

...
services:
  proxy:
    image: vfarcic/docker-flow-proxy:1.166
    ports:
      - "80:80/host"
      - "443:443/host" 
...

但这不起作用(使用与 https://docs.docker.com/docker-cloud/apps/stack-yaml-reference/#/ports 中相同的模式)

如何使用堆栈文件 (yml v3) 获得与通过 docker service create 使用 --publish mode=host,target=80,published=80 时相同的行为?

@hamburml - 密切关注https://github.com/docker/docker/issues/30447,这是一个开放的问题/功能。

不幸的是,我不能使用mode=host作为解决方法,因为我需要我的服务与 swarm 网络进行通信,并且还要监听所有节点,而不仅仅是主机接口......

@tkeeler33我认为您应该能够将该服务部署为global服务(在集群中的每个节点上部署一个实例),并将其连接到集群网络以与集群中的其他服务进行通信

@thaJeztah - 是的,但我无法将容器同时连接到覆盖/群网络和主机mode=host 。 这是我目前最大的限制。

@tkeeler33似乎对我

$ docker network create -d overlay swarm-net

$ docker service create \
  --name web \
  --publish mode=host,published=80,target=80 \
  --network swarm-net \
  --mode=global \
  nginx:alpine

$ docker service create --name something --network swarm-net nginx:alpine

测试web服务是否能够与同一网络的something服务连接;

docker exec -it web.xczrerg6yca1f8ruext0br2ow.kv8iqp0wdzj3bw7325j9lw8qe sh -c 'ping -c3 -w1 something'
PING something (10.0.0.4): 56 data bytes
64 bytes from 10.0.0.4: seq=0 ttl=64 time=0.251 ms

--- something ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.251/0.251/0.251 ms

@thaJeztah - 谢谢! 在深入挖掘之后,我意识到我的问题是我使用--opt encrypted选项创建了我的 docker 网络,这导致容器无法与主机连接。 一旦我尝试了您的步骤,我就能够迅速缩小根本原因的范围。 这个选项可能是一个很好的临时解决方法,我只需要考虑任何安全问题。

非常感谢信息!

@tkeeler33 --opt encrypted不应影响主机端口映射。 加密选项的唯一目的是加密节点之间的 vxlan 隧道流量。 来自文档:“如果您计划创建一个加密的覆盖网络(--opt 加密),您还需要确保允许协议 50 (ESP) 流量。” 您能检查一下您的配置以确保允许使用 ESP 吗?
此外, --opt encrypted选项是纯粹的数据平面加密。 即使没有选项,所有控制平面流量(路由交换、服务发现分发等)都默认加密。

@mavenugo你说得对。 当我用--opt encrypted创建一个新网络时,它运行良好。 当我将新创建的网络与现有网络进行比较时,我注意到"Internal": true已设置。 这可能是问题所在,并且在最初的网络创建过程中出现了错误...感谢您的帮助和澄清,这是漫长的一天...

@dack @kobolog在 LVS-Tunnel 和 LVS-DR 模式的典型部署中,传入数据包中的目标 IP 将是服务 VIP,它也被编程为真实服务器中的非 ARP IP。 路由网格的工作方式完全不同,传入的请求可以发送到任何主机。 为了让真实服务器接受数据包(在任何 LVS 模式下),目标 IP 必须更改为本地 IP。 来自后端容器的回复数据包无法返回正确的源地址。 我们可以尝试将回复数据包返回到入口主机,而不是直接返回。 但是,除了更改源 IP 使我们回到原点之外,没有什么干净的方法可以做到这一点。

@thaJeztah我认为我们应该在文档中澄清这一点,如果必须保留客户端 IP,建议使用主机 mod 并关闭此问题。

@sanimej我仍然不明白为什么没有 NAT 就不可能做到这一点。 例如,我们不能选择使用常规的 LVS-DR 流程吗? Docker 将非 arp vip 添加到相应的节点,LVS 将传入的数据包定向到节点,传出的数据包直接返回。 为什么传入的数据包可以击中任何主机很重要? 这与具有多个前端和多个后端服务器的标准 LVS 没有什么不同。

@thaJeztah感谢您的解决方法:)
如果您使用 compose 版本 3 部署代理,则不支持新发布语法,因此我们可以使用此命令修补已部署的服务(将nginx_proxy替换

docker service update nginx_proxy \
    --publish-rm 80 \
    --publish-add "mode=host,published=80,target=80" \
    --publish-rm 443 \
    --publish-add "mode=host,published=443,target=443"

@dack在常规 LVS-DR 流中,目标 IP 将是服务 VIP。 因此,LB 可以将数据包发送到后端,而无需更改任何目标 IP。 路由网格不是这种情况,因为传入数据包的目标 IP 将是主机的 IP 之一。

@sanimej上述使用 IPIP 封装模式解决此问题的建议有任何反馈吗?

@tlvenn LVS-IP 隧道的工作方式与 LVS-DR 非常相似,不同之处在于后端通过 IP 隧道中的 IP 而不是 mac-rewrite 获取数据包。 所以它对于路由网格用例有同样的问题。

从你提到的提案中..
The real server receives the enclosing packet, decapsulates it and sees real client IP as source and virtual service IP as destination.

数据包的目标 IP 将是客户端将数据包发送到的主机的 IP,而不是 VIP。 如果不重写,真实服务器将在删除外部 IP 标头后丢弃它。 如果目的IP被改写,真实服务器对客户端的回复就会有不正确的Source IP,导致连接失败。

感谢@sanimej 的澄清。 你能不能实现代理协议? 它不会提供无缝解决方案,但至少会为服务提供解决用户 IP 的解决方案。

有一种笨拙的方法可以通过将源端口范围拆分为块并为集群中的每个主机分配一个块来实现源 IP 保留。 然后可以采用混合 NAT+DR 方法,其中入口主机执行通常的 SNAT 并将数据包发送到真实服务器。 在运行真实服务器的主机上,根据源 IP 执行 SNAT,将源端口更改为为入口主机分配的范围内的端口。 然后在来自容器的返回数据包与源端口范围(和目标端口)匹配并将源 IP 更改为入口主机的 IP。

从技术上讲,这会起作用,但在快速添加和删除集群成员的实际部署中不切实际且脆弱。 这也显着减少了端口空间。

我提到的 NAT+DR 方法不起作用,因为无法在入口主机中更改源 IP。 通过仅将源端口更改为该特定主机范围内的一个端口并使用来自后端主机的路由策略将数据包返回到入口主机可能是一种选择。 这还有我之前提到的其他问题。

@thaJeztah
目前是否有任何解决方法可以将真实 IP 地址从 Nginx 容器转发到 Web 容器?
我有 Nginx 容器以global模式运行并发布到host ,因此 Nginx 容器获得正确的 IP 地址。 两个容器都能看到对方,但是,Web 容器获取 Nginx 容器 IP 地址,而不是客户端 IP 地址。
Nginx 是 web 的反向代理,web 在 8000 端口运行 uwsgi:

server {
    resolver 127.0.0.11;
    set $web_upstream http://web:8000;

    listen 80;
    server_name domain.com;
    location / {
        proxy_pass $web_upstream;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

@lpakula请检查我上面的回答 +这个有效的 nginx 配置

@pi0谢谢回复

我正在使用链接中的 nginx 配置,但 IP 地址仍然错误,我的配置中一定缺少某些内容

我有一个带有覆盖网络和两个服务的docker (

    docker service create --name nginx --network overlay_network --mode=global \
        --publish mode=host,published=80,target=80 \
        --publish mode=host,published=443,target=443 \
        nginx:1.11.10

    docker service create --name web --network overlay_network \
        --replicas 1 \
        web:newest

Nginx 容器使用最新的官方容器https://hub.docker.com/_/nginx/
Web 容器在端口 8000 上运行 uwsgi 服务器

我正在使用链接中的全局nginx.confconf.d/default.conf如下所示:

   server {
       resolver 127.0.0.11;
       set $web_upstream http://web:8000;

       listen 80;
       server_name domain.com;
       location / {
        proxy_pass $web_upstream;
      }
  }

然后是 nginx 容器日志:

  194.168.X.X - - [17/Mar/2017:12:25:08 +0000] "GET / HTTP/1.1" 200

Web 容器日志:

  10.0.0.47 - - [17/Mar/2017 12:25:08] "GET / HTTP/1.1" 200 -

那里缺少什么?

IP 地址仍然是错误的。 但它会添加 HTTP 标头
包含真实IP地址。 您必须配置您选择的 Web 服务器
信任代理(使用标头而不是源 IP)
2560 年 3 月 17 日星期五晚上 7:36 Lukasz Pakula通知@ github.com
写道:

@pi0 https://github.com/pi0感谢回复

我正在使用链接中的 nginx 配置,但 IP 地址仍然是
错了,我的配置中一定缺少某些东西

我有一个docker (
服务

docker service create --name nginx --network overlay_network --mode=global \
    --publish mode=host,published=80,target=80 \
    --publish mode=host,published=443,target=443 \
    nginx:1.11.10

docker service create --name web --network overlay_network \
    --replicas 1 \
    web:newest

Nginx 容器使用最新的官方容器
https://hub.docker.com/_/nginx/ http://url
Web 容器在端口 8000 上运行 uwsgi 服务器

我正在使用链接中的全局 nginx.conf 并且 conf.d/default.conf 看起来
如下:

服务器 {
解析器 127.0.0.11;
设置 $web_upstream http://web :8000;

   listen 80;
   server_name domain.com;
   location / {
    proxy_pass $web_upstream;
  }

}

然后是 nginx 容器日志:

194.168.XX - - [17/Mar/2017:12:25:08 +0000] "GET / HTTP/1.1" 200

Web 容器日志:

10.0.0.47 - - [17/Mar/2017 12:25:08] "GET / HTTP/1.1" 200 -

那里缺少什么?


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/docker/docker/issues/25526#issuecomment-287342795
或静音线程
https://github.com/notifications/unsubscribe-auth/ABtu97EFaCmLwAZiOYT4nXi4oXPCbLQks5rmn43gaJpZM4Jf2WK
.

>

潘杰,
Panjamapong Sermsawatsri
电话。 (+66)869761168

@lpakula啊,您的web:newest图像也应该尊重X-Real-IP标题。 nginx 不会自动更改发件人 IP,它只会发送一个提示标头。

@pi0 @PanJ
有道理,谢谢各位!

使用主机模式绑定端口。

nginx 使用TPROXY 内核模块支持IP 透明

@stevvooe Docker 也可以做类似的事情吗?

nginx 使用 TPROXY 内核模块支持 IP 透明。
@stevvooe Docker 也可以做类似的事情吗?

不太可能,因为需要跨节点跟踪条目。 我会让@sanimej或@mavenugo。

swarm 可以提供 REST API 来获取客户端 IP 地址吗?

@tonysongtl与此问题无关

还需要考虑的是如何在高度可用的设置中将流量传送到您的节点。 节点应该能够在不为客户端创建错误的情况下关闭。 当前的建议是使用外部负载均衡器(ELB、F5 等)并在第 4 层对每个 Swarm 节点进行负载均衡,并进行简单的第 4 层健康检查。 我相信 F5 使用 SNAT,因此此配置中的最佳情况是捕获 F5 的单个 IP,而不是真正的客户端 IP。

参考:
https://docs.docker.com/engine/swarm/ingress/#configure -an-external-load-balancer
https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Docker_EE_Best_Practices_and_Design_Considerations
https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Universal_Control_Plane_2.0_Service_Discovery_and_Load_Balancing

反映上面的评论 - 可以不使用代理协议吗? 所有云负载均衡器和 haproxy 都使用它来保存源 ip。

印花布也有IPIP模式- https://docs.projectcalico.org/v2.2/usage/configuration/ip-in-ip -这是原因之一github上使用它。 https://githubengineering.com/kubernetes-at-github/

你好。

为了理解和完整起见,让我总结一下,如果我错了,请纠正我:

主要问题是容器接收的不是原始 src-IP,而是 swarm VIP。 我已经在以下场景中复制了这个问题:

create docker swarm
docker service create --name web --publish 80:80 nginx
access.log source IP is 10.255.0.7 instead of client's browser IP

它似乎:

当 swarm 中的服务使用(默认)网格时,swarm 会执行 NAT以确保来自同一来源的流量始终发送到同一主机运行服务吗?
因此,它失去了原始的 src-IP 并用 swarm 的服务 VIP 替换它。

似乎@kobolog https://github.com/moby/moby/issues/25526#issuecomment -258660348 和@dack https://github.com/moby/moby/issues/25526#issuecomment -260813865 提案被@sanimej驳斥https://github.com/moby/moby/issues/25526#issuecomment -280722179 https://github.com/moby/moby/issues/25526#issuecomment -281289906 但是,TBH,他的论点并不完全清楚我呢,我也不明白为什么如果这绝对不可能,线程还没有关闭。

@sanimej这不行吗?:

  1. Swarm 接收带有 src-IP=A 和 destination="my-service-virtual-address" 的消息
  2. 包被发送到运行该服务的 swarm 节点,封装原始 msg。
  3. 节点转发到任务更改目的地到容器运行那个服务IP
    Swarm 和节点可以维护表,以确保尽可能将来自同一来源的流量转发到同一节点。

为特定服务启用“反向代理而不是 NAT”的选项不能解决所有这些让所有人满意的问题吗?

另一方面,IIUC,剩下的唯一选择是使用https://docs.docker.com/engine/swarm/services/#publish -a-services-ports-directly-on-the-swarm-node,其中- 再次 IIUC- 似乎根本不使用网格,因此我看不到使用群模式(与组合)

感谢您的帮助和耐心。
问候

@sanimej
甚至更多......为什么 Docker 不只是做一个端口转发 NAT(只改变目标 IP/端口)?

  1. Swarm 接收消息“从 A 到 myservice”
  2. Swarm 将消息转发到运行该服务的主机,设置 dest=node1
  3. 节点 1 接收消息“从 A 到节点 1”,并转发设置 dest=container1
  4. Container1 收到消息“从 A 到 container1”
  5. 回复,容器使用默认网关路由

我只想插话; 虽然我知道没有简单的方法可以做到这一点,但不以某种方式保留原始 IP 地址严重阻碍了许多应用程序用例。 这里有一些我能想到的:

  • 能够拥有详细说明用户来源的指标对于网络/服务工程至关重要。

  • 在许多安全应用程序中,您需要访问原始 IP 地址,以允许基于服务滥用的动态黑名单。

  • 位置感知服务通常需要能够访问 IP 地址,以便在其他方法失败时定位用户的大致位置。

从我对这个问题线程的阅读来看,当您希望在 Docker Swarm 中拥有可扩展的服务时,给定的解决方法似乎并不奏效。 将自己限制为每个工作节点一个实例会大大降低产品的灵活性。 此外,在馈入 Swarm 编排容器之前,保持一种混合方法,让边缘上的 LB/代理作为非 Swarm 编排容器运行,这似乎是回到了过去。 为什么用户需要维护 2 种不同的服务编排范式? 能够在边缘动态扩展 LB/代理怎么样? 那必须手动完成,对吗?

Docker 团队是否可以考虑这些评论,看看是否有某种方法可以引入此功能,同时仍然保持 Docker 生态系统中存在的质量和灵活性?

另外,我现在正受到这个打击。 我有一个 Web 应用程序,它将授权/经过身份验证的请求转发到下游 Web 服务器。 我们的服务技术人员需要能够验证人们是否已经访问了他们喜欢使用 Web 访问日志的下游服务器。 在当前情况下,我无法提供该功能,因为我的代理服务器永远不会看到原始 IP 地址。 我希望我的应用程序可以轻松扩展,而且我似乎无法通过所提供的变通方法来做到这一点,至少不能为每个扩展的实例抛出新的 VM。

@Jitsusama Kubernetes可以解决您的问题吗?

@thaJeztah有没有办法使用 docker-compose 来完成工作?

我试过

`services:
  math:
    build: ./math
    restart: always
    ports:
    - target: 12555
      published: 12555
      mode: host

但是好像是以172.xx1作为源IP

@trajano ,我不知道。 Kubernetes 是否设法解决了这个问题?

@Jitsusama
是的,他们有关于如何保留源 IP 的文档。 它是功能性的,但如果您不使用负载均衡器,那么它就不那么漂亮了,因为数据包会被丢弃在没有这些端点的节点上。 如果你打算使用 Rancher 作为你的自托管负载均衡器,不幸的是它目前还不支持它

@trajano

但是好像是以172.xx1作为源IP

如果您在本地访问您的应用程序,那么该 IP 应该是正确的(如果您使用 swarm),因为docker_gwbridge是与您的代理容器交互的接口。 您可以尝试从 IP 网络中的另一台计算机访问该应用程序,以查看它是否捕获了正确的地址。

至于撰写解决方法,这是可能的。 在这里,我使用图像jwilder/nginx-proxy作为我的前端反向代理(以简化概念)以及nginx的官方构建图像作为后端服务。 我在 Docker Swarm 模式下部署堆栈:

version: '3.3'

services:

  nginx-proxy:
    image: 'jwilder/nginx-proxy:alpine'
    deploy:
      mode: global
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  nginx:
    image: 'nginx:1.13.5-alpine'
    deploy:
      replicas: 3
    ports:
      - 80
      - 443
    environment:
      - VIRTUAL_HOST=website.local
$ echo '127.0.0.1 website.local' | sudo tee -a /etc/hosts
$ docker stack deploy --compose-file docker-compose.yml website

这将为堆栈创建一个website_default网络。 我的端点在环境变量VIRTUAL_HOST ,访问http://website.local给了我:

website_nginx-proxy.0.ny152x5l9sh7<strong i="30">@Sherry</strong>    | nginx.1    | website.local 172.18.0.1 - - [08/Sep/2017:21:33:36 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
website_nginx.1.vskh5941kgkb<strong i="33">@Sherry</strong>    | 10.0.1.3 - - [08/Sep/2017:21:33:36 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36" "172.18.0.1"

请注意, website_nginx.1.vskh5941kgkb的标头末尾暗示了原始 IP ( 172.18.0.1 )。 X-Forwarded-For & X-Real-IP 默认设置在jwilder/nginx-proxynginx.tmpl中。

对于端口443 ,我无法在 docker-compose 文件中添加两个端口,所以我只使用:

docker service update website_nginx-proxy \
    --publish-rm 80 \
    --publish-add "mode=host,published=80,target=80" \
    --publish-rm 443 \
    --publish-add "mode=host,published=443,target=443" \
    --network-add "<network>"

同时还添加了我想使用包含环境变量VIRTUAL_HOST应用程序进行反向代理的网络。 jwilder/nginx-proxy的文档中可以提供更细化的选项,或者您可以创建自己的设置。

Kubernetes 上的入口控制器基本上做同样的事情,因为入口图表(通常)支持X-Forwarded-ForX-Real-IP ,在入口的选择和类型及其部署副本方面具有更大的灵活性。

所以kubernetes的文档并不完整。 另一种方式是
很常见的实际上是入口+代理协议。

https://www.haproxy.com/blog/haproxy/proxy-protocol/

代理协议是一种被广泛接受的协议,它保留了源
信息。 Haproxy 内置了对代理协议的支持。 nginx
可以读取但不能注入代理协议。

设置代理协议后,您可以从任何
下游服务,如
https://github.com/nginxinc/kubernetes-ingress/blob/master/examples/proxy-protocol/README.md

甚至 openshift 也利用它来获取源 IP 信息
https://docs.openshift.org/latest/install_config/router/proxy_protocol.html

这是注入代理协议的 k8s 的最新 haproxy 入口。

恕我直言,在 swarm 中执行此操作的方法是使入口能够读取代理
协议(以防它从具有
已经注入的代理协议)以及注入代理协议
信息(以防所有流量首先到达入口)。

我不赞成以任何其他方式做这件事,尤其是当有
普遍接受的标准来做到这一点。

Traefik 几周前确实添加了 proxy_protocol 支持,并且从 v1.4.0-rc1 开始可用。

这需要在 docker swarm 入口级别完成。 如果入口
不注入代理协议数据,不注入任何下游服务
(包括 traefix、nginx 等)将能够读取它。

在 2017 年 9 月 10 日 21:42,“monotykamary”通知@github.com 写道:

Traefik 确实添加了 proxy_protocol 支持
几周前https://github.com/containous/traefik/pull/2004
从 v1.4.0-rc1 开始可用。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-328352805或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU3jj5dJcpMDysjIyGQK7SGx8GwWbks5shApqgaJpZM4Jf2WK
.

我也对这个 bug 与 infrakit 的关系感到困惑。 例如https://github.com/docker/infrakit/pull/601有人可以评论 docker swarm 将要采取的方向吗?

将 swarm rollup 到 infrakit 中吗? 我特别热衷于它的入口方面。

我们也遇到了这个问题。 我们想知道入站连接的客户端 IP 和请求的 IP。 例如,如果用户执行到我们服务器的原始 TCP 连接,我们想知道他们的 IP 是什么以及他们连接到我们机器上的哪个 IP。

@blazedd正如之前和其他线程中所评论的,这实际上可以使用 publishMode。 即:服务不是由网状网络处理的。

IIUC,在改进 ingress 处理此问题的方式方面取得了一些进展,但这实际上是唯一的解决方案。

我们使用 publishmode 和mode:global部署我们的 nginx 服务,以避免外部 LB 配置

@mostolog感谢您的回复。 只是一些注意事项:

  1. publishMode不能解决任何问题。 入站套接字连接仍然解析到 swarm 建立的本地网络。 至少当你使用端口列表mode: host
  2. nginx并不是一个好的解决方案。 我们的应用程序基于 TCP,但不是 Web 服务器。 如果不手动编码,我们将无法使用任何标头。
  3. 如果我使用docker run --net=host ...它一切正常。
  4. 到目前为止,我看到的唯一有效的解决方案是使用: https :

@blazedd在我们的堆栈中,我们有:

    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host

所以,我敢打赌我们会在日志中获得真实的 IP。

@mostolog它至少在 Windows 上不起作用。 我仍然以 172.0.0.x 地址作为来源。

@mostolog mode: host不会将您的容器暴露给主机网络。 它从入口网络中删除容器,这是 Docker 在运行容器时的正常运行方式。 它会复制 docker run 命令中使用的--publish 8080:8080 。 如果 nginx 获得真正的 ips,这不是套接字直接连接到这些 ips 的结果。 要对此进行测试,您应该认真考虑使用没有框架的原始 TCP 实现或 HTTP 服务器,并检查报告的地址。

为什么不直接使用 IPVS 路由网络到容器? 将所有swarm节点的overlay接口的ips绑定为虚拟ips,使用ip rule from xxx table xxx做多网关,然后swarm节点可以直接将客户端路由到容器(DNAT),不需要任何用户空间网络代理守护进程(dockerd)

@blazedd你试过了吗? 在遵循@mostolog的示例时,我获得了外部 IP 地址。

我再次遇到这个问题。

我的设置如下:

  • DR 模式下的 ipvs 负载均衡器(在 docker swarm 外部)
  • 3 个 docker 节点,将目标 IP 添加到所有节点,并为 IPVS DR 路由适当配置了 arp

我想将堆栈部署到 swarm 并让它在虚拟 IP 上的端口 80 上侦听,而不会修改地址。

通过这样做,我几乎可以到达那里:
端口:
- 目标:80
已发表:80
协议:tcp
模式:主机

这里的问题是它不允许您指定要绑定到哪个 IP 地址 - 它只是绑定到所有。 如果您想使用该端口运行多个服务,这会产生问题。 它只需要绑定到一个IP。 使用不同的端口不是 DR 负载平衡的选项。 开发人员似乎假设多个节点上永远不会存在相同的 IP,而使用 DR 负载均衡器时并非如此。

此外,如果使用短语法,它将忽略绑定 IP 并仍然绑定到所有地址。 我发现绑定到单个 IP 的唯一方法是运行非集群容器(不是服务或堆栈)。

所以现在我不得不使用独立的容器并且不得不自己管理它们,而不是依赖服务/堆栈特性来做到这一点。

我们有同样的问题。
我会投票支持 docker ingress 中的透明解决方案,该解决方案允许所有应用程序(一些使用原始 UDP/TCP,尤其是 HTTP)按预期工作。

我可以使用“模式=主机端口发布”解决方法,因为我的服务是在全球部署的。
但是,这似乎与 macvlan 网络驱动程序的使用不兼容,我出于其他一些原因需要使用它。
我们得到诸如“macvlan 驱动程序不支持端口映射”之类的日志。
我尝试使用多个网络,但没有帮助。

我在这里创建了一张特定的票: https :
这让我暂时没有解决方案:'(

嗨,大家好
现在有解决方法吗? 没有将其作为主机端口发布
港口 ?

2018 年 1 月 11 日 00:03,“Olivier Voortman”通知@ github.com 写道:

我们有同样的问题。
我会投票支持 docker ingress 中的透明解决方案,该解决方案允许所有
应用程序(一些使用原始 UDP/TCP,不是特别是 HTTP)作为
预期的。

我可以使用“模式=主机端口发布”解决方法,因为我的服务是
全球部署。
不过这个好像和macvlan的使用不兼容
网络驱动程序,由于其他一些原因我需要它。
我们得到诸如“macvlan 驱动程序不支持端口映射”之类的日志。
我尝试使用多个网络,但没有帮助。

我在这里创建了一张特定的票:docker/libnetwork#2050
https://github.com/docker/libnetwork/issues/2050
这让我暂时没有解决方案:'(


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-356693751或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUzlM-BMbEsDYAiYH6hKLha-aRqerks5tJQJngaJpZM4Jf2WK
.

无法获得客户端的IP真是太可惜了。 这使得 docker swarm 的大部分功能都无法使用。

在我的设置中,获取客户端 IP 的唯一方法是使用network_mode:host而根本不使用 swarm。

使用mode=host port publishing或传统的docker run -p "80:80" ...不起作用

https://github.com/moby/moby/issues/15086中提出了一些解决方案,但唯一对我

没有正确 IP 的另一个问题是 nginx 速率限制无法正常工作,因此不能与 docker swarm 负载均衡器一起使用,因为请求受到速率限制并被拒绝,因为 nginx 将所有请求都计算在内,因为它们来自单个用户/IP。 所以唯一的解决方法是使用模式=主机,但这样我就失去了负载平衡功能,不得不将 DNS 指向特定的实例。

也许 docker 不是这项工作的理想工具,我正在研究 vagrant 来设置前端 HTTP 服务器并将客户端 IP 作为 HTTP 请求标头的一部分。

在 Docker 能够通过覆盖网络传递客户端信息之前,人们可以使用像 Docker Flow Proxy 或 Traefik 这样的代理,在该服务中以主机模式发布所需的端口并将应用程序服务连接到它。 不是一个完整的解决方案,但效果很好,并允许应用程序服务的负载平衡/客户端 IP 的检索。

@deeky666 Traefik 和类似的工作只有在没有 dockerized 的情况下

我在 traefik 上看不到 udo 支持

从我的iPhone发送

最后我们放弃了 docker 容器。 还没有准备好生产!

在星期三,2018年1月24日在上午05时43,埃弗拉[email protected]写道:

我在 traefik 上看不到 udo 支持

从我的iPhone发送

>


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-360091189或静音
线程
https://github.com/notifications/unsubscribe-auth/AHf7rvMcH2iFBxcExfO_Ol0UttCspuTnks5tNwlkgaJpZM4Jf2WK
.

通过使用mode=host似乎在17.12.0-ce部分解决了这个问题。

docker service create --publish mode=host,target=80,published=80 --name=nginx nginx

它有一些限制(没有路由网格)但有效!

@goetas mode=host作为一种解决方法工作了一段时间,所以我不会说问题以某种方式解决了。 使用 mode=host 有很多限制,端口被暴露,不能使用群负载平衡等。

@darklow我知道这些限制,但对于我的用例来说很好(如果不是更好的话!)。 在17.09.1-ce根本不工作,所以对我来说已经是一个进步!

该变通方法的一大缺点是无法避免更新期间的停机时间。
目前,我们不得不选择放弃稳定性还是源IP地址。

我同意。 Swarm 需要一种高可用性方式来保留源 IP。

可能使用代理协议。 我不认为这是一个巨大的努力添加
对 docker swarm 的代理协议支持。

有人在调查这个吗?

2018 年 1 月 28 日 22:39,“泷内元气”通知@github.com 写道:

该变通方法的一大缺点是无法避免停机
更新期间的时间。
目前,我们不得不选择放弃稳定性还是源IP
地址。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-361078416或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU-or7fnhKTg7fhjtZYjGYHBFRE7Dks5tPKnYgaJpZM4Jf2WK
.

@sandys我同意。 代理协议将是个好主意。
@thaJeztah @aluzzardi @mrjana请问这个问题能引起注意吗? 有一段时间没有团队的任何回应。 谢谢你。

代理协议对我来说听起来是最好的解决方案。 希望团队考虑一下。

@goetas它在某一时刻起作用,至少我看到它起作用了,但它似乎在 docker 1.12.6 中再次恢复到 172.xxx 行为

这非常糟糕,它减轻了任何速率限制、欺诈预防、登录、安全登录、会话监控等!
使用mode:host侦听有效,但不是真正的解决方案,因为您失去了网状负载平衡,并且只有具有公共 ip 的主机上的软件负载平衡器必须单独处理所有流量。

这对我们来说是一个非常关键和重要的错误,这阻碍了我们与 Swarm 的上线。 我们也相信代理协议是解决此问题的正确解决方案。 Docker 入口必须在代理协议上传递源 ip。

在 twitter 上提出的解决方案之一是使用 Traefik 作为在 Swarm 之外管理的入口。 这对我们来说是非常次优的 - 而不是我们想要管理的开销。

如果 Swarm 开发人员想查看如何在 Swarm-ingress 中实现代理协议,他们应该查看 Traefik 中讨论的所有错误(例如 https://github.com/containous/traefik/issues/2619)

我一直使用“撰写”而不是群模式使这项工作始终如一。 也许有什么需要考虑的。

代理协议的一些问题:

它是由docker本身解码还是由应用程序解码? 如果我们依赖于应用程序来实现代理协议,那么这不是适用于所有应用程序的通用解决方案,仅适用于 Web 服务器或其他实现代理协议的应用程序。 如果 docker 解开代理协议并转换地址,那么它还必须跟踪连接状态并对传出数据包执行反向转换。 我不赞成特定于 Web 的解决方案(依赖于应用程序中的代理协议),因为 docker 对许多非 Web 应用程序也很有用。 对于任何 TCP/UDP 应用程序的一般情况,应该解决这个问题 - docker 中没有其他内容是特定于 Web 的。

与任何其他封装方法一样,也存在数据包大小/MTU 问题。 但是,我认为这可能会成为解决此问题的任何解决方案的问题。 答案可能是确保您的 swarm 网络支持足够大的 MTU 以允许开销。 我认为大多数群体都在本地网络上运行,所以这可能不是主要问题。

@trajano - 我们知道它适用于主机网络(这可能是您的撰写解决方案正在做的事情)。 然而,这抛弃了 swarm 的所有集群网络优势(例如负载平衡)。

@dack后端必须知道代理协议。
我认为它解决了大多数情况,至少你可以放置一个类似 passthrough 的代理来处理容器内后端前面的协议标头。
由于缺乏信息是致命的问题,我认为有必要在其他巧妙的解决方案之前尽快解决它。

代理协议被广泛接受。 查看支持的工具数量 - https://www.haproxy.com/blog/haproxy/proxy-protocol/
它甚至不包括云负载均衡器(ELB、Google LB)和更新的工具,如 Traefik。

另外 - 这几乎是 kubernetes 的标准: https :

在这一点上,代理协议是解决这个问题的最广泛接受的标准。 我没有看到重新发明它并破坏与世界上 nginx 的兼容性的巨大价值。

这些是 L7 协议。 Swarm 入口是 L4。 这里没有任何重新发明,都是使用 DNAT 的 IPVS。

@cpuguy83无法理解您的意思。

代理协议是第 4 层。
http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt

PROXY 协议的目标是用
由代理收集的服务器能够获得的信息
如果客户端直接连接到服务器而不是通过
代理。 协议携带的信息是服务器将要传递的信息
使用 getockname() 和 getpeername() :

  • 地址族(用于 IPv4 的 AF_INET、用于 IPv6 的 AF_INET6、AF_UNIX)
  • 套接字协议(用于 TCP 的 SOCK_STREAM,用于 UDP 的 SOCK_DGRAM)
  • 第 3 层源地址和目的地址
  • 第 4 层源和目标端口(如果有)

http://cbonte.github.io/haproxy-dconv/1.9/configuration.html#5.1 -accept-proxy

接受代理

在任何接受的任何连接上强制使用 PROXY 协议
在同一行上声明的套接字。 PROXY 协议的版本 1 和 2
支持并正确检测。 PROXY 协议规定了层
传入连接的 3/4 地址可用于任何地址
使用,唯一的例外是“tcp-request connection”规则,它将
只能看到真实的连接地址。 日志将反映地址
在协议中指出,除非它被违反,在这种情况下,真正的
地址仍将被使用。 这个关键字结合外部的支持
组件可以用作高效可靠的替代品
X-Forwarded-For 机制并不总是可靠,甚至不总是
可用。 另请参阅“tcp-request connection expect-proxy”以获得更细粒度的
设置允许哪个客户端使用该协议。

你的意思是有比代理协议更好的方法吗? 这是完全有可能的,并且希望在 docker swarm 中的源 ip 保存上下文中了解更多信息。 然而,代理协议更广泛地被其他工具(如 nginx 等)支持,这些工具将在 swarm-ingress 的下游......以及像 AWS ELB 这样的工具将在上游到 swarm-ingress。 那是我唯一的 0.02 美元

@sandys代理协议看起来像封装(至少在连接启动时),这需要从接收器一直到堆栈的封装知识。 这种方法有很多权衡。

我不想在核心中支持这一点,但也许使入口可插拔是一种值得的方法。

@sandys https://github.com/sandys代理协议看起来像
封装(至少在连接启动时),这需要知识
从接收器一直到堆栈的封装。 那里
这种方法有很多权衡。

那是真实的。 这就是为什么它是带有 RFC 的标准的原因。 有
这背后的动力 - 几乎每个组件的重要性
支持它。 恕我直言,支持它并不是一个糟糕的决定。

我不想在核心支持这一点,但也许会进入
可插拔将是一种值得的方法。

这是一个更大的讨论 - 但是我可能会补充说最大的
Docker Swarm 的优势在于它拥有所有电池
内置。

我仍然会要求你考虑代理协议作为一个很好的解决方案
这个问题得到了行业的支持。

是否无法在 Linux 和 LxCs 上模拟 L3 路由器(不是专门的 docker)

不需要@trajano模拟,而是需要封装来解决这个问题。
例如,可以为需要客户端 IP 地址并知道如何处理封装的数据包(例如 nginx)的服务提供一个选项(例如: --use-proxy-protocol )。

按照目前的工作方式,接收数据包的 docker 节点执行 SNAT 并将数据包转发到具有应用程序容器的节点。 如果使用某种形式的隧道/封装而不是 SNAT,那么应该可以将原始未更改的数据包传递给应用程序。

这是其他项目中已解决的问题。 例如,借助 OpenStack,您可以使用 GRE 和 VXLAN 之类的隧道。

是否有人在此线程的最近部分代表 docker 团队并至少说“我们听到了你的声音”? 在大约 18 个月前于 2016 年 8 月 9 日首次报告之后,您希望“开箱即用”且对社区如此感兴趣的功能似乎仍然没有得到解决。

是否有人在此线程的最近部分代表 docker 团队并至少说“我们听到了你的声音”?

/cc @GordonTheTurtle @thaJeztah @riyazdf @aluzzardi

@bluejaguar @ruudboon我是 Docker 的一员。 这是一个众所周知的问题。 现在,网络团队专注于覆盖网络稳定性的长期存在的错误。 这就是为什么在最近的几个版本中没有真正的新网络功能的原因。

我的建议是提出一个你愿意致力于解决问题的具体提案,或者至少是一个足够好的提案,任何人都可以接受并执行它。

@cpuguy83我一直在关注https://github.com/kubernetes/kubernetes/issues/42616 (有趣的是,这里的代理协议是从 Google Kubernetes Engine 流入的,它在 HTTPS 模式下原生支持代理协议)。

此外,ELB 在 2017 年 11 月增加了对代理协议 v2 的支持 (https://docs.aws.amazon.com/elasticloadbalancing/latest/network/doc-history.html)

Openstack Octavia LB-as-a-service(类似于我们的入口)去年四月合并了代理协议 - http://git.openstack.org/cgit/openstack/octavia/commit/?id=bf7693dfd884329f7d1169eec33eb03d2ae81ace

以下是有关 openstack 中代理协议的一些文档 - https://docs.openshift.com/container-platform/3.5/install_config/router/proxy_protocol.html
一些细微差别与 https 的代理协议有关(无论是否在入口处终止证书)。

有关此问题的任何更新/解决方法? 我们真的需要知道 docker swarm 模式下的客户端 IP。
任何帮助将非常感激。

我的版本:

客户:
版本:18.02.0-ce
API 版本:1.36
Go版本:go1.9.3
Git 提交:fc4de44
内置:2018 年 2 月 7 日星期三 21:16:33
操作系统/架构:linux/amd64
实验:假
编排者:swarm

服务器:
引擎:
版本:18.02.0-ce
API 版本:1.36(最低版本 1.12)
Go版本:go1.9.3
Git 提交:fc4de44
内置:2018 年 2 月 7 日星期三 21:15:05
操作系统/架构:linux/amd64
实验:假

@adijes和其他面临此问题的用户。 您可以将容器绑定到bridge网络(如该线程中的某个人所述)。

version: "3.4"

services:
  frontend:
    image: nginx
    deploy:
      placement:
        constraints:
          - node.hostname == "prod1"
    networks:
      - default
      - bridge
  # backed services...
  # ...

networks:
  bridge:
    external:
      name: bridge

我们的frontend绑定到bridge并且始终停留在一个精确的主机上,其 IP 绑定到我们的公共域。 这使它能够接收真实的用户 IP。 并且因为它也绑定到default网络,所以它将能够连接到支持的服务。

您还可以缩放frontend ,只要您将其保留在该唯一主机中即可。 这使主机成为单点故障,但是(我认为)对于小型站点来说还可以。

编辑以添加更多信息:

我的 nginx 容器在https://github.com/jwilder/nginx-proxy后面,我也使用https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion来启用 SSL。 nginx-proxy 通过docker run命令运行,而不是 docker swarm 服务。 也许,这就是我从客户那里获得真实 IP 的原因。 bridge网络需要允许我的 nginx 容器与 nginx-proxy 通信。

FWIW,我正在使用:

Client:
 Version:      17.09.1-ce
 API version:  1.32
 Go version:   go1.8.3
 Git commit:   19e2cf6
 Built:        Thu Dec  7 22:23:40 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.09.1-ce
 API version:  1.32 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   19e2cf6
 Built:        Thu Dec  7 22:25:03 2017
 OS/Arch:      linux/amd64
 Experimental: false

以上设置也适用于另一个正在运行的设置:

Client:
 Version:      17.09.1-ce
 API version:  1.32
 Go version:   go1.8.3
 Git commit:   19e2cf6
 Built:        Thu Dec  7 22:23:40 2017
 OS/Arch:      linux/amd64

Server:
 Version:      17.09.1-ce
 API version:  1.32 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   19e2cf6
 Built:        Thu Dec  7 22:25:03 2017
 OS/Arch:      linux/amd64
 Experimental: false

@letientai299对我不起作用,我明白了

网络“桥”被声明为外部的,但它不在正确的范围内:“本地”而不是“群”

我有一个主节点和三个工作节点

@trajano ,请参阅我的更新。

@letientai299实际上我想知道你是如何让bridge模式下工作的。 即你没有得到我的错误。

@dack当您说主机网络时,我认为您的意思是拥有

ports:
- target: 12555
  published: 12555
  protocol: tcp
  mode: host

不幸的是,当以docker stack deploy模式运行时,它不起作用并且仍然丢失源 IP,而 docker-compose up 工作正常。

我还尝试了以下基于@goetas

docker service create --constraint node.hostname==exposedhost \
  --publish published=12555,target=12555,mode=host \
  trajano.net/myimage

仍然没有运气获得Server Version: 17.12.0-ce上的源 IP

似乎每个人在某个时候都会想要的东西,并且由于将覆盖网络与桥接/主机网络一起使用是不可能的,因此在出于各种原因确实需要客户端 IP 的情况下,这是一个阻止程序。

客户:
版本:17.12.0-ce
API 版本:1.35
转版本:go1.9.2
Git 提交:c97c6d6
内置:2017 年 12 月 27 日星期三 20:03:51
操作系统/架构:达尔文/amd64

服务器:
引擎:
版本:17.12.1-ce
API 版本:1.35(最低版本 1.12)
转版本:go1.9.4
Git 提交:7390fc6
建成时间:2018 年 2 月 27 日星期二 22:17:54
操作系统/架构:linux/amd64
实验:真实

现在是 2018 年。关于这个问题有什么更新吗?
在 swarm 模式下,我不能使用 nginx req 限制。 $remote_addr 总是捕获 10.255.0.2。
这是关于 docker swarm 的一个非常严重的问题。
也许从今天开始我应该尝试 kubernetes。

@Maslow我在上面发布了一些评论。

我们可以放宽检查吗

networks:
  bridge:
    external:
      name: bridge

或者像这样扩展它

networks:
  bridge:
    external:
      name: bridge
      scope: local

scope: local网络仅在网络模式为host时才允许

网络“桥”被声明为外部的,但它不在正确的范围内:“本地”而不是“群”

或允许

networks:
  bridge:
    driver: bridge

不要失败

未能创建服务 trajano_serv:来自守护进程的错误响应:网络 trajano_bridge 不能与服务一起使用。 只能使用范围限定为 swarm 的网络,例如使用覆盖驱动程序创建的网络。

在已发布的端口上使用mode: host时。

ports:
- target: 32555
  published: 32555
  protocol: tcp
  mode: host

@trajano您可以使用非 swarm 范围的网络与 swarm 已经...例如这有效:

version: '3.4'

services:
  test:
    image: alpine
    command: top
    ports:
      - target: 32555
        published: 32555
        protocol: tcp
        mode: host
    networks:
      - bridge

networks:
  bridge:
    external:
      name: bridge

您是否在一个具有 docker stack deploy 的不止一个工人的集群上测试过这个。 我知道它适用于撰写。

在2018年3月18日,在20:55,布莱恩·高夫[email protected]写道:

@trajano您可以使用非 swarm 范围的网络与 swarm 已经...例如这有效:

版本:'3.4'

服务:
测试:
图片:高山
命令:顶部
端口:
- 目标:32555
发表:32555
协议:tcp
模式:主机
网络:
- 桥

网络:
桥:
外部的:
名称:桥

你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看,或将线程静音。

是的,我是通过 swarm 来做这件事的...

2018 年 3 月 19 日星期一上午 9:12,阿基米德·特拉哈诺 <
[email protected]> 写道:

您是否在一个拥有 docker stack 的不止一个工人的集群上测试过这个
部署。 我知道它适用于撰写。

2018 年 3 月 18 日晚上 8:55,Brian Goff通知@ github.com
写道:

@trajano您可以使用非 swarm 范围的网络与 swarm 已经...
例如这有效:

版本:'3.4'

服务:
测试:
图片:高山
命令:顶部
端口:

  • 目标:32555
    发表:32555
    协议:tcp
    模式:主机
    网络:

网络:
桥:
外部的:
名称:桥

你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看,或将线程静音。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-374206587或静音
线程
https://github.com/notifications/unsubscribe-auth/AAwxZsm3OohKL0sqUWhlgUNjCrqR0OaVks5tf67YgaJpZM4Jf2WK
.

——

  • 布赖恩·高夫

+1

以下 docker swarm 负载平衡与 3 个节点存在此问题:

覆盖网络 <-> nginx 代理 jwilder docker <-> nginx web 头 docker

我遵循了建议,日志不断返回 docker network ip 10.255.0.3 而不是 Real Client IP。

+1

@cpuguy83这已经开始成为我们更大的群体设置的

您对 ETA 有任何想法吗? 这对我们有很大帮助。

@sandys到底是什么的预计

@cpuguy83您好,感谢您的回复。 我知道你想如何解决它没有广泛的共识。 我是在评论团队如何专注于稳定性问题并且没有为这个问题腾出时间。
您认为什么时候会讨论这个问题(如果有的话)?

请注意,您可以通过使用 PublishMode=host 运行全局服务和发布端口来解决此问题。 如果您知道人们将连接到哪个节点,您甚至不需要它,只需使用约束将其固定到该节点即可。

@kleptog部分你不能。 在更新服务时无法避免停机。

测试场景 - 仔细研究 lvs/ipvs。

  • nsenter到隐藏的入口容器并删除snat规则
  • nsenter 到具有已发布端口的服务,删除默认 gw 并将默认路由添加到入口容器 ip。

现在源 ip 将被保留。

我仍在尝试理解开销的含义,在每个服务容器中维护基于策略的路由,而不是在入口容器中只有 snat 规则。
不过,让这个工作真的很令人欣慰。

对不起,我天真地摆弄,但有人( @dack ?)能指出我的

啊,现在我明白了。 在多节点群中,IP 必须是 lvs 导演的,才能找到返回请求进入的正确节点的方法......

无论如何,看看代码会很有趣。 如果有人已经知道,它可以为我节省一些时间。 谢谢

这方面的任何更新,在不同国家/地区拥有三个集群,甚至 Azure 流量管理器都需要真实的用户 IP,如果没有,他不会将用户重定向到好的集群等。任何人,很快或曾经会检查这个? 谢谢

还需要对此进行更新 - 这是一个巨大的问题 - 我发现解决此问题的唯一方法是在前面添加另一个代理并将 x-forwarded-for 发送到堆栈,这意味着 Swarm 不适合公众使用面对很多场景的流量。

@cpuguy83 @trajano
我可以确认以下不起作用

version: '3.4'
services:
  nginx:
    ports:
      - mode: host
        protocol: tcp
        published: 80
        target: 80
      - mode: host
        protocol: tcp
        published: 443
        target: 81
networks:
  bridge:
    external:
      name: bridge

它失败了network "bridge" is declared as external, but it is not in the right scope: "local" instead of "swarm"

码头工人版本

Client:
 Version:       18.03.0-ce-rc4
 API version:   1.37
 Go version:    go1.9.4
 Git commit:    fbedb97
 Built: Thu Mar 15 07:33:59 2018
 OS/Arch:       windows/amd64
 Experimental:  false
 Orchestrator:  swarm

Server:
 Engine:
  Version:      18.03.0-ce
  API version:  1.37 (minimum version 1.12)
  Go version:   go1.9.4
  Git commit:   0520e24
  Built:        Wed Mar 21 23:08:31 2018
  OS/Arch:      linux/amd64
  Experimental: false

@Mobe91
尝试重新创建群。 我也有错误。 在重新初始化群之后,一切都对我有用。
我的docker-compose.yml文件:

version: "3.6"

services:
    nginx:
        image: nginx:latest
        depends_on:
            - my-app
            - my-admin
        ports: 
            - target: 80
              published: 80
              protocol: tcp
              mode: host
            - target: 443
              published: 443
              protocol: tcp
              mode: host
            - target: 9080
              published: 9080
              protocol: tcp
              mode: host
        volumes:
            - /etc/letsencrypt:/etc/letsencrypt:ro
            - /home/project/data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
            - /home/project/data/nginx/conf.d:/etc/nginx/conf.d
            - /home/project/public:/var/public
        networks:
            - my-network
            - bridge
        deploy:
            placement:
                constraints: [node.role == manager]

    my-app:
        image: my-app
        ports:
            - 8080:8080
        volumes:
            - /usr/src/app/node_modules
            - /home/project/public:/usr/src/app/public
        networks:
            - my-network

    my-admin:
        image: my-admin
        ports:
            - 9000:9000
        networks:
            - my-network

networks:
    my-network:
    bridge:
        external: true
        name: bridge

我的docker version

Client:
 Version:   18.03.0-ce
 API version:   1.37
 Go version:    go1.9.4
 Git commit:    0520e24
 Built: Wed Mar 21 23:10:01 2018
 OS/Arch:   linux/amd64
 Experimental:  false
 Orchestrator:  swarm

Server:
 Engine:
  Version:  18.03.0-ce
  API version:  1.37 (minimum version 1.12)
  Go version:   go1.9.4
  Git commit:   0520e24
  Built:    Wed Mar 21 23:08:31 2018
  OS/Arch:  linux/amd64
  Experimental: false

对不起我的英语不好。

@Mobe91这是我使用的,但我从“portainer”或 Linux 机器上部署。 我无法从 Windows 正确部署它。

version: '3.4'
services:
  hath:
    image: trajano.net/hath
    deploy:
      placement:
        constraints:
        - node.hostname==docker-engine
    networks:
    - host
    ports:
    - target: 12555
      published: 12555
      protocol: tcp
      mode: host
    secrets:
    - hath_client_login
    volumes:
    - hath:/var/lib/hath
volumes:
  hath:
    name: 'noriko/s/hath'
    driver: cifs
networks:
  host:
    external:
      name: host
secrets:
  hath_client_login:
    external:
      name: hath_client_login

主要区别是我使用host而不是bridge在我的情况下,我也将我的主机作为 VirtualBox VM 运行,我使用执行 NAT 路由的路由器,并一直保留传入的 IP容器。

当然没有负载平衡功能我认为如果你想要负载平衡你需要在前面有一个像L3 路由器那样的东西

@trajano是对的,Windows 客户端是问题所在,使用 Linux 客户端进行部署有效。

但我不明白为什么你甚至需要hostbridge网络?
以下对我来说很好用,即我在 nginx 中获得了真实的客户端 IP 地址:

version: '3.4'
services:
  nginx:
    ports:
      - mode: host
        protocol: tcp
        published: 80
        target: 80

@ Mobe91谢谢我想为此打开一个问题。 基本上与https://github.com/moby/moby/issues/32957 相关,因为它仍然出现在 Windows 18.03-ce 客户端上。

有人用过 Cilium 吗? http://cilium.readthedocs.io/en/latest/gettingstarted/docker/

似乎可以在不将服务绑定到主机的情况下解决此问题。

@sandys很好的发现 - 我即将开始测试它,它对你

我们在重新设计我们的部署时遇到了这一点,以避免将代理固定到单个主机(在生产中,由于其他原因绑定到接口,因此“获取”客户端 IP 作为副产品)。

在我们的测试环境中,我们只能通过约束部署到管理器并设置mode = global以确保每个管理器获得一个正在运行的实例来改进。 必须注意的仍然是额外的开销,特别是如果我们丢失了一个管理器节点并且某些东西将我们的流量引导到它。 但是,这比固定到单个主机要好。

@sandys你试过 Cilium 吗? 看起来与 Weave 相似,它似乎至少在 k8s 上遇到了同样的问题: https :

我一直无法使用 Cilium,但我已经联系了 Cilium
开发人员帮助进行群配置。 但我对 Cilium 感到非常兴奋
因为入口是它想要解决的一个既定问题(与编织不同)

2018 年 5 月 10 日星期四,James Green, notifications@ github.com 写道:

我们在重新设计我们的部署时遇到了这一点,以避免将代理固定到
单个主机(在生产中,绑定到其他主机的接口)
原因,因此“选择”客户端 IP 作为副产品)。

在我们的测试环境中,我们只能通过部署给经理来改进
约束和设置 mode = global 以确保每个经理都得到一个
运行实例。 必须注意的仍然是额外的开销,
特别是如果我们失去了一个管理器节点并且某些东西正在指导我们
交通到它。 但是,这比固定到单个主机要好。

@sandys https://github.com/sandys你试过 Cilium 吗? 看起来类似于
Weave,至少在 k8s 中似乎遇到了同样的问题:
kubernetes/kubernetes#51014
https://github.com/kubernetes/kubernetes/issues/51014


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-388032011或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUzQCgIeTenQIHIERxOfHKCzn1O6Aks5txCpogaJpZM4Jf2WK
.

2018 年 5 月 10 日 17:24,“James Green”通知@ github.com 写道:

我们在重新设计我们的部署时遇到了这一点,以避免将代理固定到
单个主机(在生产中,绑定到其他主机的接口)
原因,因此“选择”客户端 IP 作为副产品)。

在我们的测试环境中,我们只能通过部署给经理来改进
约束和设置模式=全局以确保每个管理器都运行
实例。 必须注意这仍然是额外的开销,特别是如果
我们失去了一个管理节点,并且有什么东西将我们的流量引向它。
但是,这比固定到单个主机要好。

@sandys https://github.com/sandys你试过 Cilium 吗? 看起来类似于
Weave,至少在 k8s 中似乎遇到了同样的问题:
kubernetes/kubernetes#51014
https://github.com/kubernetes/kubernetes/issues/51014


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-388032011或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUzQCgIeTenQIHIERxOfHKCzn1O6Aks5txCpogaJpZM4Jf2WK
.

  • 1

嗨,大家好,
如果你想在 Cilium 中支持 Docker Swarm(特别是对于入口和
围绕这个特定问题),请评论/喜欢这个错误 -
https://github.com/cilium/cilium/issues/4159

在星期五,2018 5月11日12:59 AM,McBacker [email protected]写道:

>

  • 1


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-388159466或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU_18F_cNttRUaAwaRF3gVpMZ-3qSks5txJUfgaJpZM4Jf2WK
.

对我来说,当前版本的工作方式如下:
然后我可以访问群中的其他节点,因为它也在“默认”网络中

  web-server:
    image: blabla:7000/something/nginx:latest
    #ports:
    #  - "80:80"
    #  - "443:443"
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host        
    deploy:
      mode: global
      restart_policy:
        condition: any
      update_config:
        parallelism: 1
        delay: 30s

我可以确认关键是使用ports.mode: host 。 从文档(https://docs.docker.com/compose/compose-file/#long-syntax-1):

mode:用于在每个节点上发布主机端口的主机,或用于负载平衡的群模式端口的入口。

然后,使用mode: host停止被 ingress 负载均衡,真实 IP 出现。 例如,这是我的 nginx 日志:

  • mode: host
    metrics-agents_nginx.1.pip12ztq3y1h<strong i="14">@xxxxxxxx</strong> | 62.4.X.X - - [12/Jun/2018:08:46:04 +0000] "GET /metrics HTTP/1.1" 200 173979 "-" "Prometheus/2.2.1" "-" [CUSTOM] "request_time: 0.227" remote_addr: 62.4.X.X proxy_add_x_forwarded_for: 62.4.X.X
  • 没有mode: host
    metrics-agents_nginx.1.q1eosiklkgac<strong i="20">@xxxxxxxx</strong> | 10.255.0.2 - - [12/Jun/2018:08:50:04 +0000] "GET /metrics HTTP/1.1" 403 162 "-" "Prometheus/2.2.1" "-" [CUSTOM] "request_time: 0.000" remote_addr: 10.255.0.2 proxy_add_x_forwarded_for: 10.255.0.2

如果您想知道为什么最后一个日志是 403 Forbidden 响应,这是因为在 nginx 上使用了白名单( allow 62.4.X.Xdeny all )。

语境:
Description: Debian GNU/Linux 9.4 (stretch)
Docker version 18.03.0-ce, build 0520e24

我确认@nperron所说的话。
使用主机模式允许获取客户端IP。

Docker 版本 18.03.1-ce,构建 9ee9f40
Ubuntu 16.04.4 LTS

我可以确认它正在工作。

Docker 版本 18.03.1-ce,构建 9ee9f40
Ubuntu 16.04.4 LTS

警告:如果您设置了 IPTABLES=FALSE,这将不起作用!
如果您使用 UFW 来保护端口并发现 docker swarm 覆盖了那些 UFW 设置,那么您可能已经这样做了(或者至少我这样做了)。

有一些教程建议通过命令或在 /etc/docker/daemon.json 中设置 iptables = false

希望这能让某人免于我刚刚经历的挫折!

人们真的应该停止说“模式:主机”=工作,因为那不是在使用 Ingress。 这使得不可能只有一个容器在 swarm 上运行一个服务,但仍然能够通过任何主机访问它。 您要么必须将服务设为“全局”,要么只能在它运行的主机上访问它,这有点违背了 Swarm 的目的。

TLDR:“模式:主机”是一种解决方法,而不是解决方案

@r3pek虽然我同意你的观点,如果你使用主机模式来解决这个困境,你会失去 Ingress,但我想说它几乎没有违背 Swarm 的全部目的,它比面向公众的入口网络做得更多。 在我们的使用场景中,我们在同一个覆盖群中:
管理只应通过内联网访问的复制容器 -> 它们不需要调用者的 ip,因此它们被“正常”配置并利用入口。
未暴露的容器 -> 对这些没什么好说的(我相信你低估了通过它们的服务名称访问它们的能力)。
面向公众的服务 -> 这是一个 nginx 代理,它执行基于 https 和 url 的路由。 它甚至在需要 x-forward-for 客户端的真实 ip 之前就被定义为全局的,所以那里没有真正的问题。

拥有 nginx 全局且没有入口意味着您可以通过集群的任何 ip 访问它,但它不是负载平衡或容错的,因此我们在 nginx 前面添加了一个非常便宜且易于设置的 L4 Azure 负载平衡器服务。

正如您所说,Host 是一种解决方法,但是说启用它完全违背了 Docker Swarm 的目的是有点夸张的 imo。

嗨罗伯托
我不认为它被夸大了 - 因为主机模式暴露了单点
失败。 此外,它还需要额外的负载管理层
在群体生态系统之外保持平衡。

通过说你自己使用了 azure lb,你已经证实了这一点
争论。

无异于说“用客户端ip传播运行swarm,
确保您使用的是您设置的外部负载平衡器...或使用
云服务之一”。

我们并不是说这不是一个临时的解决方法......但它会是
如果我们都没有明确地认识到 Swarm 的承诺,则无视
缺点。

2018 年 7 月 5 日星期四,14:16 Roberto Fabrizi, notifications@ github.com
写道:

@r3pek https://github.com/r3pek虽然我同意你输了
Ingress如果用Host模式来解决这个困境,我会说它
几乎没有打败 Swarm 的全部目的,它的作用远不止一个
面向公众的入口网络。 在我们的使用场景中,我们有相同的
覆盖群:
管理复制容器,只能通过
内网 -> 他们不需要调用者的 ip,因此他们被配置
“通常”并利用入口。
非暴露容器 -> 无话可说(我相信你是
低估了通过他们的服务访问他们的能力
虽然名字)。
面向公众的容器 -> 这是一个执行 https 和 url 的 nginx 代理
基于路由。 它甚至在需要 x-forward-for 之前就被定义为全局的
客户的真实IP,所以没有真正的问题。

拥有 nginx 全局且没有入口意味着您可以通过以下方式访问它
集群的任何 ip,但它不是负载平衡的,所以我们添加了一个非常非常
廉价且易于在 nginx 前设置 L4 Azure 负载均衡器
服务。

正如你所说,主机是一种解决方法,但说完全启用它
违背 Docker Swarm 的目的有点夸张了。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-402650066或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU_ogRzwM6X0PMknXxsxmZLLTtfraks5uDdJlgaJpZM4Jf2WK
.

很明显,Docker Swarm 的入口选择了一个糟糕的负载均衡器 (IPVS)。 如果它至少支持L4 代理协议,那么这将不是问题。 除了它仍然是一个 L4(TCP) 负载平衡器,没有 L7 lb 可以提供的所有额外功能。

在 Kubernetes 中有 L4(TCP)-L7(HTTP) 负载均衡器,如nginx ingresshaproxy ingress ,它们都允许使用L4 代理协议或 L7 HTTP 标头以确保利用X-Forwarded-For来传递用户的真实数据后台IP。

我想知道 Docker Swarm ingress 的开发人员会怎么说。 可能有人必须将此案例移至https://github.com/docker/swarmkit/issues

在 Kubernetes 中有 L4(TCP)-L7(HTTP) 负载均衡器,如 nginx 入口、haproxy 入口,它们都允许使用 L4 代理协议或 L7 HTTP 标头以确保利用 X-Forwarded-For 来传递用户的真实 IP到后端。

AFAICS,那些 LB 服务没有嵌入到 K8s 中,而是需要明确部署的服务。 你也可以用 Docker swarm 做同样的事情。 我看不出这里有什么区别。 (除此之外,nginx 入口控制器似乎是“官方的”。)

据我所知,不同之处在于,即使您部署了这样的负载平衡服务,它也会被 swarmkit 负载平衡器“调用”,因此您会丢失用户 ip。 因此,如果不使用主机模式,则无法禁用 swarmkit 负载均衡器。

公平地说 - 在 k8s 中,可以有自定义入口。 蜂拥而至
不是。

swarm 认为一切都是“内置”的。 同样是这种情况
网络 - 在 k8s 中,您需要在其内置的 swarm 中设置编织等。

所以安德烈的观点(我有点同意)是——
swarm 应该将此功能作为入口的一部分,因为用户已经
无法控制它。

在星期六,2018年7月28日在下午5时07塞提[email protected]写道:

据我所知,不同之处在于,如果您部署这样的
负载均衡服务将从 swarmkit 负载均衡器“调用”
所以你失去了用户的 ip。 所以你不能禁用swarmkit
如果不使用主机模式,则为负载均衡器。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-408601274或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU1-Im_S1Awml8lO8N0Aq6rtrLH4ks5uLEzugaJpZM4Jf2WK
.

我以为我们已经完成了我们的群体皱纹,但后来我们开始并注意到对 Web 服务器容器的所有外部访问都显示为入口网络 IP。

我正在一个单节点群上运行我的堆栈,并且至少在接下来的几个月内都会这样做。 您能否为我们当前的(单节点群)用例推荐最不坏的解决方法? 我不能没有客户端 IP——太依赖它了。

我们的临时方法是在“全局”模式下运行一个简单的代理容器(IIRC 可以获取实际 NIC 的 IP),然后让它将所有连接转发到在 swarm 覆盖网络上运行的内部服务,并添加代理标头。

如果获得 x-forwarded-for 标头对您来说就足够了,那么该设置应该可以正常工作。

谢谢,@maximelb。 你最终选择了什么(例如,nginx,haproxy)?

@jamiejackson这就是事情会有所不同的地方。 在我们的例子中,我们运行的服务器承载长期运行的 SSL 连接和一个自定义的二进制协议,因此 HTTP 代理是不可能的。 所以我们创建了一个简单的 TCP 转发器并使用了一个“msgpack”头,我们可以在内部服务器上手动解包。

我对 HTTP 代理不是很熟悉,但我怀疑它们中的大多数都会为您解决问题。 :-/

嗨,马克西姆,
这对我们来说非常有趣。 你能分享你的 docker-compose 吗?
机会 ?

我试图了解这是如何工作的。 今天我们有 nginx 作为反向
代理(作为服务)和它背后的多个 docker 服务。

在您的情况下 - nginx 是否成为“全局模式”代理? 或者它是一个
特殊的 TCP 转发器。 因此,当您扩展节点数量时,代理转发器
在每个节点上。 我以某种方式认为在这种情况下 x-forwarded for
标头丢失.. 因为入口网络杀死了外部 ip
(因为没有代理协议)。

如果您能帮助我们提供更多细节,我们将不胜感激。

问候
桑迪普

2018 年 8 月 8 日,星期三,上​​午 7:18,Maxime Lamothe-Brassard <
[email protected]> 写道:

我们的临时方法是在
“全局”模式(IIRC 可以获取实际 NIC 的 IP)然后拥有它
将所有连接转发到在 swarm 上运行的内部服务
添加代理标头的覆盖网络。

如果获得 x-forwarded-for 标头对您来说就足够了,那么该设置应该
工作 AFAICT。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-411257087或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUx3DOjXb79FNjsuZ-RZVqkkhHAbYks5uOkOHgaJpZM4Jf2WK
.

@sandys当然,这是我们的

这是反向代理 docker-compose 条目:

reverseproxy:
    image: yourorg/repo-proxy:latest
    networks:
      - network_with_backend_service
    deploy:
      mode: global
    ports:
      - target: 443
        published: 443
        protocol: tcp
        mode: host

这是后端服务条目:

backendservice:
    image: yourorg/repo-backend:latest
    networks:
      - network_with_backend_service
    deploy:
      replicas: 2

反向代理(后端)的目标是tasks.backendservice (每个副本都有 A 记录)。 如果后端服务在默认的 swarm 覆盖网络上,您可以跳过networks部分。

global位表示“在每个 Docker swarm 节点上仅部署此容器一次。端口mode: host是说“绑定到节点的本机 NIC”的端口。

希望能帮助到你。

您正在使用主机模式。 所以几乎你有一个外部负载平衡器
在整个事情面前。
您不能再依赖 Swarm,因为您处于主机模式。

这实际上是我们一直在谈论的问题:(

2018 年 8 月 8 日,星期三,20:47 Maxime Lamothe-Brassard,<
[email protected]> 写道:

@sandys https://github.com/sandys当然,这是我们的摘录
docker-compose 与相关的容器。

这是反向代理 docker-compose 条目:

反向代理:
图片:yourorg/repo- proxy:latest
网络:
- network_with_backend_service
部署:
模式:全球
端口:
- 目标:443
已发表:443
协议:tcp
模式:主机

这是后端服务条目:

后端服务:
图片:yourorg/repo-后端:最新
网络:
- network_with_backend_service
部署:
复制品:2

反向代理(后端)的目标是
tasks.backendservice(每个副本都有 A 记录)。 你可以
如果后端服务在默认 swarm 上,则跳过网络部分
覆盖网络。

全局位说“在每个 Docker 上只部署一次这个容器
群节点。 端口模式:主机是说“绑定到本机
节点的 NIC”。

希望能帮助到你。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-411442155或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU8N7KAFtOp_cPO8wpbBQqzDfpBWOks5uOwEkgaJpZM4Jf2WK
.

不是 100% 确定你的意思,但在外部我们使用一个 DNS,每个集群节点都有一个 A 记录。 这提供了廉价的“平衡”而无需外部移动部件。 当客户端发出请求时,他们选择了一个随机的 A 记录,并连接到集群节点之一上的 443。

在那里,在该特定节点上运行并侦听 443 的反向代理获得本机连接,包括实际的客户端 IP。 然后,该反向代理容器添加一个标头,并使用 swarm 覆盖网络 (tasks.backend) 将连接转发到另一个内部容器。 由于它使用 tasks.backend 目标,因此它还会为内部服务获取随机 A 记录。

所以从严格意义上讲,它绕过了重定向连接的覆盖网络的魔力。 相反,它使用反向代理复制了这种行为并添加了一个标头。 最终效果与覆盖网络的魔力相同(从松散的意义上讲)。 它还与运行 swarm 并行执行,这意味着我可以在同一集群上运行不需要客户端 IP 的所有其他服务,而无需为这些服务做任何其他事情。

绝不是一个完美的解决方案,但在进行修复之前(如果有的话),它可以让您无需外部组件或主要的 docker 配置。

@jamiejackson我们发现的“最不坏”的解决方法是在主机模式下使用 Traefik 作为全球服务。 他们的文档中有一个很好的
https://github.com/containous/traefik/issues/1880

希望这可以帮助。 我们也无法使用不允许我们检查实际请求者 IP 的解决方案,因此我们一直坚持使用这种杂乱无章的修复方法,直到出现某些变化。 至少出于安全原因,这似乎是一个非常普遍的需求。

理解(我们使用的是松散版本)。

但是 - 这个特定的错误议程是要求开发人员
将其构建到神奇的覆盖网络中(也许通过使用代理
协议或其他机制)

2018 年 8 月 8 日,星期三,21:22 Maxime Lamothe-Brassard,<
[email protected]> 写道:

不能 100% 确定您的意思,但在外部我们使用带有 A 的 DNS
每个集群节点的记录。 这提供了廉价的“平衡”,而无需
外部运动部件。 当客户提出请求时,他们选择了一个随机的 A
记录,并连接到集群节点之一上的 443。

在那里,在该特定节点上运行的反向代理和
侦听 443 会获得本机连接,包括实际的客户端 IP。
该反向代理容器然后添加一个标头并转发连接
使用 swarm 覆盖网络到另一个内部容器
(tasks.backend)。 因为它使用了 tasks.backend 目标,所以它也会得到一个
随机 内部服务的记录。

所以从严格意义上讲,它绕过了覆盖网络的魔力
重定向连接。 相反,它复制了这种行为
反向代理并添加一个标头。 最后的效果是一样的(在一个
松散的意义)作为覆盖网络的魔力。 它也这样做
与运行 swarm 并行,这意味着我可以运行所有其他服务
不需要在同一集群上的客户端 IP 不做任何事情
否则为那些。

绝不是一个完美的解决方案,但在进行修复之前(如果有的话)它会得到
您无需外部组件或主要 docker 配置。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-411455384或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU5RKjGc3hEk6bk-doicDa1MbYGAyks5uOwlIgaJpZM4Jf2WK
.

TBH 我不确定为什么没有修补入口网络以添加 ip
代理协议中的数据。

它是增量的,不会破坏现有的堆栈,它是一个定义明确的
标准,即使是大型云供应商也广泛支持,它被广泛使用
由应用程序框架支持。

这是一项重大的开发工作吗?

2018 年 8 月 8 日,星期三,21:30 Matt Glaser, notifications@ github.com 写道:

@jamiejackson https://github.com/jamiejackson “最不坏”
我们发现的解决方法是在主机模式下使用 Traefik 作为全局服务。
他们的文档中有一个很好的通用示例
https://docs.traefik.io/user-guide/cluster-docker-consul/#full-docker-compose-file_1
我们已经看到一些可能与此设置相关或无关的错误,但是
Traefik 是一个很棒的项目,它在 Swarm 上似乎非常稳定。 有一个
他们的问题页面上的整个线程(循环回到这里:)),与
类似的解决方法:
包含/traefik#1880
https://github.com/containous/traefik/issues/1880

希望这可以帮助。 我们也不能使用不允许我们这样做的解决方案
检查实际的请求者 IP,所以我们一直坚持这个 kludge 修复,直到
有什么变化。 出于安全原因,这似乎是一个非常普遍的需求
至少。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-411458326或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU7NNbsW44L95VYCvlyL_Bje-h6L9ks5uOwsUgaJpZM4Jf2WK
.

好吧,Docker 目前不涉及入口流量,所以绝对至少添加不是微不足道的。
还要记住,这是一个开源项目,如果你真的想要一些东西,那么通常取决于你来实现它。

+1,这真的是一个表演者。
我相信大多数应用程序都需要真正的客户端 IP。 想想邮件服务器堆栈——你不能接受来自任意主机的邮件。

我们切换到proxy_protocol nginx全局流实例主机模式,转发到复制的应用proxy_nginx。 目前这已经足够了。

服务全局 nginx_stream

stream {
    resolver_timeout 5s;
    # 127.0.0.11 is docker swarms dns server
    resolver 127.0.0.11 valid=30s;
    # set does not work in stream module, using map here
    map '' $upstream_endpoint {
        default proxy_nginx:443;
    }

    server {
        listen 443;
        proxy_pass $upstream_endpoint;
        proxy_protocol on;
    }
}

服务复制 nginx_proxy

server {
    listen 443 ssl http2 proxy_protocol;
    include /ssl.conf.include;

    ssl_certificate /etc/nginx/certs/main.crt;
    ssl_certificate_key /etc/nginx/certs/main.key;

    server_name example.org;

    auth_basic           "closed site";
    auth_basic_user_file /run/secrets/default.htpasswd;

    # resolver info in nginx.conf
    set $upstream_endpoint app;
    location / {
        # relevant proxy_set_header in nginx.conf
        proxy_pass http://$upstream_endpoint;
    }
}

是否可以通过 nginx_stream 的整个 nginx 配置和
nginx_proxy 和他们的 Swarm 配置?

如果它有效,那就太棒了!

在 2018 年 9 月 11 日星期二 17:14 rubot, notifications @github.com 写道:

我们切换到proxy_protocol nginx 全局流实例,也就是
转发到复制的应用程序 proxy_nginx。 这工作得很好
暂时。

服务全局 nginx_stream

溪流 {
resolver_timeout 5s;
# 127.0.0.11 是 docker swarms dns 服务器
解析器 127.0.0.11 有效 = 30 秒;
# set 在流模块中不起作用,这里使用 map
地图 '' $upstream_endpoint {
默认代理_nginx :443;
}

server {
    listen 443;
    proxy_pass $upstream_endpoint;
    proxy_protocol on;
}

}

服务复制 nginx_proxy

服务器 {
听 443 ssl http2 proxy_protocol;
包括/ssl.conf.include;

ssl_certificate /etc/nginx/certs/main.crt;
ssl_certificate_key /etc/nginx/certs/main.key;

server_name example.org;

auth_basic           "closed site";
auth_basic_user_file /run/secrets/default.htpasswd;

# resolver info in nginx.conf
set $upstream_endpoint app;
location / {
    # relevant proxy_set_header in nginx.conf
    proxy_pass http://$upstream_endpoint;
}

}


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-420244262或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU5K-gK09XdI9NxLlT36IrJP7U7_cks5uZ6IrgaJpZM4Jf2WK
.

@sandys我有一个基于 haproxy 的解决方案,用于通过环境变量配置

是否可以使用 Swarm 配置来通过 nginx_stream 和 nginx_proxy 的整个 nginx 配置? 如果它有效,那就太棒了!

@sandys是这样的:
https://gist.github.com/rubot/10c79ee0086a8a246eb43ab631f3581f

遇到了同样的问题,这个会解决吗? 似乎是应该发布的基本功能。

部署:
模式:全球
端口:

  • 目标:443 发布:443 协议:tcp 模式:主机

遵循这个建议可以解决这个问题,因为 docker swarm balancer 现在已经不在考虑范围内了。
对我来说,这是一个有效的解决方案,因为它仍然是 HA 并且我已经有了 haproxy(在 docker flow 代理容器内)。
唯一的问题是 haproxy 统计信息分布在所有副本中,因此在监视整个集群的流量时,我需要以某种方式汇总该信息。 过去,我只有一个 haproxy 实例位于 docker swarm 平衡器后面。
干杯,
雅克

在阅读 OP 的请求 ( @PanJ ) 时,似乎当前的功能现在解决了这个问题,正如几个月以来一直建议的那样。 OP 没有要求入口路由 + 客户端 IP AFAIK,他们要求一种在副本/全局获取客户端 IP 中拥有群服务的方法,这现在是可行的。 两个主要的改进领域允许这种情况发生:

  1. 我们现在可以创建一个 Swarm 服务,将端口“发布”到主机 IP,跳过入口路由层
  2. 同一个服务可以同时连接到其他网络,如覆盖,因此它可以访问具有覆盖优势的其他服务

对于使用 18.09 引擎的我来说,我在测试中获得了两全其美的结果。 单个服务可以连接到后端覆盖网络,还可以在主机 NIC 上发布端口,并在主机 IP 上查看真实客户端 IP 的传入。 我将它与traefik 反向代理一起使用,

@PanJ这为您解决了吗?

关键是在mode: host而不是mode: ingress (默认)中发布端口。

这种模式的优点是您可以获得真实的客户端 IP 和本机主机 NIC 性能(因为它在 IPVS 封装 AFAIK 之外)。 缺点是它只会监听运行副本的节点。

对我来说,“我想使用入口 IPVS 路由并查看客户端 IP”的请求是 libnetwork 的不同功能请求。

这里发生了什么变化? 因为我们一直在使用主机模式来做到这一点
很长一段时间了。 事实上,这是该线程中建议的解决方法
好。

问题是当然你必须将此服务锁定到特定的
托管,因此 Swarm 无法将其安排在其他地方。 这是什么问题
完全 - 代理协议/IPVS等解决了这个问题。

2019 年 1 月 4 日星期五,09:34 Bret Fisher < [email protected]写道:

在阅读 OP 的请求( @PanJ https://github.com/PanJ )时,它
似乎当前的功能现在解决了这个问题,正如建议的那样
个月。 OP 没有要求入口路由 + 客户端 IP AFAIK,他们要求
一种在副本/全局获取集群服务的方法,获取客户端 IP,
现在是可行的。 两个主要的改进领域允许这种情况发生:

  1. 我们现在可以创建一个 Swarm 服务,将端口“发布”到
    主机 IP,跳过入口路由层
  2. 相同的服务可以附加到其他网络,如覆盖在
    同时,因此它可以访问具有覆盖优势的其他服务

对于使用 18.09 引擎的我来说,我在测试中获得了两全其美的结果。 一种
单个服务可以连接到后端覆盖网络并发布
主机 NIC 上的端口,并查看主机 IP 上传入的真实客户端 IP。 我是
将其与 traefik 反向代理一起使用以记录 traefik 中的客户端 IP 流量
用于后端服务
https://github.com/BretFisher/dogvscat/blob/7e9fe5b998f2cf86951df3f443714beb413d63fb/stack-proxy-global.yml#L75-L83
我觉得这可以解决我见过的大多数“记录真实的
知识产权”。

@PanJ https://github.com/PanJ这为您解决了吗?

关键是在模式:主机而不是模式:入口(
默认)。

这种模式的优点是您可以获得真实的客户端 IP 和本机主机 NIC
性能(因为它在 IPVS 封装 AFAIK 之外)。 缺点是
只会监听运行副本的节点。

对我来说,“我想使用入口 IPVS 路由并且还看到”的请求
客户端 IP”是 libnetwork 的不同功能请求。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451348906或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUzs15UVWOVl54FLwBJSZJKX-9D0jks5u_tLPgaJpZM4Jf2WK
.

@BretFisher mode: host只是一种解决方法,而不是解决方案。 正如@sandys所说,解决方法几乎没有什么注意事项,我们不应将此问题视为已解决。

我不确定自从发现解决方法后是否有任何改进。 我已经转移到 Kubernetes 很长一段时间了,但仍然很惊讶这个问题仍然存在两年多。

我仍然有点惊讶,为什么人们认为这是一个错误。 从我的
甚至转移到 kubernetes 的声明也不够充分
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。 你要么
有一个外部 LB,或者使用像 nginx 入口代理这样的东西,它必须
作为守护进程运行。 如果我错了,请纠正我,但我们有相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人可以
检查并打包我提出的上述 tcp 流解决方案以获得
类似于 nginx 代理行为。 接受吧,那群人需要
自己定制

PanJ [email protected] schrieb am Fr., 4. Jan. 2019, 09:28:

@BretFisher https://github.com/BretFisher模式:主机只是一个
解决方法,但不是解决方案。 作为@sandys https://github.com/sandys
说解决方法有几个警告,我们不应该考虑这个问题
作为固定。

我不确定是否有任何改进,因为解决方法已经
发现。 我已经转移到 Kubernetes 很长一段时间了,但仍然是
令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451382365或静音
线程
https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK
.

您甚至可以扩展 dockerflow 项目并添加一个 nginx 变体来启动
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了蜂群
会引发额外的系统容器,因为你知道有一堆
他们与 kubernetes。 难道这不是swarm对纤薄资源的力量吗?
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr., 4. Jan. 2019, 09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从我的
甚至转移到 kubernetes 的声明也不够充分
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。 你要么
有一个外部 LB,或者使用像 nginx 入口代理这样的东西,它必须
作为守护进程运行。 如果我错了,请纠正我,但我们有相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人可以
检查并打包我提出的上述 tcp 流解决方案以获得
类似于 nginx 代理行为。 接受吧,那群人需要
自己定制

PanJ [email protected] schrieb am Fr., 4. Jan. 2019, 09:28:

@BretFisher https://github.com/BretFisher模式:主机只是一个
解决方法,但不是解决方案。 作为@sandys https://github.com/sandys
说解决方法有几个警告,我们不应该考虑这个问题
作为固定。

我不确定是否有任何改进,因为解决方法已经
发现。 我已经转移到 Kubernetes 很长一段时间了,但仍然是
令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451382365或静音
线程
https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK
.

这些是复杂的解决方案 - 代理协议只是添加了额外的标头
信息并且是一个众所周知的标准 - haproxy、nginx、AWS elb、
等等都跟着它。 https://www.haproxy.com/blog/haproxy/proxy-protocol/

更改的表面积将仅限于内置的 Swarm
入口(将添加此支持的地方)。 所有服务都会有它
可用的。

2019 年 1 月 4 日星期五,14:36 rubot < [email protected]写道:

您甚至可以扩展 dockerflow 项目并添加一个 nginx 变体来启动
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了蜂群
会引发额外的系统容器,因为你知道有一堆
他们与 kubernetes。 难道这不是swarm对纤薄资源的力量吗?
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr., 4. Jan. 2019, 09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从我的
甚至转移到 kubernetes 的声明也不够充分
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。 你
任何一个
有一个外部 LB,或者使用像 nginx 入口代理这样的东西,它必须
作为守护进程运行。 如果我错了,请纠正我,但我们有相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人可以
检查并打包我提出的上述 tcp 流解决方案以获得
类似于 nginx 代理行为。 接受吧,那群人需要
自己定制

PanJ [email protected] schrieb am Fr., 4. Jan. 2019, 09:28:

@BretFisher https://github.com/BretFisher模式:主机只是一个
解决方法,但不是解决方案。 作为@sandys https://github.com/sandys
说解决方法有几个警告,我们不应该考虑这个
问题
作为固定。

我不确定是否有任何改进,因为解决方法已经
发现。 我已经转移到 Kubernetes 很长一段时间了,但仍然

令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451382365 ,或
沉默的
线程
<
https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451389574或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU2FCEGFs5v6IOEy6AqjcBMl7IqEiks5u_xmTgaJpZM4Jf2WK
.

正如我所说,检查上面的 tcp 流解决方案,它已经使用了代理
协议。
添加代理协议还需要在容器内进行配置,如果
添加到上游集群。 我看不到任何价值,除了更清洁,也许更好
在您的请求中记录目标

Sandeep Srinivasa [email protected] schrieb am Fr., 4. Jan. 2019,
11:37:

这些是复杂的解决方案 - 代理协议只是添加了额外的标头
信息并且是一个众所周知的标准 - haproxy、nginx、AWS elb、
等等都跟着它。 https://www.haproxy.com/blog/haproxy/proxy-protocol/

更改的表面积将仅限于内置的 Swarm
入口(将添加此支持的地方)。 所有服务都会有它
可用的。

2019 年 1 月 4 日星期五,14:36 rubot < [email protected]写道:

您甚至可以扩展 dockerflow 项目并将 nginx 变体添加到
开始
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了蜂群
会引发额外的系统容器,因为你知道有一堆
他们与 kubernetes。 难道这不是swarm对纤薄资源的力量吗?
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr., 4. Jan. 2019, 09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从我的
甚至转移到 kubernetes 的声明也不够充分
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。 你
任何一个
有一个外部 LB,或者使用类似 nginx 入口代理的东西
必须
作为守护进程运行。 如果我错了,请纠正我,但我们有相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人可以
检查并打包我提出的上述 tcp 流解决方案以获得
类似于 nginx 代理行为。 接受吧,那群人需要

自己定制

PanJ [email protected] schrieb am Fr., 4. Jan. 2019, 09:28:

@BretFisher https://github.com/BretFisher模式:主机只是一个
解决方法,但不是解决方案。 作为@sandys <
https://github.com/sandys>
说解决方法有几个警告,我们不应该考虑这个
问题
作为固定。

我不确定是否有任何改进,因为解决方法已经
发现。 我已经转移到 Kubernetes 很长一段时间了,但仍然

令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451382365
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK
>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451389574 ,或
沉默的
线程
<
https://github.com/notifications/unsubscribe-auth/AAEsU2FCEGFs5v6IOEy6AqjcBMl7IqEiks5u_xmTgaJpZM4Jf2WK

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451409453或静音
线程
https://github.com/notifications/unsubscribe-auth/AAPgu83fSrSzfopOlDXsDooN1tMboGZaks5u_y8EgaJpZM4Jf2WK
.

上面的解决方案需要主机模式绑定。 这是大问题。 它
消除了使用 docker scheduler 分配容器的可能性
到不同的主机 - 我不再是网状网络的一部分。

2019 年 1 月 4 日星期五,17:28 rubot < [email protected]写道:

正如我所说,检查上面的 tcp 流解决方案,它已经使用了代理
协议。
添加代理协议还需要在容器内进行配置,如果
添加到上游集群。 我看不到任何价值,除了更清洁,也许更好
在您的请求中记录目标

Sandeep Srinivasa [email protected] schrieb am Fr., 4. Jan. 2019,
11:37:

这些是复杂的解决方案 - 代理协议只是添加了额外的标头
信息并且是一个众所周知的标准 - haproxy、nginx、AWS elb、
等等都跟着它。 https://www.haproxy.com/blog/haproxy/proxy-protocol/

更改的表面积将仅限于内置的 Swarm
入口(将添加此支持的地方)。 所有服务都会有

可用的。

2019 年 1 月 4 日星期五,14:36 rubot < [email protected]写道:

您甚至可以扩展 dockerflow 项目并将 nginx 变体添加到
开始
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了
一群
会增加额外的系统容器,因为你知道有很多

他们与 kubernetes。 难道这不是swarm对纤薄资源的力量吗?
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr., 4. Jan. 2019, 09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从我的
甚至转移到 kubernetes 的声明也不是一个观点
足够的
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。 你
任何一个
有一个外部 LB,或者使用类似 nginx 入口代理的东西
必须
作为守护进程运行。 如果我错了,请纠正我,但我们有
相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人
可以
检查并打包我提出的上述 tcp 流解决方案以获得
类似于 nginx 代理行为。 接受吧,那群人需要

自己定制

PanJ [email protected] schrieb am Fr., 4. Jan. 2019, 09:28:

@BretFisher https://github.com/BretFisher模式:仅主机
一种
解决方法,但不是解决方案。 作为@sandys <
https://github.com/sandys>
说解决方法有几个警告,我们不应该考虑
这个
问题
作为固定。

我不确定是否有任何改进,因为解决方法有

发现。 我已经转移到 Kubernetes 很长一段时间了
仍然

令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451382365
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK

>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451389574 ,或
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAEsU2FCEGFs5v6IOEy6AqjcBMl7IqEiks5u_xmTgaJpZM4Jf2WK
>

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451409453 ,或
沉默的
线程
<
https://github.com/notifications/unsubscribe-auth/AAPgu83fSrSzfopOlDXsDooN1tMboGZaks5u_y8EgaJpZM4Jf2WK

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451424992或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU-q-I3fXVAP9JcGgTdJJOzI7b575ks5u_0HIgaJpZM4Jf2WK
.

正如我所说,kubernetes nginx ingress 也需要主机模式绑定,称为
守护进程。 外部 LB 连接到 nodeports,这也需要主机模式
在服务中,或在服务中手动配置代理协议。 Kubernetes
处理同样的问题,仍然。
从我的角度来看,swarm 的一个可能的功能请求是
使网络提供商可插入。 这将使使用
lvs/iptables 以外的其他技术

Sandeep Srinivasa [email protected] schrieb am Fr., 4. Jan. 2019,
13:05:

上面的解决方案需要主机模式绑定。 这是大问题。 它
消除了使用 docker scheduler 分配容器的可能性
到不同的主机 - 我不再是网状网络的一部分。

2019 年 1 月 4 日星期五,17:28 rubot < [email protected]写道:

正如我所说,检查上面的 tcp 流解决方案,它已经使用了代理
协议。
添加代理协议还需要在容器内进行配置
如果
添加到上游集群。 我看不到任何价值,除了清洁剂,也许
更好的
在您的请求中记录目标

桑迪普斯里尼瓦沙[email protected] schrieb是大一,4月
2019年,
11:37:

这些是复杂的解决方案 - 代理协议只是增加了额外的
标题
信息并且是一个众所周知的标准 - haproxy、nginx、AWS
易北,
等等都跟着它。
https://www.haproxy.com/blog/haproxy/proxy-protocol/

更改的表面积将仅限于内置的 Swarm
入口(将添加此支持的地方)。 所有服务都会有

可用的。

2019 年 1 月 4 日星期五,14:36 rubot < [email protected]写道:

您甚至可以扩展 dockerflow 项目并将 nginx 变体添加到
开始
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了
一群
会增加额外的系统容器,因为你知道有很多

他们与 kubernetes。 这不是swarm对苗条的力量吗?
资源
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr.,2019 年 1 月 4 日,
09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从
我的
甚至转移到 kubernetes 的声明也不是一个观点
足够的
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。

任何一个
有一个外部 LB,或者使用类似 nginx 入口代理的东西
哪一个
必须
作为守护进程运行。 如果我错了,请纠正我,但我们有
相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人
可以
检查并打包我提出的上述 tcp 流解决方案
得到
类似于 nginx 代理行为。 接受吧,那群需要


自己定制

PanJ [email protected] schrieb am Fr.,2019 年 1 月 4 日,
09:28:

@BretFisher https://github.com/BretFisher模式:主机是
只要
一种
解决方法,但不是解决方案。 作为@sandys <
https://github.com/sandys>
说解决方法有几个警告,我们不应该考虑
这个
问题
作为固定。

我不确定是否有任何改进,因为解决方法有

发现。 我已经转移到 Kubernetes 很长一段时间了
仍然

令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
< https://github.com/moby/moby/issues/25526#issuecomment -451382365
,
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK

>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451389574
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAEsU2FCEGFs5v6IOEy6AqjcBMl7IqEiks5u_xmTgaJpZM4Jf2WK

>

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451409453 ,或
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu83fSrSzfopOlDXsDooN1tMboGZaks5u_y8EgaJpZM4Jf2WK
>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451424992 ,或
沉默的
线程
<
https://github.com/notifications/unsubscribe-auth/AAEsU-q-I3fXVAP9JcGgTdJJOzI7b575ks5u_0HIgaJpZM4Jf2WK

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451426276或静音
线程
https://github.com/notifications/unsubscribe-auth/AAPguw88UN68sw_TNTunZpuAGqgvexxMks5u_0NxgaJpZM4Jf2WK
.

只是为了澄清,上述解决方案在服务前面有 tcp 流
代理。 因此,您的请求绝对不是错误,而是功能请求。 和
如果网络模式可以,则此功能只能在 swarm 中实现
更改,因为主要问题仍然是在 Nat/主机级别丢失 ip

Ruben Nicolaides [email protected] schrieb am Fr., 4. Jan. 2019, 13:11:

正如我所说,kubernetes nginx ingress 也需要主机模式绑定,称为
守护进程。 外部 LB 连接到 nodeports,这也需要主机模式
在服务中,或在服务中手动配置代理协议。 Kubernetes
处理同样的问题,仍然。
从我的角度来看,swarm 的一个可能的功能请求是
使网络提供商可插入。 这将使使用
lvs/iptables 以外的其他技术

桑迪普斯里尼瓦沙[email protected] schrieb是大一,4月
2019 年 13:05:

上面的解决方案需要主机模式绑定。 这是大问题。 它
消除了使用 docker 调度程序分配的可能性
集装箱
到不同的主机 - 我不再是网状网络的一部分。

2019 年 1 月 4 日星期五,17:28 rubot < [email protected]写道:

正如我所说,检查上面的 tcp 流解决方案,它已经使用了代理
协议。
添加代理协议还需要在容器内进行配置
如果
添加到上游集群。 我看不到任何价值,除了清洁剂,也许
更好的
在您的请求中记录目标

桑迪普斯里尼瓦沙[email protected] schrieb是大一,4月
2019年,
11:37:

这些是复杂的解决方案 - 代理协议只是增加了额外的
标题
信息并且是一个众所周知的标准 - haproxy、nginx、AWS
易北,
等等都跟着它。
https://www.haproxy.com/blog/haproxy/proxy-protocol/

更改的表面积将仅限于内置的 Swarm
入口(将添加此支持的地方)。 所有服务都将


可用的。

2019 年 1 月 4 日星期五,14:36 rubot < [email protected]写道:

您甚至可以扩展 dockerflow 项目并将 nginx 变体添加到
开始
swarn 的 kubernetes-ingressproxy。 绝对这一切都挤满了
一群
会引发额外的系统容器,因为你知道有一个


他们与 kubernetes。 这不是swarm对苗条的力量吗?
资源
项目要精益?

Ruben Nicolaides [email protected] schrieb am Fr.,2019 年 1 月 4 日,
09:48:

我仍然有点惊讶,为什么人们认为这是一个错误。 从
我的
甚至转移到 kubernetes 的声明也不是一个观点
足够的
回答。 正如我看到的 kubernetes 有完全相同的问题/行为。

任何一个
有一个外部 LB,或者使用类似 nginx 入口代理的东西
哪一个
必须
作为守护进程运行。 如果我错了,请纠正我,但我们有
相同的
这里的确切情况,但这里没有准备好的自动解决方案。 有人
可以
检查并打包我提出的上述 tcp 流解决方案
得到
类似于 nginx 代理行为。 接受吧,那群人
需要

自己定制

PanJ [email protected] schrieb am Fr.,2019 年 1 月 4 日,
09:28:

@BretFisher https://github.com/BretFisher模式:主机是
只要
一种
解决方法,但不是解决方案。 作为@sandys <
https://github.com/sandys>
说解决方法有几个警告,我们不应该考虑
这个
问题
作为固定。

我不确定是否有任何改进,因为解决方法有

发现。 我已经转移到 Kubernetes 很长一段时间了
仍然

令人惊讶的是,这个问题仍然开放了两年多。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
<
https://github.com/moby/moby/issues/25526#issuecomment-451382365>,
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu40OJ-uNKORD-LAD12m1lafxzMiSks5u_xCcgaJpZM4Jf2WK

>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451389574
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAEsU2FCEGFs5v6IOEy6AqjcBMl7IqEiks5u_xmTgaJpZM4Jf2WK

>

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451409453
或者
沉默的
线程
<

https://github.com/notifications/unsubscribe-auth/AAPgu83fSrSzfopOlDXsDooN1tMboGZaks5u_y8EgaJpZM4Jf2WK
>

.


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451424992 ,或
沉默的
线程
<
https://github.com/notifications/unsubscribe-auth/AAEsU-q-I3fXVAP9JcGgTdJJOzI7b575ks5u_0HIgaJpZM4Jf2WK

.


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-451426276或静音
线程
https://github.com/notifications/unsubscribe-auth/AAPguw88UN68sw_TNTunZpuAGqgvexxMks5u_0NxgaJpZM4Jf2WK
.

  1. 经过这么长的线程,我试图用一个完整的例子来记录当前的功能集。
  2. 我在 OP 的请求中没有看到您的具体需求。 @PanJ要求查看开箱即用的客户端 IP,截至 2018 年年中,您可以这样做。 我没有看到他们要求它也使用入口路由网格。

无论您将其称为错误还是功能请求,没有源 nat 的入口网格(在我看来)都是必不可少的。 当无法看到真正的源 IP 时,有许多应用程序会中断。 当然,在 Web 服务器的情况下,您可以使用主机节点进行反向代理并添加客户端 IP 标头。 但是,这会增加开销,并且可能不是非基于 Web 的应用程序的选项。 对于实际需要数据包上的真实源 IP 正确的应用程序,唯一的选择是不使用入口网格。 这首先抛弃了使用 swarm 的很大一部分好处。

请让我们知道此问题何时修复?!
我们应该改用 kuberneties 吗?

我遇到了同样的问题……目前我还没有找到解决办法。

当有人找到此行为的解决方案时,请在此处报告。

谢谢!

我有同样的问题。 我有一个 apache httpd 服务器,我想记录所有访问,以便稍后提取有关我们从哪些国家/地区接收请求的统计信息。

我自己在试图弄清楚为什么php:apache没有正确记录主机头字段时偶然发现了这个问题。 我感到震惊和失望,这些年来这还没有奏效。 当主机字段不断记录用户态代理 IP 时,我们应该如何使用 Swarm 模式进行 Web 托管? 我一直无法通过 Swarm 模式找到解决此问题的方法。 我想我可以使用 Classic Swarm(基于容器)和 Consul 之类的东西,但我觉得这是倒退。

我为我的场景找到了一个可接受的解决方案:

services:
  server:
    image: httpd:2
    deploy:
      mode: global
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
    networks:
      - my_second_service
      - another_great_software

这将导致 apache 监听主机计算机而不是覆盖网络后面(读取正确的远程 IP 地址),同时仍然通过networks选项将请求代理到其他服务并通过使用它来实现“高可用性”到处跑

@rafaelsierra - 这是我

@SysEngDan是的,您只能将单个容器绑定到 80/443 端口,但在我的情况下,这不是问题,因为绑定到此端口的容器仅负责将所有请求代理到其他容器在覆盖网络后面运行。

您可以使用相同的解决方案,让单个 nginx/apache 容器接收所有请求并基于 vhost 代理到正确的容器,并且这些容器不必绑定到主机

@rafaelsierra -直言,我不确定您是否理解此票中记录的问题。 如果我按照您在上一段中提到的那样配置服务,问题是客户端 IP 不会传递给仅在覆盖网络中侦听的容器。 如果我直接绑定到主机,那不是问题。 如果我们依赖从外部(主机)到内部(覆盖)的 docker 网络代理,目标 Apache 容器将不会接收原始客户端 IP 地址,而是接​​收代理的 IP(来自 docker 网络)。

@SysEngDan我确实理解这个问题,而且由于过去 2 年没有解决方案(老实说,我不确定这是否“可修复”),我不得不想出一个适合我需要的替代解决方案(限制访问基于远程 IP 地址)。

有一个容器侦听主机上的端口 80/443,然后代理到其他容器(使用适当的 HTTP 标头,我没有提到,因为超出了这个问题的范围)解决了我的问题,我想分享这个解决方案对于因重叠网络无法传递远程 IP 地址而面临类似问题的人

哦,我明白你在那里做了什么……对不起,我错过了。 您切断了覆盖网络,而是将面向外部的容器直接附加到服务网络(当您在不指定网络的情况下启动新服务时自动创建的网络)。 好的,我认为这确实有效。 增加的开销是将服务网络添加到 docker-compose 文件的任务。 我想知道当主机容器启动并且其中一项服务不可用时会发生什么?

在这种情况下,您将获得 502。

我没有一个 docker-compose.yml,我有多个堆栈,其中多个服务通过重叠网络相互通信,然后我有面向公众的服务,该服务绑定到主机服务器但仍然可以访问所有其他覆盖网络,以便它可以代理所有请求。

主机模式解决方法已经在这个问题上多次讨论过。 虽然对于某些有限的场景(例如某些反向代理 Web 流量设置)可能没问题,但它不是此问题的通用解决方案。 请阅读以前的帖子,而不是再次重新散列相同的“解决方案”。

@darrellenns这里有超过 200 条评论,我认为最好锁定和清理此问题,提供基本的“如果适用于您,则使用主机绑定”解决方案,而如果提供则没有官方解决方案,否则更多像我这样的人会错过然后一遍又一遍地评论同样的东西

所以,我相信这个 bug 会影响 traefiks 将 ips 列入白名单的能力。 那是对的吗?

无论如何,对于任何想要运行 swarm 模式的人来说,这是一个使用主机模式发布端口的例子。

docker service create \
--name traefik \
--constraint=node.role==manager \
--publish mode=host,target=80,published=80 \
--publish mode=host,target=443,published=443 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=bind,source=/home/$USER/dev-ops/logs,target=/dev-ops/logs \
--mount type=bind,source=/opt/data/traefik/traefik.toml,target=/traefik.toml \
--mount type=bind,source=/opt/data/traefik/acme.json,target=/acme.json \
--network traefik \
--label traefik.frontend.rule=Host:traefik.example.com \
--label traefik.port=8080 \
traefik \
--docker \
--docker.swarmMode \
--docker.watch \
--docker.exposedByDefault

@coltenkrauter我不知道它到底有什么影响,但在主机模式下我只能运行一个 traefik 服务的副本,而且我不认为它只是我一个人。 通过这种方式,我必须完全信任 traefik 的稳定性,而无需为服务中继群模式功能。

此外,正如最初报道的那样,它与 traefik 的特殊需求没有太大关系,它是用一个通用的 http 服务进行测试的,它没有接收到原始 ip,这意味着 docker swarm 模式被破坏(缺少这个重要功能),而且看起来没有人关心它。

我想继续评论这些东西,因为我希望噪音会打扰那些想要修复它的人:)(抱歉,我的用户对我也是如此)

在主机模式下,我只能运行 traefik 服务的一个副本,而且我不认为它只是我一个人。 通过这种方式,我必须完全信任 traefik 的稳定性,而无需为服务中继群模式功能。

每个主机可以运行一个实例

在主机模式下,我只能运行 traefik 服务的一个副本,而且我不认为它只是我一个人。 通过这种方式,我必须完全信任 traefik 的稳定性,而无需为服务中继群模式功能。

每个主机可以运行一个实例

是的,但是 traefik 被迫在管理器节点上工作,因为它需要它才能正常工作。 所以,一个管理节点,一个主机,一个实例

traefik 可以通过多种方式处理管理器节点,包括使用
docker 套接字代理、远程套接字或 traefik 企业。 这是一个
如何做到这一点的示例堆栈文件:
https://github.com/BretFisher/dogvscat/blob/master/stack-proxy-global.yml

2019 年 3 月 16 日星期六下午 5:25 Daniele Cruciani通知@ github.com
写道:

在主机模式下,我只能运行 traefik 服务的一个副本,而我不
认为这只是我。 这样我就必须完全信任 traefik 的稳定性
无需中继服务的群模式功能。

每个主机可以运行一个实例

是的,但是 traefik 被迫在管理节点上工作,因为它需要这个
正常工作。 所以,一个管理节点,一个主机,一个实例


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-473593956或静音
线程
https://github.com/notifications/unsubscribe-auth/AAwW31DHIwEJE1EqN3-8qj44WopocuQTks5vXWE_gaJpZM4Jf2WK
.

知道它很有趣,但是请注意,此功能在 kubernetes 上可用,但在 docker swarm 模式下不可用,并且您坚持认为可以选择运行多个 traefik 实例,但在多个节点中,如果我想在其中运行多个实例单个节点,这是不可能的,因为这是不支持的。
此外,不只是代理请求的任何其他服务都不允许映射任何端口,因为它需要一种特殊的配置,需要将每个主机映射到它,并且无论如何它需要多个节点,每个实例至少一个.

等等等等。 您可以向上滚动此讨论并找到其他相关内容。 我不认为它可以简化为您制作变通方法有多好的演示,因为这些变通方法仍然难以维护且难以遵循。 并且所有花在维护特殊情况解决方法上的时间最好花在解决问题上。

另一方面,如果这种特性对于docker swarm的模型来说是一个安全问题,只需将其标记为wontfix,我就会计划切换到kubernetes,如果是这样,我认为项目之间没有冲突,它只是明确地说它永远不会发生,所以每个人都可以采取行动,如果可能的话,在为任何类型的节点群选择 docker swarm 模式之前

kubernetes 中有很多特性在 swarm 中没有,反之亦然。 我们都根据许多因素(包括功能)来决定将哪个编排器用于特定解决方案。 没有一种工具可以解决所有问题/需求。

我只是一个试图提供帮助的社区成员。 如果您不喜欢此问题的当前解决方案,那么听起来您应该寻找其他方法来解决它,可能使用 kubernetes 之类的方法。 如果您认为 kubernetes 解决它的方式更符合您的喜好,那么这是选择一个编排器而不是另一个编排器的合理理由。

从历史上看,moby 和 swarm 维护者不会像 wontfix 那样关闭这样的问题,因为明天社区中的某个人可能会放弃解决这个问题的 PR。 另外,我认为在此之前讨论解决它的方法是对这个问题线程的有效使用。 :)

虽然不是 swarm 维护者,但我可以说,从历史上看,除了您目前可以在 repos 中看到的 PR 之外,团队不会透露未来的功能计划。

我忘了说当然欢迎您的评论(或者我说得含糊不清,抱歉)。 但我喜欢强调最初的@PanJ报告:

与此同时,我想我必须做一个解决方法,在 swarm 模式之外运行一个代理容器,并让它以 swarm 模式转发到已发布的端口(SSL 终止也应该在这个容器上完成),这打破了 swarm 的目的自我修复和编排模式。

我的意思是,这“打破了群体模式的目的”,当然只是在这个特定主题上,值得更多关注。

我试图让我的团队建立一个 PR,将代理协议添加到
入口网络。 我们不是 Golang 程序员,所以觉得有点
棘手。

但是我热切地希望 Docker 团队同意最好的和最
兼容(跨生态系统)解决方案是在代理协议上分层
支持入口网络。

复杂性在于入口网络不仅必须
注入自己的头文件,但它必须支持这样一个事实,即可能存在
已插入的上游代理协议标头(例如 Google LB 或
AWS ELB)。

2019 年 3 月 17 日星期日,12:17 Daniele Cruciani, notifications@ github.com
写道:

我忘了说当然欢迎你的评论(或者我在
晦涩的方式,对不起)。 但我喜欢加强原来的@PanJ
https://github.com/PanJ报告:

与此同时,我想我必须做一个运行
集群模式之外的代理容器并让它转发到已发布的端口
在 swarm 模式下(SSL 终止也应该在这个容器上完成),这
打破了群体模式用于自我修复和编排的目的。

我的意思是这“破坏了群体模式的目的”,当然仅限于此
具体话题,值得更多关注。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-473621667或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUwNWJsGKlLejcNzS2pR0awBB4OVlks5vXeTugaJpZM4Jf2WK
.

https://stackoverflow.com/questions/50585616/kubernetes-metallb-traefik-how-to-get-real-client-ip
对于 k8s 的要求,它是分层的、完全的和可配置的

对于使用 docker swarm 在 digitalocean 上运行 nginx 并试图在 nginx 日志中获取真正的$remote_addr而不仅仅是10.255.0.2 ; 您可以使用@coltenkrauter 的解决方案。 美中不足的是,你只能使用此解决方案,这应该是确定的大多数人在主机上运行一个nginx的容器。

只需更改您的docker-compose.yml文件:

不正确

services:
  nginx:
    ports:
      - "80:80"
      - "443:443"

正确的

services:
  nginx:
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host

_edit:现在我们都保证得到正确的答案_

不使用 ingress ( mode: host )不是解决方法,当问题表明问题发生在 ingress 网络时。
没有人会只使用一台主机作为反向代理。 您需要多个具有浮动 ip 的主机,并且必须使用 swarm-mesh 来实现此设置。

也许这是不可能的,但我认为,在INGRESS链的某个阶段修改 iptables 规则以执行MASQUERADE将是一种解决方法,因此它保留了真正的源 IP。 周围不是有一些 iptables/netfilter 专家吗?

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
DOCKER-USER  all  --  anywhere             anywhere            
DOCKER-INGRESS  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DOCKER     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (2 references)
target     prot opt source               destination         

Chain DOCKER-INGRESS (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
DOCKER-ISOLATION-STAGE-2  all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-ISOLATION-STAGE-2 (2 references)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere            
DROP       all  --  anywhere             anywhere            
RETURN     all  --  anywhere             anywhere            

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere            

作为替代方案,不能 swarm 只获取原始源 ip 并创建一个X-Forwarded-For标头吗?

没有人会只使用一台主机作为反向代理。 您需要多个具有浮动 ip 的主机,并且必须使用 swarm-mesh 来实现此设置。

swarm 中的每个节点都可以运行一个反向代理实例,并通过覆盖网络将流量路由到底层服务(但只有代理知道原始 IP 地址)。

确保阅读整个线程(我看到 GitHub 隐藏了一些有用的评论,所以你必须展开那些 :disappointed :);

作为替代方案,不能 swarm 只获取原始源 ip 并创建一个X-Forwarded-For标头吗?

https://github.com/moby/moby/issues/25526#issuecomment -367642600; X-Forwarded-For是L7协议; Swarm 入口是 L4,使用 IPVS 和 DNAT

@port22通常我们同意解决方法不是解决方案,解决方案是使其可分层,请参阅@sandys#25526 评论中提出的建议

作为替代方案,不能 swarm 只获取原始源 ip 并创建

X-Forwarded-For 标题?
见#25526(评论)
https://github.com/moby/moby/issues/25526#issuecomment-367642600 ;
X-Forwarded-For 是 L7 协议; Swarm 入口是 L4,使用 IPVS 和 DNAT

正确的解决方案是在 L4 注入代理协议。 有一些
Envoy 中针对同一用例的相关利弊讨论
https://github.com/envoyproxy/envoy/issues/4128
https://github.com/envoyproxy/envoy/issues/1031

2019 年 4 月 10 日星期三上午 1:40 Sebastiaan van Stijn <
[email protected]> 写道:

没有人会只使用一台主机作为反向代理。 你想要多个
具有浮动 ip 的主机,并且必须使用 swarm-mesh 来实现此目的
设置。

swarm 中的每个节点都可以运行一个反向代理的实例,并路由
通过覆盖网络传输到底层服务的流量(但只有
代理将知道原始 IP 地址)。

确保阅读整个线程(我看到 GitHub 隐藏了一些有用的
评论,所以你必须扩展那些😞);

作为替代方案,不能 swarm 只获取原始源 ip 并创建
X-Forwarded-For 标题?

见#25526(评论)
https://github.com/moby/moby/issues/25526#issuecomment-367642600 ;
X-Forwarded-For 是 L7 协议; Swarm 入口是 L4,使用 IPVS 和 DNAT


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-481415217或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsU5KdnWQ21hJx_xzc-QROJiWbAlulks5vfPOigaJpZM4Jf2WK
.

swarm 中的每个节点都可以运行一个反向代理实例

这消除了 swarm 负载均衡器的特性,而这个问题实际上就是这个特性。
我的具体问题是,traefik 不是集群敏捷的。 它必须独立运行,除非您使用 consul 作为配置后端,然后将最大证书限制为 ~100,这对我不适用。 当然,你可以说这不是 swarm 问题,而是 traefik 的问题。 有趣的事实:traefik 说这是一个 consul 问题。 consul 说:traefik 做错了。

@port22通常我们同意解决方法不是解决方案

我的观点是,当您需要 ingress 时,不使用 ingress 并不是一种解决方法。 一种解决方法是可以在保留源 ip 的同时仍然使用 swarm 负载均衡器,即使它需要一些黑客攻击。

将 IPVS 与 DNAT 结合使用

因此我认为可以在 DNAT 规则/链中使用MASQUERADE来完成。 ?

@port22我明白你的意思,但是
也许应该有像桥接网络这样的选项https://docs.docker.com/network/overlay/#customize -the-docker_gwbridge-interface
所以为了简化设置,但主要问题仍然是覆盖网络中缺少支持。 所以选项不存在,因为这些将被忽略,如果从外部修改,dockerd 将重写规则。

我已提交了代理协议支持的功能请求以解决
此错误中的问题。

以防万一有人想添加他们的评论。

https://github.com/moby/moby/issues/39465

2019 年 4 月 10 日,星期三,21:37 Daniele Cruciani, notifications@ github.com
写道:

@port22 https://github.com/port22我明白你的意思,但是docker管理
它的网络本身,我试图让它与 shorewall 一起工作,但是
唯一的方法是为 docker 规则/链创建例外,而我没有
使用 docker swarm 模式成功(但对于 docker swarm 模式是可以的,因为
到目前为止,我禁用了所有服务,但那些运行到 swarm 中的服务)
也许应该有像桥接网络那样的选项
https://docs.docker.com/network/overlay/#customize -the-docker_gwbridge-interface
所以为了使设置变得简单,但主要问题仍然是
在覆盖网络中缺少支持。 所以选项不存在,因为
这些将被忽略,如果从
外部。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526#issuecomment-481754635或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEsUxsVQ7m9uiYbHhNKMMtkhTZV6iTNks5vfgwygaJpZM4Jf2WK
.

3年后,没有修复?

我也有同样的问题,但使用 haproxy。 虽然可以在主机模式下使用代理服务器并使用 keepalived 进行 HA,但唯一缺少的部分是负载平衡,我认为这对于简单的 Web 代理来说不是什么大问题。 除非包含复杂的脚本,或者代理和后端不在同一台物理机器上,并且网络流量对于一个 NIC 来说太高了......

那么真的不可能看到来自 Docker Swarm 外部的请求的源 IP 地址而不是内部覆盖网络私有地址吗? 仍然?

@thaJeztah Docker Inc 团队中的某个人能否向我们更新此问题的状态。 是否仍在考虑和/或工作中? 任何预计到达时间? 还是因为 Docker 与 Kubernetes 集成,这完全被忽略了? 大约 3 年前就有报道:/

@thaJeztah https://github.com/thaJeztah Docker Inc 上的某人可以吗
团队向我们通报此问题的状态。 是否还在考虑中
和/或工作过? 任何预计到达时间? 或者这完全被忽略了,因为 Docker
与 Kubernetes 集成? 大约 3 年前就有报道:/

得到这个声明(“不会修复”)真的很好,所以我可以完全
证明迁移到 kubernetes 是合理的。 真是太可惜了。

谢谢。

>

有一个建议的增强请求可以解决这个问题 - https://github.com/moby/moby/issues/39465

请在那里添加您的想法和评论

我已经对这个问题发表了评论:-)

一段时间以来,这一直是我的障碍。 我需要通过 IP 地址,经过多次搜索(哇,在这个线程中与其他人一起搜索了将近 3 年……)还没有找到任何适用于 swarm 的解决方案。

由于这个问题,我一直无法在生产中使用 swarm,如果可以添加或不添加,我正在等待官方答复。 如果没有添加此选项,欢迎提出替代解决方案。

我们在 haproxy 后面使用 traefik 时遇到了同样的问题。 我很惊讶自 2016 年以来有 254 条评论。

@Betriebsrat为什么不让 traefik 立即处理请求? haproxy真的有必要,还是只是一种习惯? 如果您在主机模式下公开 traefik,您将看到客户端 IP 地址,然后一切正常:)

我相信这个“​​解决方案”被多次提及,但人们一直错过它。

我也知道有时这不是一种选择,但我相信大多数时候这应该是可能的。

@ajardan我尝试过的解决方案对我来说不可行,因为我不止一个主机在前端响应。 理想情况下,我希望整个群都能够路由请求。 我同意对于小规模操作,只需将一项服务转换为host模式并将其用作摄取服务器就可以正常工作。

尽管在大多数情况下,将 traefik 之类的东西置于主机模式会抵消我们试图从使用 swarm 中获得的好处:(

@pattonwebz可以为在多个主机上运行多个容器的服务启用主机模式,您甚至可以使用 mode=global 来实现。 然后 traefik 将在您所有的 swarm 节点上运行并接受到指定端口的连接,然后在内部将请求路由到需要查看这些连接的服务。

我将此设置与全局模式下的服务一起使用,但仅限于管理器节点,并且对于数以万计的请求/秒运行得非常好

如果需要更多细节,我很乐意详细说明。

@pattonwebz @ajardan我正在为所有这些情况使用可配置的 haproxy 服务。 就我而言,haproxy 仅使用 2 MB 的 RAM。 我认为这是微不足道的。

@pattonwebz除了上面@ajardan的解决方案,您还可以在全局模式下使用主机网络运行https://hub.docker.com/r/decentralize/swarm-tcp-proxy以向入站流量添加代理协议支持,然后将其转发到配置为解码代理协议标头的 Traefik。

它应该只是作为 Docker Swarm 一部分的一个标志,而不是所有这些
恕我直言,令人费解的解决方案。

我们只是使用 haproxy 来管理证书和卸载 ssl。
人们一直忽略“运行是主机模式”的解决方案不是解决方案。
他们希望它与入口网络一起工作以利用 docker 负载平衡。
整个线程基本上是一个“使用主机模式”->“不可能,因为“原因””圈子,现在已经持续了 3 年。

我将再次将swarm-tcp-proxy视为一种可行的替代方案,但是在过去查看类似的事情时,总是会因这些想法而对我来说是一个交易烧杯。

在一个完美的世界中,我现有的(并且除了无法检索真实客户端 IP 的能力之外运行良好)群只会工作并传递 IP 数据,而无需任何额外的服务层或更多的代理代理。

人们一直忽略“运行是主机模式”的解决方案不是解决方案。

本身不是解决方案,但可以非常成功地使用(并且正在使用)作为解决方法。 您仍然可以使用 Docker 的本机负载均衡器- 您所做的只是在访问 Docker 的服务网格之前向主机网络堆栈添加一个层。

@Betriebsrat traefik 可以很好地处理证书和 SSL,所以我仍然不确定为什么需要它。

同样正如@matthanley之前提到的,

这甚至可以为每个服务进行配置,因此您非常灵活。

您可以尝试在 docker swarm 集群之外设置另一个 Nginx 服务器,并将请求转发到 swarm 服务。 在这个 Niginx conf 中,只需添加前向标头。 例如。
地点 / {
proxy_pass http://phpestate;

    #Proxy Settings
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;

在 docker swarm 模式下,似乎没有获得真实客户端 ip 的解决方案。

我们看到了同样的问题并通过实施来解决它:
https://github.com/moby/moby/issues/25526#issuecomment -475083415

这是一个非理想的解决方案,因为我们不能在单个节点上运行多个入口容器(猜测它们现在是全局的)

难点在于Docker处理的是TCP/UDP,而这是HTTP协议问题。 至少我希望 docker 将源 IP“伪造”为远程主机,而不是从 Swarm Mesh 提供自己的内部 IP……但这可能会破坏事情,因为返回的流量会流向错误的地方。

最简单的方法是为每个 http 请求添加原始 IP 的标头。

正确的。 只是为了具体 - 作为适用于 l4 的代理协议标头
和 l7 并被大多数已知的应用软件(以及
大型云提供商)。

我为此提交了一个单独的错误,它链接了一些评论
以上。 如果您有兴趣,请添加该错误

2019 年 9 月 5 日星期四,弗拉基米尔, notifications@ github.com 写道:

最简单的方法是为每个原始 IP 添加标头
http 请求。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/moby/moby/issues/25526?email_source=notifications&email_token=AAASYU7APUNJPLZ6AJ6XXMDQIECIJA5CNFSM4CL7MWFKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJLOTDN25000000000001
或静音线程
https://github.com/notifications/unsubscribe-auth/AAASYU4VZGKUFLL5STZ44GDQIECIJANCNFSM4CL7MWFA
.

它是 2019 年,这仍然是一个问题?? 它使 traefik 上的 ip 白名单变得痛苦。 我不应该在每个节点上都需要主机端口。

@kaysond我们的立场是放弃 Swarm。 我们一直在迁移到 AWS 和 ECS。 很抱歉,我不能发布更具建设性的内容,但最终我们需要一些有效的内容; 这不是影响我们和其他人近年来没有明显修复/反馈的唯一主要 Swarm 错误(或缺乏功能)。 最令人失望,但在那里。

@jmkgreen我们处于相同的位置,并且在过去 6 个多月的时间里从 docker swarm 转移到其他东西,因为这个问题仍然存在。 我自己已经投入了数十个小时,并投入了数百个小时的团队成员时间,但从未找到可接受的解决方法。 绑定到所有主机端口完全违背了我们浮动 LB 的目的:(

你的解决方法有什么问题? 您在主机模式 + 全局上声明您的服务并设置您的 LB 以访问所有节点,它可以工作。 因为代理是轻量级的(我使用 nginx,因为我做 https 卸载和其他东西),它部署在每台服务器上的事实不是问题,它使用的服务器资源不到 1%。 如果您在此过程中遇到任何错误,我可以帮助您([email protected])。

你的解决方法有什么问题? 您在主机模式 + 全局上声明您的服务并设置您的 LB 以访问所有节点,它可以工作。

@RemiBou当代理本身需要更新/重新启动时,外部负载均衡器不会立即检测到中断,而是继续向代理仍在重新启动的节点发送请求。 因此,根据外部 LB 配置,会有大约 30 秒的中断。

在 Swarm 中也没有办法在服务更新过程中加入一个钩子来调用外部负载均衡器并在更新期间使节点停止服务。 您也不能在容器更新之前触发脚本在容器内运行(例如,删除“ i_am_healthy ”标志并让外部 LB 通过轮询发现它正在停止服务)。

你的解决方法有什么问题?

我的问题是,使用这种解决方法,我不可能在主机上运行多个相同的服务(或多个需要相同端口的服务)。 这是我从事的项目的需要。

确实,但您不能部署一个代理服务,它只会执行此操作,然后当 ip 在 swarm 中时。您可以将其作为 http 标头转发到您的其他服务吗?

确实,但您不能部署一个代理服务,它只会执行此操作,然后当 ip 在 swarm 中时。您可以将其作为 http 标头转发到您的其他服务吗?

是的……只要瘦代理服务永远不需要重新配置或更新,就可以使用 Swarm LB 更新其背后的组件以避免停机。

有人指出https://hub.docker.com/r/decentralize/swarm-tcp-proxy使用 haproxy 来完成它。

不过有点痛。 如果您必须更新代理,您仍然有停机时间。

@ms1111 Nginx 几秒内启动,如果这个服务只管理这部分,那么你就不需要经常更新它。 恕我直言,缺点不是很重要,但您的情况可能会有所不同

你的解决方法有什么问题?

在我们的例子中,这是该变通方法与无法将主机公开端口绑定到特定 IP 地址的组合。 相反,所有需要真实访问者 IP 并支持 PROXY 协议的内部服务都将其端口暴露在主机上的0.0.0.0上,这不是最佳的。

另一个是当你每秒有数百个新连接时不可忽略的性能conntrack - 所有暴露的端口实际上都是 iptables 中的 DNAT 规则,需要有其他问题(也遇到 k8s,但 Swarm 有这个使情况更糟的额外 NAT 级别)。

对于 Docker,

醒来! 考虑到有多少人参与了这个问题,这是一个明显的问题(还有其他人有同样的原因)。 我们得到的只是一再重复有变通方法的人,尽管已经多次解释为什么该变通方法不是解决方案。 “解决方法”这个词表明这是一个临时的事情,稍后会解决。 自从这个问题产生以来已经过去 3 年多了,一直以来的回应都是“有一个解决方法”。

致所有 Swarm 用户,

让我们现实一点。 可悲的事实是,没有人,包括 Docker,真正关心 Swarm。 每个人都转向了 k8s,并且在 Swarm 中没有“真正的”投资。 该项目处于生命支持状态,等待死亡,所以不要指望这个问题会得到解决。 聪明一点,转向 k8s。

这个问题似乎被忽视太久了。 它似乎永远不会被实施。 直接切入正题并使用k8s。

@leojonathanoh能否请您详细说明 k8s 究竟是如何解决这个特定问题的 :)?

简单:代理协议

@ajatkj如前所述。 或者,如果这是不可能的,则在Service资源上使用外部负载平衡器和externalTrafficPolicy: Local 。 这就是我在这里要说的。 我正在取消订阅该线程。

为什么人们期望其他人会为他们做这些工作?

我很想成为英雄并解决这个问题,但现实是我正在做许多其他事情,这对我的日常生活没有影响。 这会影响您的日常生活吗? 我们很乐意帮助您解决这个问题!

我也多次查看过这个问题,似乎真的没有办法让它与 IPVS NAT 一起工作,而 IPVS NAT 正是神奇的群路由所使用的。

我同意 k8s 在这里更加灵活。 如果它更适合您的需求,请使用它。
抱怨它没有修复,然后威胁要切换到 k8s 在我们的问题跟踪器中确实没有地位,而且通常是无益的。

人们利用他们所拥有的知识提供帮助。 并非所有人都具备自行更改代码的技能,因此他们会创建这样的问题来帮助就必要的更改达成共识。

这里没有人争辩说您必须专门进行更改,但即使在@sandys 提出的有关代理协议的问题上,核心团队也同意更改。 那么,如果有人不知道更改是否会被接受,他们怎么能做这件事呢?

最好的方法是提出一个建议,即。 你希望工作完成后的架构是什么样的。 它带来什么? 我们失去了什么?

最好的方法是提出一个建议,即。 你希望工作完成后的架构是什么样的。 它带来什么? 我们失去了什么?

已经在这里完成:#39465

尝试主机模式网络

请在评论前阅读整个主题

《使用代理协议》,虽然确实很有趣,但并没有列出什么
需要对代码库进行更改。

也许这是一个幼稚的问题,但为什么要重写源ip呢? 无论如何,流量不会通过接口的默认网关返回吗? 即使它来自群负载均衡器,网关也可以通过负载均衡器返回它,负载均衡器已经知道流量来自哪里......

也许这是一个幼稚的问题,但为什么要重写源ip呢? 无论如何,流量不会通过接口的默认网关返回吗? 即使它来自群负载均衡器,网关也可以通过负载均衡器返回它,负载均衡器已经知道流量来自哪里......

有必要知道请求来自哪个 IP。 也许特定用户想要限制 ip,而你不能在服务运行的外部进行,即 traefik 不知道可能指定哪个用户发出的请求的内容,因此它不能排除某些用户并接受其他仅基于 ip(因为本示例中的策略是 ip + request-content => 允许/禁止)。

或者,更常见的是,仅用于记录连接。 我需要向客户收取我的服务使用费,我需要以表格形式提供:请求时间、资源量、请求源 IP。 几乎所有收费的服务都提供这种报告。

我想你误解了我的问题。 我明白为什么服务会想要看到真正的源 ip。 我想知道为什么 Docker 在它到达容器之前改变它

2019 年 11 月 1 日凌晨 1 点 47 分,Daniele Cruciani通知@ github.com 写道:

也许这是一个幼稚的问题,但为什么要重写
源 ip 开始? 流量不会通过
接口的默认网关呢? 即使它来自群负载
平衡器,网关可以通过负载平衡器返回它
已经知道流量来自哪里......

有必要知道请求来自哪个 IP。 也许一个
特定用户想限制ip,你不能在外面做
服务正在运行,即 traefik 不知道请求的内容
可以指定哪个用户正在制作它,所以它不能排除一些
用户并仅基于 ip 接受其他用户(因为此策略中的
例如 ip + request-content => 允许/禁止)。

或者,更常见的是,仅用于记录连接。 我需要向客户收费
对于我的服务使用,我需要以表格形式提供:时间
请求,资源量,请求的源IP。 几乎所有服务
billed 提供这种报告。

——
你收到这个是因为你被提到了。
直接回复此邮件或在 GitHub 上查看:
https://github.com/moby/moby/issues/25526#issuecomment -548711563

@kaysond不是一个提问的好地方。

你基本上是在问两个问题,

  1. IPVS 在技术上是如何工作的,以及
  2. 为什么 libnetwork 选择 IPVS 开始

他们都很难以不同的方式回答。

我想知道问这些问题的最佳地点是哪里,因为我现在非常想阅读这些选择的历史以及它是如何运作的,这样我就可以在这里获得更多背景信息。

@kaysond不是一个提问的好地方。

你基本上是在问两个问题,

  1. IPVS 在技术上是如何工作的,以及
  2. 为什么 libnetwork 选择 IPVS 开始

他们都很难以不同的方式回答。

任何更新?

我已经关注这个线程一段时间了,因为我偶然发现了同样的问题,但是在traefik 后面的 swarm 中旋转了几个

刚刚再次尝试使用:

Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

和以下码头工人组成:

version: "3.3"

services:

  traefik:
    image: "traefik:v2.0.0-rc3"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "containous/whoami"
    container_name: "simple-service"
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.whoami.rule=HostRegexp(`{any:.*}`)"
        - "traefik.http.routers.whoami.entrypoints=web"
        - "traefik.http.services.whoami.loadbalancer.server.port=80"

whoami 输出是:

Hostname: 085c373eb06d
IP: 127.0.0.1
IP: 10.0.1.10
IP: 172.19.0.4
RemoteAddr: 10.0.1.11:51888
GET / HTTP/1.1
Host: testserver.nub.local
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Dnt: 1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 10.0.0.2
X-Forwarded-Host: testserver.nub.local
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: ad14e372f6e9
X-Real-Ip: 10.0.0.2

所以不行。 它仍然不起作用

出于好奇......一些开发人员可以指出我管理群网络的代码吗?

刚刚再次尝试使用:

Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea838
 Built:             Wed Nov 13 07:29:52 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:22 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

和以下码头工人组成:

version: "3.3"

services:

  traefik:
    image: "traefik:v2.0.0-rc3"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "containous/whoami"
    container_name: "simple-service"
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.whoami.rule=HostRegexp(`{any:.*}`)"
        - "traefik.http.routers.whoami.entrypoints=web"
        - "traefik.http.services.whoami.loadbalancer.server.port=80"

whoami 输出是:

Hostname: 085c373eb06d
IP: 127.0.0.1
IP: 10.0.1.10
IP: 172.19.0.4
RemoteAddr: 10.0.1.11:51888
GET / HTTP/1.1
Host: testserver.nub.local
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Dnt: 1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 10.0.0.2
X-Forwarded-Host: testserver.nub.local
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: ad14e372f6e9
X-Real-Ip: 10.0.0.2

所以不行。 它仍然不起作用

您可以通过主机模式使用traefik来获取真实IP

ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host

还开着?
2020-05-08

还开着?
2020-05-08

是的,还是开了。 线程中提到了一些架构问题,这些问题强调了为什么这不能像表面上看起来那样容易解决。 在这一点上,这些问题可能无法克服。

如果您需要获取真实的用户 IP,那么这里的线程中会发布一些可能适合的替代方案。 服务的 HOST 模式似乎是最简单的方法,但它不适合某些需要在单个节点上进行可扩展性的方法。

我们在 DigitalOcean LB -> Traefik -> Apache 容器中使用 PROXY 协议取得了成功。 Apache 容器能够记录访问该服务的用户的真实 IP。 只要所有代理层都支持代理协议,理论上应该可以工作。

https://docs.traefik.io/v1.7/configuration/entrypoints/#proxyprotocol

Traefik 服务位于一个名为“ingress”的 Docker 网络上,Apache 服务有自己的堆栈网络,但作为外部网络也是“ingress”网络的一部分。

https://autoize.com/logging-client-ip-addresses-behind-a-proxy-with-docker/

2020 年仍然没有固定,真是太累了。 似乎是一个非常重要的功能

这是非常需要的。 把一些主机模式只是一个补丁,有时需要在网络后面运行NGINX(取决于使用和设置)。 请解决这个问题。

我认为对此的解决方法是在客户端获取 IP,并且在不设置主机的情况下运行 docker swarm。 前任。 将 js 用于 Web 和移动客户端,并且只接受来自可信来源的信息。 前任。 js -> get ip, backend 只接受包含 user-token 等的 ips。ip 可以在 header 中设置并通过 https 加密。 但是,我不知道性能

@Damidara16这正是我们不想做的。 这样做真的很不安全。 您可以根据需要绕过它。

糟糕的是,这仍然是一个悬而未决的问题,遗憾的是......看起来它不会很快得到修复

糟糕的是,这仍然是一个悬而未决的问题,遗憾的是......看起来它不会很快得到修复

我认为它很快就会被机器人关闭。 由于github推出了这个功能,很多bug可以忽略。

糟糕的是,这仍然是一个悬而未决的问题,遗憾的是......看起来它不会很快得到修复

我认为它很快就会被机器人关闭。 由于github推出了这个功能,很多bug可以忽略。

这是企业臃肿的团队获得社区控制权的最佳功能。

永远不可能解决这个问题。 AFAIK 每个人都认为 k8s 赢得了“比赛”,不需要 swarm,但我想说两者可以共存并根据使用它们的团队的需要和技能而正确使用。 RIP 群 :)

我使用托管 HAIP,但您可以在 swarm 前面使用其他东西,一个独立的 nginx 负载均衡器,指向您的 swarm 的 IP。
https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/

在你的群中,反向代理需要这个:

server {
        listen 443 ssl proxy_protocol;
        location / {
        proxy_set_header   X-Real-IP $proxy_protocol_addr;  # this is the real IP address 

如果您正在运行一个群,您将需要一个负载平衡器来循环请求到您的群(或粘性等)。

到目前为止,这个架构决定似乎是一个“缺失的部分”,但是,它通过提供选项和消除禁用内置功能的需要来增加灵活性,以将其替换为更适合应用程序需求的东西。

我相信我可能已经找到了解决此问题的方法,其中 _current_ 限制是服务容器副本必须全部部署到单个节点,例如使用 --constraint-add='node.hostname==mynode',或使用一组群,每个群由一个节点组成。

问题

底层问题是由 ingress_sbox 命名空间中的 iptables nat 表中的 SNAT 规则引起的,这导致容器看到的所有传入请求都具有 ingress 网络中节点的 IP 地址(例如 10.0.0.2、10.0.0.3、. ..,在默认的入口网络配置中),例如:

iptables -t nat -A POSTROUTING -d 10.0.0.0/24 -m ipvs --ipvs -j SNAT --to-source 10.0.0.2

然而,删除这个 SNAT 规则意味着虽然容器仍然接收传入的数据包 - 现在来自原始源 IP - 发送回原始源 IP 的传出数据包是通过容器的默认网关发送的,该网关不在同一个入口网络上,而是在docker_gwbridge 网络(例如 172.31.0.1),然后这些数据包就会丢失。

解决方法

因此,解决方法包括: 1. 在 ingress_sbox 命名空间中删除(实际上是禁止)此 SNAT 规则; 和 2. 为 swarm 服务容器创建一个策略路由规则,强制那些传出的数据包回到节点的入口网络 IP 地址,它会返回(例如 10.0.0.2); 3. 自动添加策略路由规则,以便每个新的服务容器在创建时立即安装。

  1. 为了禁止 SNAT 规则,我们在表的前面创建了一个规则,以防止到达通常的 SNAT:
nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t nat -I POSTROUTING -d $INGRESS_SUBNET -m ipvs --ipvs -j ACCEPT

(我们这样做,而不是仅仅删除现有的 SNAT 规则,因为 docker 似乎在创建服务的过程中多次重新创建 SNAT 规则。这种方法只是取代了该规则,使其更具弹性)。

  1. 创建容器策略路由规则:
docker inspect -f '{{.State.Pid}}' <container-id>
nsenter -n -t $NID bash -c "ip route add table 1 default via 10.0.0.2 && ip rule add from 10.0.0.0/24 lookup 1 priority 32761"
  1. 最后,将上述内容与docker event一起,我们通过此ingress-routing-daemon脚本自动修改 SNAT 规则、监视新启动的容器以及添加策略路由规则的过程:
#!/bin/bash

# Ingress Routing Daemon
# Copyright © 2020 Struan Bartlett
# --------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person 
# obtaining a copy of this software and associated documentation files 
# (the "Software"), to deal in the Software without restriction, 
# including without limitation the rights to use, copy, modify, merge, 
# publish, distribute, sublicense, and/or sell copies of the Software, 
# and to permit persons to whom the Software is furnished to do so, 
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be 
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
# SOFTWARE.
# --------------------------------------------------------------------
# Workaround for https://github.com/moby/moby/issues/25526

echo "Ingress Routing Daemon starting ..."

read INGRESS_SUBNET INGRESS_DEFAULT_GATEWAY \
  < <(docker inspect ingress --format '{{(index .IPAM.Config 0).Subnet}} {{index (split (index .Containers "ingress-sbox").IPv4Address "/") 0}}')

echo INGRESS_SUBNET=$INGRESS_SUBNET
echo INGRESS_DEFAULT_GATEWAY=$INGRESS_DEFAULT_GATEWAY

# Add a rule ahead of the ingress network SNAT rule, that will cause the SNAT rule to be skipped.
echo "Adding ingress_sbox iptables nat rule: iptables -t nat -I POSTROUTING -d $INGRESS_SUBNET -m ipvs --ipvs -j ACCEPT"
while nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t nat -D POSTROUTING -d 10.0.0.0/24 -m ipvs --ipvs -j ACCEPT; do true; done 2>/dev/null
nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t nat -I POSTROUTING -d $INGRESS_SUBNET -m ipvs --ipvs -j ACCEPT

# Watch for container start events, and configure policy routing rules on each container
# to ensure return path traffic from incoming connections is routed back via the correct interface.
docker events \
  --format '{{.ID}} {{index .Actor.Attributes "com.docker.swarm.service.name"}}' \
  --filter 'event=start' \
  --filter 'type=container' | \
  while read ID SERVICE
  do
    if [ -n "$SERVICE" ]; then

      NID=$(docker inspect -f '{{.State.Pid}}' $ID)
      echo "Container ID=$ID, NID=$NID, SERVICE=$SERVICE started: applying policy route."
      nsenter -n -t $NID bash -c "ip route add table 1 default via $INGRESS_DEFAULT_GATEWAY && ip rule add from $INGRESS_SUBNET lookup 1 priority 32761"
    fi
  done

现在,当请求到达单个节点的已发布端口时,其容器将看到发出请求的机器的原始 IP 地址。

用法

_each 和每一个_before_创建服务的群节点上以 root身份运行上述ingress-routing-daemon 。 (如果您的服务已经创建,请确保在将其缩放回正数副本之前将其缩放到 0。)守护进程将初始化 iptables,检测 docker 何时创建新容器,并将新的路由规则应用于每个新容器。

测试、用例和限制

上面已经使用多个副本进行了测试,这些副本限制在运行在多节点群上的服务上的单个节点。

它还使用多个节点进行了测试,每个节点都有一个单独的每节点服务约束到该节点,但这带来了限制,即每个节点服务必须使用不同的发布端口。 这仍然可能适用于某些用例。

如果每个节点都配置为自己的集群中的单个节点,则该方法也应该使用多个节点。 这带来了 docker swarm 不能再用于跨节点分发容器的限制,但是使用 docker 服务仍然可以有其他管理优势,例如容器副本和生命周期管理。

改进解决方法以解决更多用例

随着进一步的发展,这种方法应该能够扩展到多个节点,而无需单独的每个节点服务或拆分群。 我可以想到两种可能的方法: 1. 安排 Docker 或定制守护程序从每个节点的 ipvsadm 表中删除所有非本地 IP。 2. 扩展策略路由规则以适应将输出包路由回正确的节点。

对于 1,我们可以轮询 ipvsadm -S -n 以查找添加到任何服务的新 IP,检查每个 IP 是否是本地的,并删除任何不是。 这将允许每个节点在整个服务中充当其自己容器的负载平衡器,但到达一个节点的请求无法转发到另一个节点。 这肯定会满足我自己的用例,其中我们有自己的 IPVS 负载均衡器,位于一组服务器前面,每个服务器都运行一个 Web 应用程序,我们希望将其替换为同一应用程序的多个负载均衡容器化实例,允许我们在不丢失整个服务器的情况下推出更新。

对于 2,我们可以使用 iptables 在每个节点的 ingress_sbox iptable 中分配每个节点的 TOS(例如分配到节点入口网络 IP 的最后一个字节); 然后在容器中,安排将 TOS 值映射到连接标记,然后从连接标记映射到用于传出数据包的防火墙标记,并为每个防火墙标记选择不同的路由表,将数据包路由回原始节点。 这个规则有点笨拙,但我想应该可以很好地扩展到 2-16 个节点。

我希望以上内容有用。 我还将尝试 (2),如果我取得进展,将发布进一步的更新。

下面是入口路由守护进程ingress-routing-daemon-v2的改进版本,它扩展了策略路由规则模型,允许每个容器将其输出数据包路由回正确的节点,而无需 SNAT。

改进后的模型

除了按照以前的模型禁止 SNAT 规则之外,新模型还需要在您打算用作 IPVS 负载均衡器端点的每个节点上的 ingress_sbox 命名空间中设置一个 iptables 规则(因此通常是您的管理器节点,或这些节点的一个子集)管理器节点),它为所有发往入口网络中任何节点的数据包分配每个节点的 TOS 值。 (我们使用节点入口网络 IP 的最后一个字节。)

由于 TOS 值存储在数据包中,因此传入请求所指向的目标节点可以读取它,并且数据包已被发送。

然后在目标节点上的容器中,我们安排将任何传入数据包上的 TOS 值映射到连接标记,使用相同的值。

现在,由于同一连接上的传出数据包将具有相同的连接标记,我们将任何传出数据包上的连接标记映射到防火墙标记,再次使用相同的值。

最后,一组策略路由规则根据防火墙标记值选择不同的路由表,旨在将传出数据包路由回所需的负载均衡器端点节点。

现在,当客户端请求到达 swarm 中任何节点的已发布端口时,请求所指向的容器(无论是在同一节点和/或其他节点上)将看到发出请求的客户端的原始 IP 地址,并且能够将响应路由回原始负载平衡器节点; 反过来,这将能够将响应路由回客户端。

用法

配置

生成特定于您的 swarm 的INGRESS_NODE_GATEWAY_IPS值,通过在您想要用作负载平衡器端点的每个 swarm 节点上以 root 身份运行ingress-routing-daemon-v2 (通常只有您的管理器)节点或管理器节点的子集),注意INGRESS_DEFAULT_GATEWAY显示的值。 您只需执行此操作一次,或者在添加或删除节点时执行此操作。 您的INGRESS_NODE_GATEWAY_IPS应该看起来像10.0.0.2 10.0.0.3 10.0.0.4 10.0.0.5 (根据为入口网络定义的子网和节点数)。

运行守护进程

_each 和每一个 _ 集群节点(管理器和工作器) _before_创建您的服务时,以根用户身份运行INGRESS_NODE_GATEWAY_IPS="<Node Ingress IP List>" ingress-routing-daemon-v2 --install 。 (如果您的服务已经创建,请确保在将其缩放回正数副本之前将其缩放到 0。)守护进程将初始化 iptables,检测 docker 何时创建新容器,并将新的路由规则应用于每个新容器。

如果您需要将守护进程的活动限制为特定服务,则将[ -n "$SERVICE" ]修改[ "$SERVICE" = "myservice" ]

卸载iptables规则

在每个节点上运行ingress-routing-daemon-v2 --uninstall

测试

ingress-routing-daemon-v2脚本已经通过部署到四节点群的 Web 服务的 8 个副本进行了测试。

针对服务的 Curl 请求,定向到任何指定的负载平衡端点节点 IP,返回成功响应,并且对容器日志的检查显示应用程序将传入请求视为源自 Curl 客户端的 IP。

限制

由于 TOS 值可以存储 8 位数字,因此该模型原则上最多可以支持 256 个负载均衡器端点节点。

然而,由于该模型要求每个容器安装一个 iptables mangle 规则 + 一个策略路由规则 + 每个管理器端点节点一个策略路由表,随着此类端点节点数量的增加,性能可能会下降(尽管经验表明这是在现代硬件上 <= 16 个负载平衡器端点节点时不太可能引起注意)。

如果您将负载平衡器端点节点添加到您的 swarm - 或者想要开始使用现有管理器节点作为负载平衡器端点 - 您需要谨慎行事,因为现有容器将无法将流量路由回新的端点节点。 请尝试重新启动INGRESS_NODE_GATEWAY_IPS="<Node Ingress IP List>" ingress-routing-daemon-v2具有更新值INGRESS_NODE_GATEWAY_IPS ,然后执行所有容器的滚动更新,使用新的负载平衡器端点之前。

本地 Docker 集成的范围

我不熟悉 Docker 代码库,但我看不到ingress-routing-daemon-v2所做的任何事情,原则上不能由 Docker 本地实现,但我会将其留给 Docker 团队考虑一下,或者作为熟悉 Docker 代码的人的练习。

入口路由守护进程 v2 脚本

这是新的ingress-routing-daemon-v2脚本。

#!/bin/bash

# Ingress Routing Daemon v2
# Copyright © 2020 Struan Bartlett
# ----------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person 
# obtaining a copy of this software and associated documentation files 
# (the "Software"), to deal in the Software without restriction, 
# including without limitation the rights to use, copy, modify, merge, 
# publish, distribute, sublicense, and/or sell copies of the Software, 
# and to permit persons to whom the Software is furnished to do so, 
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be 
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
# SOFTWARE.
# ----------------------------------------------------------------------
# Workaround for https://github.com/moby/moby/issues/25526

if [ "$1" = "--install" ]; then
  INSTALL=1
elif [ "$1" = "--uninstall" ]; then
  INSTALL=0
else
  echo "Usage: $0 [--install|--uninstall]"
fi

echo
echo "  Dumping key variables..."

if [ "$INSTALL" = "1" ] && [ -z "$INGRESS_NODE_GATEWAY_IPS" ]; then
  echo "!!! ----------------------------------------------------------------------"
  echo "!!! WARNING: Using default INGRESS_NODE_GATEWAY_IPS"
  echo "!!! Please generate a list by noting the values shown"
  echo "!!! for INGRESS_DEFAULT_GATEWAY on each of your swarm nodes."
  echo "!!!"
  echo "!!! You only have to do this once, or whenever you add or remove nodes."
  echo "!!!"
  echo "!!! Then relaunch using:"
  echo "!!! INGRESS_NODE_GATEWAY_IPS=\"<Node Ingress IP List>\" $0 -x"
  echo "!!! ----------------------------------------------------------------------"
fi

read INGRESS_SUBNET INGRESS_DEFAULT_GATEWAY \
  < <(docker inspect ingress --format '{{(index .IPAM.Config 0).Subnet}} {{index (split (index .Containers "ingress-sbox").IPv4Address "/") 0}}')

echo "  - INGRESS_SUBNET=$INGRESS_SUBNET"
echo "  - INGRESS_DEFAULT_GATEWAY=$INGRESS_DEFAULT_GATEWAY"

# We need the final bytes of the IP addresses on the ingress network of every node
# i.e. We need the final byte of $INGRESS_DEFAULT_GATEWAY for every node in the swarm
# This shouldn't change except when nodes are added or removed from the swarm, so should be reasonably stable.
# You should configure this yourself, but for now let's assume we have 8 nodes with IPs in the INGRESS_SUBNET numbered x.x.x.2 ... x.x.x.9
if [ -z "$INGRESS_NODE_GATEWAY_IPS" ]; then
  INGRESS_NET=$(echo $INGRESS_DEFAULT_GATEWAY | cut -d'.' -f1,2,3)
  INGRESS_NODE_GATEWAY_IPS="$INGRESS_NET.2 $INGRESS_NET.3 $INGRESS_NET.4 $INGRESS_NET.5 $INGRESS_NET.6 $INGRESS_NET.7 $INGRESS_NET.8 $INGRESS_NET.9"
fi

echo "  - INGRESS_NODE_GATEWAY_IPS=\"$INGRESS_NODE_GATEWAY_IPS\""

# Create node ID from INGRESS_DEFAULT_GATEWAY final byte
NODE_ID=$(echo $INGRESS_DEFAULT_GATEWAY | cut -d'.' -f4)
echo "  - NODE_ID=$NODE_ID"

if [ -z "$INSTALL" ]; then
  echo
  echo "Ingress Routing Daemon v2 exiting."
  exit 0
fi

# Add a rule ahead of the ingress network SNAT rule, that will cause the SNAT rule to be skipped.
[ "$INSTALL" = "1" ] && echo "Adding ingress_sbox iptables nat rule: iptables -t nat -I POSTROUTING -d $INGRESS_SUBNET -m ipvs --ipvs -j ACCEPT"
while nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t nat -D POSTROUTING -d 10.0.0.0/24 -m ipvs --ipvs -j ACCEPT; do true; done 2>/dev/null
[ "$INSTALL" = "1" ] && nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t nat -I POSTROUTING -d $INGRESS_SUBNET -m ipvs --ipvs -j ACCEPT

# 1. Set TOS to NODE_ID in all outgoing packets to INGRESS_SUBNET
[ "$INSTALL" = "1" ] && echo "Adding ingress_sbox iptables mangle rule: iptables -t mangle -A POSTROUTING -d $INGRESS_SUBNET -j TOS --set-tos $NODE_ID/0xff"
while nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t mangle -D POSTROUTING -d $INGRESS_SUBNET -j TOS --set-tos $NODE_ID/0xff; do true; done 2>/dev/null
[ "$INSTALL" = "1" ] && nsenter --net=/var/run/docker/netns/ingress_sbox iptables -t mangle -A POSTROUTING -d $INGRESS_SUBNET -j TOS --set-tos $NODE_ID/0xff

if [ "$INSTALL" = "0" ]; then
  echo
  echo "Ingress Routing Daemon v2 iptables rules uninstalled, exiting."
  exit 0
fi

echo "Ingress Routing Daemon v2 starting ..."

# Watch for container start events, and configure policy routing rules on each container
# to ensure return path traffic for incoming connections is routed back via the correct interface
# and to the correct node from which the incoming connection was received.
docker events \
  --format '{{.ID}} {{index .Actor.Attributes "com.docker.swarm.service.name"}}' \
  --filter 'event=start' \
  --filter 'type=container' | \
  while read ID SERVICE
  do
    if [ -n "$SERVICE" ]; then

      NID=$(docker inspect -f '{{.State.Pid}}' $ID)
      echo "Container ID=$ID, NID=$NID, SERVICE=$SERVICE started: applying policy routes."

      # 3. Map any connection mark on outgoing traffic to a firewall mark on the individual packets.
      nsenter -n -t $NID iptables -t mangle -A OUTPUT -p tcp -j CONNMARK --restore-mark

      for NODE_IP in $INGRESS_NODE_GATEWAY_IPS
      do
        NODE_ID=$(echo $NODE_IP | cut -d'.' -f4)

    # 2. Map the TOS value on any incoming packets to a connection mark, using the same value.
        nsenter -n -t $NID iptables -t mangle -A PREROUTING -m tos --tos $NODE_ID/0xff -j CONNMARK --set-xmark $NODE_ID/0xffffffff

    # 4. Select the correct routing table to use, according to the firewall mark on the outgoing packet.
        nsenter -n -t $NID ip rule add from $INGRESS_SUBNET fwmark $NODE_ID lookup $NODE_ID prio 32700

    # 5. Route outgoing traffic to the correct node's ingress network IP, according to its firewall mark
    #    (which in turn came from its connection mark, its TOS value, and ultimately its IP).
        nsenter -n -t $NID ip route add table $NODE_ID default via $NODE_IP dev eth0

      done

    fi
  done

您好@struanb ,我不明白您的 v2 脚本中的卸载部分是如何工作的,是否缺少某些内容?

你好@jrbecart。 我希望不是。 在安装 iptables 规则之前,您会看到有两个使用iptables -D删除任何预先存在的规则的 while 循环。 这是一种安全措施,以防脚本连续多次使用--install运行,而没有对--uninstall进行任何干预调用。

因此,当使用 --uninstall 调用脚本时,脚本退出时这些规则将被删除,并且尚未添加新规则。

希望这能回答你的问题。

大家好,我想告诉你,我发现了一个解决这个问题的方法,除了很好地定义 NGINX 配置之外,没有安装和配置任何其他东西。 我知道我们所有人都尝试过不同的方法。 这个是无意中发现的。 老实说,我很久以前就放弃了。 嗯,直到今天。 当我在实现一个监控系统时,我能够使用NGINX日志获得源IP,真正的源IP,所以我开始调试这怎么可能。

这是这种日志的示例

10.0.0.2 - - [19/Nov/2020:04:56:31 +0000] "GET / HTTP/1.1" 200 58 "-" req_t=0.003 upstream_t=0.004 "<browser-info>" "<source-ip-1,source-ip2,....>"

注意:如果您使用代理(即 Cloudfare 和其他),则有多个源 IP。

信息就在那里,我的真实 IP 就在那里。 然后,我查看了日志记录 NGINX 格式以了解魔法是如何实现的,我发现了这一点:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      'req_t=$request_time upstream_t=$upstream_response_time '
                      '"$http_user_agent" "$http_x_forwarded_for"';

这意味着,魔法就在这里 -> $http_x_forwarded_for

在此之后,我更改了代理标头,如proxy_set_header X-Real-IP $http_x_forwarded_for;

最后,最后一个测试,使用 NodeJS 项目的信息,在类似生产的系统中,使用 Docker Swarm 和覆盖网络,大约有 4 个虚拟机,猜猜看,它成功了! 我终于可以得到真正的IP地址了。

我很高兴,因为这个问题已经打开了很长时间,但我认为这就是答案。 我使用的版本是:

Docker version: 19.03.8
NGINX version: nginx/1.14.2

我会等待你的反馈。 我希望你能得到和我一样的结果。

干杯!
塞巴斯蒂安。

PS:使用另一个网络接口试试这个,也就是说,在本地主机之外,因为你会在日志中找到一个“-”,而不是你的真实 IP 地址。 尝试在互联网上测试它,完全在您的家庭网络之外。

奖励:我还可以将 IP 地址映射到地理位置,使用查找表,计算它们并将其放在地图上,所以答案是肯定的,这就是我们正在寻找的人:)

@sebastianfelipe这些年来,这是一个很大的主张。 您确定在此线程中没有使用主机模式或其他解决方法吗?

@sebastianfelipe这些年来,这是一个很大的主张。 您确定在此线程中没有使用主机模式或其他解决方法吗?

我确定。 我没有在所有这些连接的服务上使用网络主机。 我刚刚部署了一个堆栈,在类似生产的环境中使用覆盖网络,包括一个 Digital Ocean 负载均衡器,并且它工作正常。 我的意思是,我不能比这更好地测试它。 是 100% 真实的。

@sebastianfelipe我猜数字海洋负载均衡器正在将用户的 IP 地址附加到 X-Forwarded-For 标头。 这是一种已知的解决方法,它不能解决在独立 Docker Swarm 模式下检索用户 IP 的问题。

@beornf我试图睡觉,然后我读了你的通知,所以我不得不醒来并尝试一种没有 Digital Ocean 负载均衡器的方法,但它失败了。 你是对的,当添加负载均衡器时,Digital Ocean 在那里添加了一个魔力。 这发生在$http_x_forwarded_for变量上。 Digital Ocean 负载均衡器将信息添加到另一个 NGINX 变量中,这些信息不是由 Docker Swarm 直接添加的。 可能这可能会导致采用“类似虚拟”的方法来为每种情况提供真正的解决方案。 至少 Digital Ocean 客户现在很高兴知道如何处理这个问题。

@beornf @sebastianfelipe添加到上下文中,CloudFlare 还添加了X-Forwarded-For并且基本上是免费的。

@beornf @sebastianfelipe添加到上下文中,CloudFlare 还添加了X-Forwarded-For并且基本上是免费的。

我认为这对我们许多需要出路来获得真正 IP 的人来说可能有用。 Cloudfare 可以调整为代理或仅 DNS。 它非常适合没有 Digital Ocean 客户。 到目前为止,这是更清洁的解决方法。 但我同意@beornf ,我们需要一个真正的解决方案,而不依赖于 Digital Ocean 或 Cloudfare 来完成这项工作。

谢谢!

此页面是否有帮助?
0 / 5 - 0 等级