shell脚本

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

2019-12-23  本文已影响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 等等

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

当前最流行的有 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 是有限制:

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

上一篇 下一篇

猜你喜欢

热点阅读