在特定 IP 地址上发布 Docker Swarm 服务

在特定 IP 地址上发布 Docker Swarm 服务

在 Centos 7.4 上,我正在设置一个集群,我想在其中运行多个路由器,这些路由器都可通过端口 80/443 访问。
目的是在单个集群上对称地托管多个环境(测试/暂存……)。

我正在使用 Docker 17.12.0-ce 和 Traefik v1.4.6 作为路由器。

基本思路是每个环境都有一个虚拟 IP 地址,并仅在该地址上发布 Traefik 端口。这在 Docker Swarm 中是无法实现的,因此我不得不让 Traefik 实例监听端口 81/82 等,并以某种方式将流量从 VIP:80 引至 :81/:82。

整个集群管理器中所有环境的虚拟 IP 地址均由 Keepalived 处理。

Traefik 的相关 docker 服务配置:

"Ports": [
          {
           "Protocol": "tcp",
           "TargetPort": 80,
           "PublishedPort": 81,
           "PublishMode": "ingress"
          },

# netstat -anp|grep 81
tcp6       7      0 :::81                   :::*                    LISTEN      4578/dockerd        

设置firewalld以允许到端口80、81、82等的流量

直接通过 VIP 上的 81 端口访问 Traefik 公开的后端服务即可。

如果 VIP 上未正确配置任何内容,则访问其 80 端口会导致连接被拒绝

Traefik docker 实例正在与我用于以下测试的同一主机上运行。

我首先尝试了基本的 DNAT:

firewall-cmd --add-forward-port=port=80:proto=tcp:toport=81:toaddr=127.0.0.1

这导致超时,服务器上似乎没有建立连接,并且 tcpdump 告诉我 SYN 被忽略

接下来我尝试使用更具体的 DNAT:

firewall-cmd --add-rich-rule='rule family=ipv4 forward-port port=80 protocol=tcp to-port=81 to-addr=127.0.0.1'

结果相同。

我发现戈登它似乎是为我的用例量身定制的,并且配备了

服务:

{
  "host": "<VIP>",
  "port": 80,
  "protocol": "tcp",
  "method": "rr",
  "persistent": true,
  "flags": "sh-port"
}

该服务的后端:

{
  "host": "<VIP>",
  "port": 81,
  "method": "nat",
  "weight": 100,
  "pulse": {
     "type": "tcp",
     "interval": "30s",
     "args": null
  }
}

我使用 ipvsadm 验证了设置并且它看起来是正确的:

# ipvsadm -l -n 
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP         <VIP>:80 rr (flag-2)
  ->        <VIP>:81              Masq    100    0          0     

在这种情况下,虽然服务器上没有出现任何连接,但 tcpdump 显示正在交换 SYN、SYNACK 和 ACK,然后是 HTTP 请求及其 ACK。
没有其他流量通过,请求最终在客户端超时。ipvsadm
将连接注册为活动连接。

如果我设置 HAProxy 来监听 VIP:80 并通过 HTTP 将请求代理到 127.0.0.1:81,那么一切就都可以正常工作,但我想避免这样做,因为它要求所有数据都通过 HAProxy,浪费资源并需要本地配置。

我没有什么主意,也不知道如何进一步排除故障。

编辑以澄清。我的问题是:
是否可以在不使用 HAProxy 或其他仅将数据泵送到真实路由器(Traefik)的进程的情况下将流量从 VIP:80 路由到 :81/:82 等?

答案1

我们需要在相同的端口上发布单独的 docker swarm 服务,但使用不同的特定 IP 地址。以下是我们的实现方式。

Docker 会将规则添加到 nat 表的 DOCKER-INGRESS 链中,用于每个已发布的端口。它添加的规则不针对特定 IP,因此通常任何已发布的端口都可以在所有主机 IP 地址上访问。以下是 Docker 将为在端口 80 上发布的服务添加的规则示例:

iptables -t nat -A DOCKER-INGRESS -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80

(您可以通过运行来查看这些内容iptables-save -t nat | grep DOCKER-INGRESS)。

我们的解决方案是在不同的端口上发布我们的服务,并使用拦截 dockerd 的 iptables 命令的脚本来重写它们,以便它们匹配正确的 IP 地址和公共端口对。

例如:

  • 服务 #1 发布在端口 1080 上,但应该监听 1.2.3.4:80
  • 服务 #2 发布在端口 1180 上,但应该监听 1.2.3.5:80

然后我们相应地配置我们的脚本:

# cat /usr/local/sbin/iptables
#!/bin/bash

REGEX_INGRESS="^(.*DOCKER-INGRESS -p tcp) (--dport [0-9]+) (-j DNAT --to-destination .*)"
IPTABLES=/usr/sbin/iptables

SRV_1_IP=1.2.3.4
SRV_2_IP=1.2.3.5

ipt() {
  echo "EXECUTING: $@" >>/tmp/iptables.log
  $IPTABLES "$@"
}

if [[ "$*" =~ $REGEX_INGRESS ]]; then
  START=${BASH_REMATCH[1]}
  PORT=${BASH_REMATCH[2]}
  END=${BASH_REMATCH[3]}
  
  echo "REQUESTED: $@" >>/tmp/iptables.log

  case "$PORT" in
     '--dport 1080') ipt $START --dport 80 -d $SRV_1_IP $END; exit $?; ;;
     '--dport 2080') ipt $START --dport 80 -d $SRV_2_IP $END; exit $?; ;;
                  *) ipt "$@"; exit $?; ;;
  esac
fi

echo "PASSING-THROUGH: $@" >>/tmp/iptables.log

$IPTABLES "$@"

注意:脚本必须安装在发行版的 iptables 命令之前的 dockerd PATH 中。在 Debian Buster 上,iptables 安装到/usr/sbin/iptables,而 dockerd 的 PATH 位于/usr/local/sbin之前/usr/sbin,因此将脚本安装在 是合理的/usr/local/sbin/iptables。(您可以通过运行 来检查 dockerd 的 PATH cat /proc/$(pgrep dockerd)/environ | tr '\0' '\012' | grep ^PATH)。

现在,当这些docker服务启动的时候,iptables规则会被重写如下:

iptables -t nat -A DOCKER-INGRESS -d 1.2.3.4/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:1080
iptables -t nat -A DOCKER-INGRESS -d 1.2.3.5/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:2080

结果是http://1.2.3.4/转到服务 #1,而请求http://1.2.3.5/前往服务 #2。

该脚本可以根据您的需要进行定制和扩展,并且必须安装在您将定向请求的所有节点上,并根据该节点的公共 IP 地址进行定制。

答案2

第一,你如果你有能力在真实网络中添加 IP,则在主机上使用多个 IP。这在 Linux 上的 Swarm 中工作。请参阅macvlan 文档并在 Google 上搜索“macvlan swarm”。

第二,您正在使用覆盖和 Swarm 的入口网络,对吗?

第三,大多数人只使用 Traefik(或者我最喜欢的http://proxy.dockerflow.com) 监听 80/443,并根据主机头路由到 Swarm 中的正确服务/堆栈。就像 Florin 问的那样,你为什么不尝试一下呢?

相关内容