docker网络

docker网络

docker有四种网络:bridge,hostnetwork,none,container

host:使用宿主机网络,表示创建容器的时候,不会创建network namespace,而是公用宿主机network namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。例如,我们在 10.10.101.105/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用 10.10.101.105:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

none:这个模式和前两个不同。在这种模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。

container:这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

bridge:bridge 模式是 Docker 默认的网络设置,此模式会为每一个容器分配 Network Namespace、设置 IP 等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配 IP 了,Docker 会从 RFC1918 所定义的私有 IP 网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的 IP 使用。如一般 Docker 会使用 172.17.0.0/16 这个网段,并将 172.17.42.1/16 分配给 docker0 网桥(在主机上使用 ifconfig 命令是可以看到 docker0 的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)

bridge模式网络

同一主机上的容器如何访问

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9b196474-7087-4710-96bf-e14dbc3a3961/Untitled.png

所有容器都连接到docker0网桥上,docker0是一个特殊的二层设备,因为它有ip。容器间访问都是通过该网桥实现二层直通,比如c1访问c2,这时报文会从容器内eth0设备流入网桥上对应的vethxxx口,这样网桥先从本地mac缓存里查看是否有目的容器ip的mac地址,如果没有,会发送arp广播,这时目的容器收到广播会将自己eth0 mac地址发送给docker0,docker0会将ip,mac信息存储在本地mac表内,方便下次查找。docker0知道了目的容器mac,这时候直接将报文发往vethxxx,因此报文流入目的容器eth0,那么回包也是同一原理。所以单机内容器间访问都是二层互通

容器如何访问宿主机外的终端

因为docker本身网络不提供跨机访问,所以这里不讨论容器跨机访问,可以使用第三方网络插件实现,所以这里讨论容器是如何访问公网的。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/95e98807-e1ec-4eef-a8ce-5a49a0d6d671/Untitled.png

安装好docker,docker run 创建一个容器,默认就有访问公网的能力,下面看下流量是如何走的

创建一个容器,并访问baidu,发现是通的,这是肯定的,不通说明有问题

[root@master-1 ~]# docker run -it a0a87e20199b bash
[root@dcb14656e986 /]# ping www.baidu.com
PING www.a.shifen.com (14.215.177.39) 56(84) bytes of data.
64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=1 ttl=50 time=27.2 ms
64 bytes from 14.215.177.39 (14.215.177.39): icmp_seq=2 ttl=50 time=27.0 ms
^C
--- www.a.shifen.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 27.029/27.157/27.285/0.128 ms

查看容器ip,发现与docker0网桥ip在一个网段,说明docker创建容器默认使用docker0分配ip,但是也可以网桥,使用自定义网桥分配ip,比如harbor

[root@dcb14656e986 ~]# 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: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
850: eth0@if851: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

查看容器路由,可以知道访问baidu走的是第一条默认路由,172.17.0.1是docker0 ip,所以docker0也充当了容器的网关,所以说它是一个特殊的网桥

[root@dcb14656e986 ~]# ip route
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3

报文到达docker0,这时候需要iptables发挥作用了,路由后,发现目的报文是baidu不是本地,所以不会进入input链,而是直接进入forward链,所以宿主机需要打开net.ipv4.ip_forward = 1内核参数,这个参数表示同一主机网卡间流量可以相互流入。所以流量进入宿主机网卡

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bd74473b-7ad5-4cb8-b0c8-0a3c0fc5432e/Untitled.png

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e3e5b552-0d28-4ab2-b978-ac03640a168e/u5mobdojtc.png

流量到达宿主机网卡,查看宿主机路由,发现只能匹配第一条路由,从而发往baidu

