美文网首页shell脚本
docker 学习笔记4:网络与 host gw

docker 学习笔记4:网络与 host gw

作者: 董泽润 | 来源:发表于2019-12-23 14:37 被阅读0次

    TL;DR docker 网络模型比较简单,在 k8s 中己经有了最新的解决方案,所以本文着重在 host-gw 实现。由于阿里云 vpc 不支持自定义路由,实验全部基于 virtual box 虚拟机实现。

    docker 与 k8s 网络

    docker 初始化时就会生成三个 net namespace

    root@worker1:~# docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    d2a9085edd71        bridge              bridge              local
    37c243cd8d97        host                host                local
    5536f50c051a        none                null                local
    

    支持很多种模式,默认是第一个网桥,docker run 时指定 --network=host|bridge|none 等等

    • bridge 默认建立一个网桥设备,充当交换机的角色,一般都是所有容器的网关
    • host 使用本机的网络 net ns,相当于没有任何隔离
    • none 不使用网络,一般用于建立 POD 时 join pause 容器
    • overlay 可以使跨宿主机的网络通信,这个是与 swarm 相关的,和后面 k8s 的还不太一样
    • macvlan 使容器拥有 mac 地址,以便兼容传统虚拟机的使用模式

    docker 自带的网络模型可以不用看,以后只看 k8s 的就可以了,毕竟在 k8s 一统江湖的情况下,底层的 docker 也可能换成其它的容器运行时。那么 k8s 对网络有什么要求呢?只有三点,具体怎么实现,要看 cni 网络插件

    • 所有 pod 与其它 pod 可以通信,无需显示的 nat
    • 所有 node 与其它 pod 可以通信,无需显示的 nat
    • pod 所看见自己的 ip, 与其它看到的是一样的

    当前最流行的有 flannel, weave, calico, 并且每家云厂商也有自己的网络实现。

    1. 默认网桥

    docker bridge
    docker 支持很多网络模型,默认用的是 bridge 桥接,如上图所示。docker0 是一个链路二层的虚拟网桥,类似交换机的功能,ip 是 172.17.0.1,默认是宿主机上所有容器的网关。
    root@worker1:~# ip addr
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 00:16:3e:00:75:2e brd ff:ff:ff:ff:ff:ff
        inet 172.24.213.40/20 brd 172.24.223.255 scope global dynamic eth0
           valid_lft 315312504sec preferred_lft 315312504sec
    9: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
        link/ether 02:42:8f:8a:5b:5f brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.1/24 brd 172.17.0.255 scope global docker0
           valid_lft forever preferred_lft forever
    

    我们再看一下当前 iptables 的设置

    root@worker1:~# iptables-save
    # Generated by iptables-save v1.6.1 on Mon Dec 23 10:49:09 2019
    *nat
    :PREROUTING ACCEPT [0:0]
    :INPUT ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    :POSTROUTING ACCEPT [0:0]
    :DOCKER - [0:0]
    -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
    -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
    -A POSTROUTING -s 172.17.0.0/24 ! -o docker0 -j MASQUERADE
    -A DOCKER -i docker0 -j RETURN
    COMMIT
    # Completed on Mon Dec 23 10:49:09 2019
    # Generated by iptables-save v1.6.1 on Mon Dec 23 10:49:09 2019
    *filter
    :INPUT ACCEPT [65:4290]
    :FORWARD DROP [0:0]
    :OUTPUT ACCEPT [37:19882]
    :DOCKER - [0:0]
    :DOCKER-ISOLATION-STAGE-1 - [0:0]
    :DOCKER-ISOLATION-STAGE-2 - [0:0]
    :DOCKER-USER - [0:0]
    -A FORWARD -j DOCKER-USER
    -A FORWARD -j DOCKER-ISOLATION-STAGE-1
    -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    -A FORWARD -o docker0 -j DOCKER
    -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
    -A FORWARD -i docker0 -o docker0 -j ACCEPT
    -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
    -A DOCKER-ISOLATION-STAGE-1 -j RETURN
    -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
    -A DOCKER-ISOLATION-STAGE-2 -j RETURN
    -A DOCKER-USER -j RETURN
    COMMIT
    # Completed on Mon Dec 23 10:49:09 2019
    

    首先最关键的就是 nat 上的一个规则

    -A POSTROUTING -s 172.17.0.0/24 ! -o docker0 -j MASQUERADE
    

    所有源地址是 172.17.0.0/24,并且目的地址不是虚拟网桥的都要做伪装 (nat), 其实就是 snat.
    再看 FORWARD,默认全部 drop,但是允许 docker0 网桥的转发。

    2. 端口转发

    此时启动容器,看下有什么变化

    root@worker1:~# docker run -it -p 8080:80 myubuntu /bin/bash
    root@adc5c18a4942:/# ip addr
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.2/24 brd 172.17.0.255 scope global eth0
           valid_lft forever preferred_lft forever
    

    启动了一个 myubuntu 容器,将 80 端口映射到宿主机的 8080 上,此时我们看下防火墙规则

    root@worker1:~# iptables-save
    # Generated by iptables-save v1.6.1 on Mon Dec 23 11:03:50 2019
    *nat
    ......
    -A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE
    -A DOCKER -i docker0 -j RETURN
    -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
    ......
    -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
    ......
    

    略去相同的部份,可以看到非 docker0 的流量防问宿主机的 8080 端口,会被做 dnat 路由到 172.17.0.2:80.

    传统 docker 网络缺点

    阿里云原生课 很棒,提到原生的 docker 网络弊端很多,都是一堆 nat 在跑,从容器出去的包要向宿主机借 ip 做 snat, 进容器的包要向容器借端口做 dnat,谁也不认识谁,而且有经验的都知道,nat 的性能相当差,做服务器的运维管理还行,跑业务还是算了

    早期的 docker 网络模型
    所以,k8s 抛弃了复杂的 docker 网络模型,只要满足 k8s 网络要求的三点即可,无论是通过 vxlan 还是 tun 实现的 overlay,还是借助宿主机网络的 underlay,通就可以。

    试验 host gw

    通过给主机加路由,就可以实现所谓的 host gateway,这也是 flannel host gw 实现的原理。由于阿里云 vpc 问题,网关都被接管了,只能用虚拟机或是实体物理机来实验。当然 host gw 是有限制:

    • 宿主机只能是同一个二层,链路层相通的,否则无法加路由
    • 每台宿主机上的 docker0 都是不同的网段,不能冲突,并且 docker0 是所有容器的默认网关
    host-gw

    上图是本次实验的网格拓扑,docker 启动时要添加 bip 参数,分别添加 bridge 的 172.17.4.1 和 172.17.1.1

    root@worker1:~# cat /lib/systemd/system/docker.service | grep -i dockerd
    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=172.17.4.1/24
    root@worker2:~# cat /lib/systemd/system/docker.service | grep -i dockerd
    ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=172.17.1.1/24
    

    需要关掉原有的 iptables nat

    iptables -t nat -F; iptables -F
    

    另外还要打开系统的 ip forward

    echo 1 > /proc/sys/net/ipv4/ip_forward
    

    此时注意 iptables forward 链默认是不是 ACCEPT,如果不是要么添加针对网桥的过滤规则

    iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    iptables -A FORWARD -s 172.0.0.0/8 -j ACCEPT
    iptables -A FORWARD -d 172.0.0.0/8 -j ACCEPT
    

    要么把默认变成 ACCEPT

    iptables -P FORWARD ACCEPT
    

    最后到了关键的一步,docker0 网桥的 ip 网段在宿主机内是可以防问的,但是跨主机是不通的,根本不知道哪台机器上的网桥属于哪些网段,所以需要我们手动加路由。在 192.168.1.168 机器上添加

    route add -net 172.17.1.0/24 gw 192.168.1.163 dev enp0s3
    

    在 192.168.1.163 上添加

    route add -net 172.17.4.0/24 gw 192.168.1.168 dev enp0s3
    

    此时可以测试,跨网络 docker ping 互通,并且主机与容器之间也互通。从这里也可以看到缺点,如果宿主机非常多,每台机器都要手动加所有的路由表。

    小结

    接下来再测试 flannel overylay

    相关文章

      网友评论

        本文标题:docker 学习笔记4:网络与 host gw

        本文链接:https://www.haomeiwen.com/subject/cbcanctx.html