我们有一个非常具体的需求,我想用 Open vSwitch 来解决。它已经以某种方式工作了 - 你能告诉我我这里缺少什么吗?
要求:连接到 mac-vlan 接口的 Docker 容器在特定端口上公开服务(需要在本地网络上广播)。我们需要在不同的端口上提供服务 - 并且无法配置运行服务的端口。我们已经尝试了不同的方法(反向代理、docker --ports 指令等),但由于各种原因,这些方法都没有奏效,主要是因为我们仍然必须坚持使用 mac-vlan 接口的 IP。
那个基础设置相当固定,我的主要目标是让它以这种方式工作,我认为这应该是可能的。
环境:Arch Linux 及其内核core/linux 5.10.9
和软件包community/openvswitch 2.14.1-1
,community/docker 1:20.10.2-4
输入 Open vSwitch:我们在 OVS Bridge 上创建了 mac-vlan 接口,并想使用 OpenFlow 指令来更改端口。
# ovs-vsctl show
... output omitted
Bridge br1
Port br1.200
tag: 200
Interface br1.200 <<< our container is connected here
type: internal
Port br1
Interface br1
type: internal
Port patch-br0
Interface patch-br0 <<< uplink to OVS bridge with physical interface
type: patch
options: {peer=patch-br1}
使用 Nginx 进行演示,应该可以与任何容器一起使用......
# docker network create -d macvlan --subnet=172.16.0.0/20 --ip-range=172.16.13.0/29 --gateway=172.16.0.1 -o parent=br1.200 mv.200
# docker run -d --name web --network mv.200 nginx
到目前为止一切正常,curl http://172.16.13.0
(在本例中为容器 Web)返回“欢迎使用 nginx!”默认页面。现在我们正在尝试以下 OpenFlow 配置,以使容器服务可在端口 9080 上访问。
变体 1:
# ovs-ofctl dump-flows br1
cookie=0x0, duration=1647.225s, table=0, n_packets=16, n_bytes=1435, priority=50,ct_state=-trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(table=0)
cookie=0x0, duration=1647.223s, table=0, n_packets=3, n_bytes=234, priority=50,ct_state=+new+trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(commit,nat(dst=172.16.13.0:80)),NORMAL
cookie=0x0, duration=1647.221s, table=0, n_packets=11, n_bytes=956, priority=50,ct_state=+est+trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(nat),NORMAL
cookie=0x0, duration=1647.219s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=-trk,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(table=0)
cookie=0x0, duration=1647.217s, table=0, n_packets=12, n_bytes=2514, priority=50,ct_state=+trk,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(nat),NORMAL
cookie=0x0, duration=84061.461s, table=0, n_packets=309364, n_bytes=36251324, priority=0 actions=NORMAL
结果变体 1:
现在curl http://172.16.13.0:9080
仅当已存在活动流时才有效,但它会在第一次尝试时中断(tcpdump -i br1.200
在服务器上)。
Client > Server : 172.16.1.51:46056 > 172.16.13.0:80 SYN
Server > Client : 172.16.13.0:80 > 172.16.1.51:46056 SYN ACK
Client > Server : 172.16.1.51:46056 > 172.16.13.0:9080 ACK (destination port not translated)
Server > Client : 172.16.13.0:9080 > 172.16.1.51:46056 RST (unknown to server)
Server > Client : 172.16.13.0:80 > 172.16.1.51:46056 SYN ACK
Client > Server : 172.16.1.51:46056 > 172.16.13.0:80 RST (already ACK'ed)
Client > Server : 172.16.1.51:46058 > 172.16.13.0:80 SYN (second curl)
Server > Client : 172.16.13.0:80 > 172.16.1.51:46058 SYN ACK
Client > Server : 172.16.1.51:46058 > 172.16.13.0:80 ACK (now with correct port 80)
... (normal TCP connection from here)
数据包#3 应该被流#3 覆盖,显然它并没有按照我想象的方式工作。
# ovs-appctl dpctl/dump-conntrack | grep 172.16.13.0
tcp,orig=(src=172.16.1.51,dst=172.16.13.0,sport=46056,dport=9080),reply=(src=172.16.13.0,dst=172.16.1.51,sport=80,dport=46056),protoinfo=(state=CLOSING)
tcp,orig=(src=172.16.1.51,dst=172.16.13.0,sport=46058,dport=9080),reply=(src=172.16.13.0,dst=172.16.1.51,sport=80,dport=46058),protoinfo=(state=TIME_WAIT)
您能否帮助我理解为什么 +trk+est 流的 ct(nat) 操作对第一个连接不起作用(但对第二个连接却起作用)?
变体 2:(将 mod_tp_dst 添加到流程 #2)
# ovs-ofctl dump-flows br1
cookie=0x0, duration=6182.935s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=-trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(table=0)
cookie=0x0, duration=6182.931s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=+new+trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=mod_tp_dst:80,ct(commit,nat(dst=172.16.13.0:80)),NORMAL
cookie=0x0, duration=6182.928s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=+est+trk,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(nat),NORMAL
cookie=0x0, duration=6182.925s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=-trk,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(table=0)
cookie=0x0, duration=6182.923s, table=0, n_packets=0, n_bytes=0, priority=50,ct_state=+trk,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(nat),NORMAL
cookie=0x0, duration=81462.938s, table=0, n_packets=302990, n_bytes=35637543, priority=0 actions=NORMAL
结果变体 2:
与变体 1(在客户端)相比,运行curl http://172.16.13.0:9080
情况略有改善。tcpdump -i eth0
Client > Server : 172.16.1.51:45974 > 172.16.13.0:9080 SYN
Server > Client : 172.16.13.0:80 > 172.16.1.51:45974 SYN ACK (response source port not translated)
Client > Server : 172.16.1.51:45974 > 172.16.13.0:80 RST (unknown to client)
Client > Server : 172.16.1.51:45974 > 172.16.13.0:9080 SYN (retransmission)
Server > Client : 172.16.13.0:9080 > 172.16.1.51:45974 SYN ACK (now with correct port 9080)
Client > Server : 172.16.1.51:45974 > 172.16.13.0:9080 ACK
这样,连接始终有效,但它也会将 SYN 重传超时添加到会话建立延迟中。
# ovs-appctl dpctl/dump-conntrack | grep 172.16.13.0
tcp,orig=(src=172.16.1.51,dst=172.16.13.0,sport=45974,dport=80),reply=(src=172.16.13.0,dst=172.16.1.51,sport=80,dport=45974),protoinfo=(state=SYN_SENT)
tcp,orig=(src=172.16.1.51,dst=172.16.13.0,sport=45974,dport=9080),reply=(src=172.16.13.0,dst=172.16.1.51,sport=80,dport=1355),protoinfo=(state=TIME_WAIT)
您能帮我理解为什么第一个 SYN ACK 未经过转换就被接收吗?流程 #5 和 ct_state=+trk 以及 action=ct(nat) 应该已经涵盖了这一点。
感谢您阅读这篇长文。我非常感谢您的任何提示!
答案1
找到了让它工作的方法,但仍然不确定为什么变体 1 不起作用......
问题的关键似乎在于变体 1 中的流#3 没有正确接收,或者 conntrack 没有可用的 NAT 信息。
这是对我有用的流程转储:
ovs-ofctl dump-flows br1
cookie=0x0, duration=160.870s, table=0, n_packets=14, n_bytes=1156, priority=50,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(table=1)
cookie=0x0, duration=160.867s, table=0, n_packets=11, n_bytes=2430, priority=50,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(table=1)
cookie=0x0, duration=184012.978s, table=0, n_packets=558802, n_bytes=60818179, priority=0 actions=NORMAL
cookie=0x0, duration=160.865s, table=1, n_packets=2, n_bytes=156, priority=50,ct_state=+new,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=ct(commit,nat(dst=172.16.13.0:80)),NORMAL
cookie=0x0, duration=160.862s, table=1, n_packets=12, n_bytes=1000, priority=50,tcp,nw_dst=172.16.13.0,tp_dst=9080 actions=mod_tp_dst:80,NORMAL
cookie=0x0, duration=160.860s, table=1, n_packets=11, n_bytes=2430, priority=50,tcp,nw_src=172.16.13.0,tp_src=80 actions=ct(nat),NORMAL