[root@master-1 ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.30.8.1      0.0.0.0         UG    0      0        0 eth0
10.233.106.0    0.0.0.0         255.255.255.0   U     0      0        0 *
10.233.106.1    0.0.0.0         255.255.255.255 UH    0      0        0 cali5f509af32dd
10.233.106.2    0.0.0.0         255.255.255.255 UH    0      0        0 cali19f95266695
10.233.106.10   0.0.0.0         255.255.255.255 UH    0      0        0 calief69dca4556
10.233.106.39   0.0.0.0         255.255.255.255 UH    0      0        0 cali03295d0ec6c
10.233.106.40   0.0.0.0         255.255.255.255 UH    0      0        0 cali0f27d5276dd
10.233.106.42   0.0.0.0         255.255.255.255 UH    0      0        0 calia3777377238
10.233.106.49   0.0.0.0         255.255.255.255 UH    0      0        0 cali11cbb9d849c
10.233.106.56   0.0.0.0         255.255.255.255 UH    0      0        0 calie119d55ddca
10.233.106.58   0.0.0.0         255.255.255.255 UH    0      0        0 cali90dca9bfeea
10.233.106.63   0.0.0.0         255.255.255.255 UH    0      0        0 cali4d729b741a9
10.233.106.64   0.0.0.0         255.255.255.255 UH    0      0        0 cali9fd39570217
10.233.106.67   0.0.0.0         255.255.255.255 UH    0      0        0 cali53ea6c7e449
10.233.106.101  0.0.0.0         255.255.255.255 UH    0      0        0 cali0fa54b064c7
10.233.106.119  0.0.0.0         255.255.255.255 UH    0      0        0 cali3557af02a9d
10.233.106.142  0.0.0.0         255.255.255.255 UH    0      0        0 cali4ac6b559799
10.233.106.143  0.0.0.0         255.255.255.255 UH    0      0        0 califfe9dd0e9aa
10.233.106.176  0.0.0.0         255.255.255.255 UH    0      0        0 cali2619adb09d2
10.233.109.0    172.30.10.163   255.255.255.0   UG    0      0        0 tunl0
10.233.112.0    172.30.9.252    255.255.255.0   UG    0      0        0 tunl0
10.233.113.0    172.30.8.234    255.255.255.0   UG    0      0        0 tunl0
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.30.8.0      0.0.0.0         255.255.252.0   U     0      0        0 eth0

这时候回包会出现问题,上图中internet经过EIP回到NAT网关时,NAT网关发现报文的目的ip是容器ip,NAT网关当然没有到容器ip的路由,所以回包在NAT网关直接丢包。所以需要在发送报文的宿主机配置snat策略,将出包的源ip改为宿主机ip,这样NAT网关就不需要配置回包路由,当然这个snat策略,docker在安装的时候就已经配置好了,snat策略在postrouting上配置,表示报文离开网卡时将源ip改掉

[root@master-1 ~]# iptables -L -t nat|grep 172
MASQUERADE  all  --  172.17.0.0/16        anywhere

删除这条规则,发现不通了

MASQUERADE  all  --  172.17.0.0/16        anywhere            
[root@master-1 ~]# iptables -t nat -L -n --line-numbers|grep MASQUERADE
4    MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
5    MASQUERADE  all  --  10.5.0.0/24          10.233.106.0/24     
1    MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            mark match 0x2000/0x2000
1    MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
2    MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose */ match-set KUBE-LOOP-BACK dst,dst,src
3    MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:SXWvdsbh4Mw7wOln */ ADDRTYPE match src-type !LOCAL limit-out ADDRTYPE match src-type LOCAL
1    MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst
[root@master-1 ~]# iptables -t nat -D POSTROUTING 4

# 超时
[root@dcb14656e986 ~]# ping www.baidu.com
PING www.a.shifen.com (14.215.177.38) 56(84) bytes of data.
^C
--- www.a.shifen.com ping statistics ---
33 packets transmitted, 0 received, 100% packet loss, time 32777ms

外部如何访问容器

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/95e98807-e1ec-4eef-a8ce-5a49a0d6d671/Untitled.png

外部客户端如果想要访问容器,如果两个宿主机在二层互通,那么只需要在外部宿主机配置到容器的路由。还是以上图为例如果两个宿主机不在一个二层网络,那么internet、EIP、NAT网关都需要配置到容器的路由,但是这是个弊端,需要一个守护进程,来学习容器的ip信息,因为容器ip不是固定的,这其实就是calico,flannel hostgw的原理。所以一般在容器的宿主机配置一条dnat规则即可

-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8443 -j DNAT --to-destination 172.17.0.2:8443

其作用是将访问宿主机8443端口请求的流量转发到容器172.17.0.2的8443端口上,所以,外界访问Docker容器是通过iptables做DNAT实现的。

posted @ 2021-08-04 15:55  LinuxSFeng  阅读(249)  评论(0编辑  收藏  举报