V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GopherDaily
V2EX  ›  Kubernetes

Kubernetes 网络: kube-proxy 和 ipvs

  •  
  •   GopherDaily · 2023-04-13 00:59:35 +08:00 · 1395 次点击
    这是一个创建于 588 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 中找到.

    创建独立的网络命名空间(netns) foo

    每个 Pod 都拥有自己独立的网络命名空间, 进而实现网络隔离. 即使同一 Node 上的不同 Pod 之间也无法直接访问, Pod 也无法直接访问 Node 的网络.

    ip netns add foo
    ip netns exec foo ip link set dev lo up
    

    在默认网络命名空间中创建 bridge, 并通过 veth, 将 foo 和默认空间链接起来

    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>
    

    创建另一个网络命名空间 bar, 并通过 veth 关联到 bridge

    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 代理 foo 中的 http 服务

    我们可以简单将 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>
    

    ipvs 的负载均衡功能

    如果将 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>
    

    在 Pod 中通过 Service 访问当前 Pod

    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
    

    我们需要做三件事来解决这个问题:

    • cni0 开启 promisc 来支持 hairpin
    • 对来自 foo 的流量做一次 SNAT
    • 激活 conntrack
    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 的原因.

    TODO

    有时间的话, 我们后续可以找个实际的集群, 来验证下上述的理解.

    5 条回复    2023-04-13 12:01:01 +08:00
    GopherDaily
        1
    GopherDaily  
    OP
       2023-04-13 01:01:07 +08:00   ❤️ 5
    冲浪冲多了,币入不敷出,多的借点给我
    artnowben
        2
    artnowben  
       2023-04-13 11:15:20 +08:00
    dperf 可以测试 ipvs 的性能,DPVS 发布版本也是用 dperf 去做性能报告的
    https://github.com/baidu/dperf
    GopherDaily
        3
    GopherDaily  
    OP
       2023-04-13 11:33:03 +08:00
    @artnowben 昨天正好看到了你这个,ipvs 在大集群的情况下是显然优于 iptables ,又没其他可以选,测试意义不大。我倒是很期待,你们准备不准备自己维护一个对主流 gateway 的测试结果集。
    artnowben
        4
    artnowben  
       2023-04-13 11:37:29 +08:00
    @GopherDaily 不维护,吃力不讨好的事。测试过 AWS 、GCP ;测试国内某大型云厂商的网卡性能,文章在国内就被屏蔽了
    GopherDaily
        5
    GopherDaily  
    OP
       2023-04-13 12:01:01 +08:00
    @artnowben 我猜也是
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3092 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:47 · PVG 21:47 · LAX 05:47 · JFK 08:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.