Docker容器技术之容器虚拟化网络(4)
1. 虚拟化网络
Network Namespace 是 Linux 内核提供的功能,是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己都在独立的网络中。而且不同Network Namespace的资源相互不可见,彼此之间无法通信。
假如我们的物理机有4块物理网卡,我们要创建4个名称空间,而这些设备是可以单独关联至某个单独的名称空间使用的
如上图所示,把第一块网卡分配给第一个名称空间,第二块分给第二个名称空间,第三块分给第三个名称空间,第四块分给第四个名称空间。此时其它名称空间都是看不见当前所在名称空间的,因为一个设备只能属于一个名称空间。
这种方式使得每一个名称空间都能配置IP地址,并且与外部网络直接通信,因为它们使用的是物理网卡。
但如果我们所拥有的名称空间数量超过物理网卡数量呢?此时我们可以使用虚拟网卡设备,用纯软件的方式来模拟一组设备来使用。Linux内核级支持2种级别设备的模拟,一种是二层设备,一种是三层设备。
Linux内核模拟的二层设备,每个网络接口设备是成对出现的,可以模拟为一根网线的两端,其中一端模拟主机的虚拟网卡,另一端模拟虚拟交换机,就相当于让一个主机连到一个交换机上去。Linux内核原生支持二层虚拟网桥设备,即用软件虚拟交换机的功能。如下图所示:
那么此时如果再有一个名称空间,它有创建了一对虚拟网卡,一端连接名称空间,一端连接虚拟交换机,此时就相当于两个名称空间连接到了同一个交换机网络中,此时如果两个名称空间的网卡地址配置在同一网段,那么很显然他们之间是可以互相通信的。如下图所示:
从网络通信的物理设备到网卡都是用纯软件的方式来实现,这种实现方式就叫做虚拟化网络。
2. 单节点容器间通信
如果在同一个物理机上的两个容器想通信,我们的办法就是在这台主机上建立一个虚拟交换机,而后让两个容器各自用纯软件的方式创建一对虚拟网卡,一半在容器上,一半在虚拟交换机上,从而实现通信。如下图所示:
这就是单节点上两个容器间的通信方式。单节点上两个容器之间的通信也有一些复杂情况,比如我们期望构建的容器要跨交换机通信呢?
我们做两个虚拟交换机,两个交换机上各自连接不同的容器,如上图所示,此时如果要C1和C3通信又该如何实现呢?其实我们可以通过名称空间创建一对网卡,一端连SW1,另一端连SW2,这样一来两个交换机就连起来了,照理说这样一来C1和C3这两个处于不同交换机的容器就可以实现通信了,但是这样一来又存在另一个问题,那就是如果C1和C3在不同网络呢?如果不在同一网络我们就必须要通过路由转发才能使其通信,也就是我们得在两台交换机之间加一个路由器,其实Linux内核本身就是支持路由转发的,只需要我们将路由转发功能打开即可。此时我们可以再启动一个容器,这个容器里面就跑一个内核,并将其转发功能打开,这样一来就模拟了一台路由器,通过这台路由器来实现路由转发。
3. 不同节点容器间通信
如上图所示,此时如果C1要与C5进行通信又该如何实现呢?如果我们采用桥接的方式,很容易产生广播风暴,因此,在大规模的虚拟机或容器的场景中,使用桥接的方式无疑是自取灭亡,所以我们不应该使用桥接的方式来实现通信。
如果一来,我们既不能桥接,又需要与外部来实现通信,那就只能使用NAT技术了。通过DNAT将容器的端口暴露到宿主机上,通过访问宿主机的端口来实现访问容器内部的目的,而在请求端我们需要做SNAT将数据包通过宿主机的真实网卡转发出去。但这样做的话,因为要进行两次NAT转换,所以效率会比较低。
此时我们可以采用一种叫做Overlay Network(叠加网络)的技术来实现不同节点间容器的相互通信功能。
Overlay Network会将报文进行隧道转发,也就是在报文发出去之前要为其添加一个IP首部,也就是上图的1.1和1.2这部分,这里的1.1是源,1.2是目标,当宿主机2收到报文后解封装发现要找的目标容器是C2,于是把包转发给C2。
4. docker容器网络
Docker在安装后自动提供3种网络,可以使用docker network ls
命令查看
[root@node02 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
1b093120d2bb bridge bridge local
7362e537cc7b host host local
910a5741bb77 none null local
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
5. docker的4种网络模式
网络模式 | 配置 | 说明 |
---|---|---|
host | --network host | 容器和宿主机共享Network namespace |
container | --network container:NAME_OR_ID | 容器和另外一个容器共享Network namespace |
none | --network none | 容器有独立的Network namespace, 但并没有对其进行任何网络设置, 如分配veth pair 和网桥连接,配置IP等 |
bridge | --network bridge | 默认模式 |
5.1 bridge模式
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥
,此主机上启动的Docker容器会连接到这个虚拟网桥上
。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用
,并设置docker0的IP地址为容器的默认网关
。在主机上创建一对虚拟网卡
veth pair设备,Docker将veth pair设备的一端放在新创建的容器
中,并命名为eth0(容器的网卡),另一端放在主机中
,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
[root@node02 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024206c52942 no veth1c4e358
veth3b5c82d
veth664302c
veth710559c
bridge模式是docker的默认网络模式
,不写--network参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
[root@node02 ~]# iptables -t nat -vnL
Chain PREROUTING (policy ACCEPT 9 packets, 676 bytes)
pkts bytes target prot opt in out source destination
4 204 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 9 packets, 676 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 18 packets, 1804 bytes)
pkts bytes target prot opt in out source destination
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 18 packets, 1804 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.17.0.5 172.17.0.5 tcp dpt:80
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.5:80
bridge模式如下图所示:
演示
[root@node02 ~]# docker run -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
bf5952930446: Pull complete
cb9a6de05e5a: Pull complete
9513ea0afb93: Pull complete
b49ea07d2e93: Pull complete
a5e4a503d449: Pull complete
Digest: sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661
Status: Downloaded newer image for nginx:latest
13452140347d7365d07d87e563c5ef9dc13e4a1c679a95006bb357bed6692e94
[root@node02 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13452140347d nginx "/docker-entrypoint.…" 11 seconds ago Up 10 seconds 80/tcp lucid_shamir
68bf73ee67f9 dragonyear22/nginx:v0.2 "/bin/httpd -f -h /l…" 13 hours ago Up 13 hours naughty_bohr
48379c69153b busybox "sh" 14 hours ago Up 14 hours eager_goldwasser
[root@node02 ~]# docker inspect 13452140347d
...........
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
...........
[root@node02 ~]# ip a
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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:dd:54:b6 brd ff:ff:ff:ff:ff:ff
inet 192.168.159.161/24 brd 192.168.159.255 scope global noprefixroute dynamic ens33
valid_lft 1791sec preferred_lft 1791sec
inet6 fe80::2d92:6f17:5128:80f9/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:06:c5:29:42 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:6ff:fec5:2942/64 scope link
valid_lft forever preferred_lft forever
5: veth1c4e358@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 56:17:7a:17:60:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::5417:7aff:fe17:60b4/64 scope link
valid_lft forever preferred_lft forever
11: veth710559c@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether a2:f8:89:83:4b:55 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::a0f8:89ff:fe83:4b55/64 scope link
valid_lft forever preferred_lft forever
13: veth664302c@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 16:b2:4d:f7:f6:28 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::14b2:4dff:fef7:f628/64 scope link
valid_lft forever preferred_lft forever
[root@node02 ~]# ping 172.17.0.4
PING 172.17.0.4 (172.17.0.4) 56(84) bytes of data.
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.111 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 172.17.0.4: icmp_seq=3 ttl=64 time=0.037 ms
64 bytes from 172.17.0.4: icmp_seq=4 ttl=64 time=0.039 ms
^C
--- 172.17.0.4 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.037/0.067/0.111/0.032 ms
Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。
使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
[root@node02 ~]# docker run -d -p 80 --name t2 nginx
064251a9b7a3b84e532d47dd993405efed8bc2ea28270937a9b311f1a70ab521
[root@node02 ~]# iptables -t nat -vnL |tail -1
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.5:80
[root@node02 ~]# ss -antl |grep 32768
LISTEN 0 128 [::]:32768 [::]:*
5.2 container模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
container模式如下图所示:
演示
在一个终端,使用bridge网络模式启动容器b1
[root@node02 ~]# docker run --name b1 -it --rm busybox:latest
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:06
inet addr:172.17.0.6 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:516 (516.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # echo "hello world b1" > /tmp/index.html
/ # httpd -h /tmp/ 在b1上启动httpd服务
/ #
/ # netstat -nutl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 :::80 :::* LISTEN
在另一个终端使用Container 网络模式创建容器b2
[root@node02 ~]# docker run --name b2 -it --network container:b1 --rm busybox:latest
/ # ifconfig -a
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:06
inet addr:172.17.0.6 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:656 (656.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # wget -O - -q 127.0.0.1 b1启动的httpd服务,在b2上直接访问
hello world b1
/ # ls /tmp/ 但是文件系统并不共享,只共享网络
5.3 host模式
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
Host模式如下图所示:
演示
[root@node02 ~]# docker run --name b2 -it --network host --rm busybox:latest
/ # ifconfig -a
docker0 Link encap:Ethernet HWaddr 02:42:06:C5:29:42
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:6ff:fec5:2942/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14 errors:0 dropped:0 overruns:0 frame:0
TX packets:19 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:974 (974.0 B) TX bytes:1484 (1.4 KiB)
ens33 Link encap:Ethernet HWaddr 00:0C:29:DD:54:B6
inet addr:192.168.159.161 Bcast:192.168.159.255 Mask:255.255.255.0
inet6 addr: fe80::2d92:6f17:5128:80f9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:117652 errors:0 dropped:0 overruns:0 frame:0
TX packets:14693 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:170532211 (162.6 MiB) TX bytes:1278297 (1.2 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:64 errors:0 dropped:0 overruns:0 frame:0
TX packets:64 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5568 (5.4 KiB) TX bytes:5568 (5.4 KiB)
veth1c4e358 Link encap:Ethernet HWaddr 56:17:7A:17:60:B4
inet6 addr: fe80::5417:7aff:fe17:60b4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:1186 (1.1 KiB)
veth3b5c82d Link encap:Ethernet HWaddr 1A:B1:44:EB:27:D9
inet6 addr: fe80::18b1:44ff:feeb:27d9/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:656 (656.0 B)
veth664302c Link encap:Ethernet HWaddr 16:B2:4D:F7:F6:28
inet6 addr: fe80::14b2:4dff:fef7:f628/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:14 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:476 (476.0 B) TX bytes:1132 (1.1 KiB)
veth710559c Link encap:Ethernet HWaddr A2:F8:89:83:4B:55
inet6 addr: fe80::a0f8:89ff:fe83:4b55/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:17 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:694 (694.0 B) TX bytes:1260 (1.2 KiB)
通过查看IP,可以发现和宿主机是一样的
5.4 none模式
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过--network none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
应用场景:
- 启动一个容器处理数据,比如转换数据格式
- 一些后台的计算和处理任务
none模式如下图所示:
演示
[root@node02 ~]# docker run --name b1 -it --network none --rm busybox:latest
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
docker network inspect bridge #查看bridge网络的详细配置
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ⁾⁾!