See*_*one 2 haproxy docker ipvs 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这导致超时,服务器上没有建立连接,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'
Run Code Online (Sandbox Code Playgroud)
具有相同的结果。
我发现GORB似乎是为我的用例量身定制的,并为其配置了
服务:
{
"host": "<VIP>",
"port": 80,
"protocol": "tcp",
"method": "rr",
"persistent": true,
"flags": "sh-port"
}
Run Code Online (Sandbox Code Playgroud)
所述服务的后端:
{
"host": "<VIP>",
"port": 81,
"method": "nat",
"weight": 100,
"pulse": {
"type": "tcp",
"interval": "30s",
"args": null
}
}
Run Code Online (Sandbox Code Playgroud)
我使用 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
Run Code Online (Sandbox Code Playgroud)
在这种情况下,虽然服务器上没有出现连接,但 tcpdump 显示正在交换 SYN、SYNACK 和 ACK,然后是 HTTP 请求及其 ACK。
没有其他流量通过,请求最终在客户端超时。
ipvsadm 将连接注册为活动状态。
如果我将 HAProxy 设置为侦听 VIP:80 并通过 HTTP 将请求代理到 127.0.0.1:81 一切正常,但我想避免它,因为它需要所有数据都通过 HAProxy,浪费资源并且需要本地配置。
我没有想法,也不知道如何进一步排除故障。
编辑以澄清。我的问题是:
是否可以在不使用 HAProxy 或其他将数据简单地泵送到真实路由器(Traefik)的进程的情况下将流量从 VIP:80 路由到 :81/:82 等?
我们需要在相同的端口上发布单独的 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 地址和公共端口对。
例如:
然后我们相应地配置我们的脚本:
# 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 "$@"
Run Code Online (Sandbox Code Playgroud)
注意:该脚本必须安装在 dockerd 的 PATH 中,位于发行版的 iptables 命令之前。在 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
Run Code Online (Sandbox Code Playgroud)
结果是,对http://1.2.3.4/的请求转到服务 #1,而对http://1.2.3.5/ 的请求转到服务 #2。
该脚本可以根据您的需要进行自定义和扩展,并且必须安装在您将请求定向到的所有节点上,并根据该节点的公共 IP 地址进行自定义。
| 归档时间: |
|
| 查看次数: |
6087 次 |
| 最近记录: |