Link: http://blog.j2gg0s.com/2023/04/12/Kubernetes-%E7%BD%91%E7%BB%9C-kube-proxy-%E5%92%8C-ipvs.html
Dustin Specker 有个Container Networking 系列的博客, 手动模拟了 kube-proxy 在 iptables/ipvs 下的相关逻辑, 非常有助理解. 我们在这儿也从零走一遍 ipvs 相关的逻辑.
整个流程的大部分命令可以在 Makefile 中找到.
每个 Pod 都拥有自己独立的网络命名空间, 进而实现网络隔离. 即使同一 Node 上的不同 Pod 之间也无法直接访问, Pod 也无法直接访问 Node 的网络.
ip netns add foo
ip netns exec foo ip link set dev lo up
ip link add dev cni0 type bridge
ip addr add 172.22.0.1/24 dev cni0
ip link set dev cni0 up
ip link add dev veth_foo type veth peer name veth_foo_eth0
ip link set dev veth_foo master cni0
ip link set dev veth_foo up
ip link set dev veth_foo_eth0 netns foo
ip netns exec foo ip link set dev veth_foo_eth0 up
ip netns exec foo ip addr add 172.22.0.2/24 dev veth_foo_eth0
为了测试, 我们在 foo 中启动一个 http 服务.
~ ip netns exec foo python3 -m http.server -d foo 8080
Serving HTTP on 0.0.0.0 port 8080 ( http://0.0.0.0:8080/) ...
这时候, 我们就可以直接访问命名空间 foo 中的 http 服务.
~ curl 172.22.0.2:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
ip netns add bar
ip netns exec bar ip link set dev lo up
ip link add dev veth_bar type veth peer name veth_bar_eth0
ip link set dev veth_bar master cni0
ip link set dev veth_bar up
ip link set dev veth_bar_eth0 netns bar
ip netns exec bar ip link set dev veth_bar_eth0 up
ip netns exec bar ip addr add 172.22.0.3/24 dev veth_bar_eth0
在测试 foo 和 bar 的互通之间, 我们需要先允许通过 cni0 来转发流量.
iptables -t filter -A FORWARD --in-interface cni0 -j ACCEPT
iptables -t filter -A FORWARD --out-interface cni0 -j ACCEPT
ip netns exec bar curl 172.22.0.2:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
我们可以简单将 ipvs 理解为 L4 的负载均衡, 通过 ipvsadm 可以创建一个虚拟的 IP 地址, 并将相关流量转发给 foo.
ipvsadm --add-service --tcp-service 172.23.0.1:8080 --scheduler rr
ipvsadm --add-server --tcp-service 172.23.0.1:8080 --real-server 172.22.0.2:8080 --masquerading
在访问 172.23.0.1:8080 之前, 我们需要将 cni0 指定为 foo 的默认网关.
ip netns exec foo ip route add default via 172.22.0.1
~ curl 172.23.0.1:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
如果将 bar 也增加到 172.23.0.1:8080 的后端负载, 则可以免费体验 ipvs 的负载均衡功能.
ipvsadm --add-server --tcp-service 172.23.0.1:8080 --real-server 172.22.0.3:8080 --masquerading
ip netns exec bar ip route ad default via 172.22.0.1
➜ curl 172.23.0.1:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
➜ curl 172.23.0.1:8080 -s | grep im
<li><a href="im-bar">im-bar</a></li>
➜ curl 172.23.0.1:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
➜ curl 172.23.0.1:8080 -s | grep im
<li><a href="im-bar">im-bar</a></li>
k8s 中一个很经典的场景. 为了便于模拟, 我们先将 bar 中 172.23.0.1 的后端服务中踢出, 仅保留 foo.
ipvsadm --delete-server --tcp-service 172.23.0.1:8080 --real-server 172.22.0.3:8080
为了在 bar 中访问 172.23.0.1, 我们需要虚构对应的设备.
ip link add dev ipvs0 type dummy
ip addr add 172.23.0.1/32 dev ipvs0
ip netns exec bar curl 172.23.0.1:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
但是你会发现, 我们无法直接在 foo 通过 ipvs 来访问 foo.
ip netns exec foo curl 172.23.0.1:8080 --connect-timeout 1
curl: (28) Connection timeout after 1001 ms
我们需要做三件事来解决这个问题:
ip link set cni0 promisc on
iptables -t nat -A POSTROUTING -s 172.22.0.0/24 -j MASQUERADE
sysctl net.ipv4.vs.conntrack=1 --write
~ ip netns exec foo curl 172.23.0.1:8080 -s | grep im
<li><a href="im-foo">im-foo</a></li>
MadHatter 在 severfault 上的这个回答非常好的解释了 hairpin 和这次 SNAT 的原因.
有时间的话, 我们后续可以找个实际的集群, 来验证下上述的理解.
1
GopherDaily OP 冲浪冲多了,币入不敷出,多的借点给我
|
2
artnowben 2023-04-13 11:15:20 +08:00
dperf 可以测试 ipvs 的性能,DPVS 发布版本也是用 dperf 去做性能报告的
https://github.com/baidu/dperf |
3
GopherDaily OP @artnowben 昨天正好看到了你这个,ipvs 在大集群的情况下是显然优于 iptables ,又没其他可以选,测试意义不大。我倒是很期待,你们准备不准备自己维护一个对主流 gateway 的测试结果集。
|
4
artnowben 2023-04-13 11:37:29 +08:00
@GopherDaily 不维护,吃力不讨好的事。测试过 AWS 、GCP ;测试国内某大型云厂商的网卡性能,文章在国内就被屏蔽了
|
5
GopherDaily OP @artnowben 我猜也是
|