1.docker 的网络原理
无论是Windows 还是 Linux中,当docker 安装完成后,docker 都会在宿主机中创建一个虚拟网桥,通过该网桥实现容器之间的以及容器和外部网络的连接。通常情况下虚拟网桥的名称为docker0.例如在Linux 系统中网桥信息如下:
[root@localhost mnt]# 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:a2:be:5a brd ff:ff:ff:ff:ff:ff
inet 192.168.124.12/24 brd 192.168.124.255 scope global noprefixroute dynamic ens33
valid_lft 1186sec preferred_lft 1186sec
inet6 fe80::d635:15c0:d0e6:6ac1/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:e7:0d:b5:60 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:e7ff:fe0d:b560/64 scope link
valid_lft forever preferred_lft forever
上边输出中 编号为3的网络接口docker 0 是docker 所创建的虚拟网桥。
在OSI七层模型中,网桥工作在数据链路层。网桥是早期的两端口二层网络设备,用来连接不同的网段。网桥就像一个中继器,从一根网络电缆中接收信号,放大他们,再将其送入下一根电缆。网桥将网络中的多个网段在数据链路层连接起来。
在dicker中,各个容器是通过docker0 实现互联的,该虚拟网桥可以设置IP地址,相当于一个隐藏的虚拟网卡。
docker 守护进程在一个容器启动时,实际上他要创建网络连接的两端。一端是在容器中的网络设备,而另一端是在运行docker守护进程的主机上打开一个veth*的一个接口,用来实现docker0 这个网桥与容器的网络通信
我们使用 brctl 命令可以查看虚拟网桥docker 的信息
[root@localhost mnt]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242e70db560 no vethf099af6
这个名为 vethf099af6 的接口是属于已经创建的容器的虚拟网络接口
2.网络模式
目前docker 支持四种网络模式,网络模式可以在创建容器时使用 --network参数来指定。这四种模式分别为: host、container、none 以及 bridge。下面详细对这四种网络模式进行介绍
a. host模式
docker使用了Linux的命名空间进行来进行资源隔离,如PID命名空间隔离进程,mount命名空间隔离文件系统,network命名空间隔离网络等。一个network 命名空间提供了一份独立的网络环境,包括网卡、路由、iptables 规则等都与其他的network命名空间隔离。一个docker 容器一般会分配一个独立的network命名空间。但是如果启动容器的时候使用的是 host模式,那么这个容器将不会获得一个独立的network命名空间,而是和宿主机共用一个network 命名空间。容器将不会虚拟出自己的网卡,配置自己的IP地址等,而是使用宿主机的IP和端口。
例如 :
[root@localhost mnt]# docker run -itd --network=host couchbase/centos7-systemd
273086c656565df6d912ead305b9d798fb86956c8d43ea06ae399742b3b49dde
[root@localhost ~]# docker exec 273086c65656 ip a show
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:a2:be:5a brd ff:ff:ff:ff:ff:ff
inet 192.168.124.12/24 brd 192.168.124.255 scope global noprefixroute dynamic ens33
valid_lft 1537sec preferred_lft 1537sec
inet6 fe80::d635:15c0:d0e6:6ac1/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:e7:0d:b5:60 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:e7ff:fe0d:b560/64 scope link
valid_lft forever preferred_lft forever
从输出结果上看,实际上容器的这些网络信息正是主机的网络信息。
b. container模式(容器模式)
在理解了host模式后,这个模式也就好理解了,这个模式指定新创建的容器和已经存在的一个容器共享一个network命名空间,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP地址,而是和一个指定的容器共享IP地址、端口范围等。同样,两个容器除了网络方面,其他的如进程,文件系统等还是隔离的。两个容器的进程可以通过共享的网卡设备通信。
c.none模式(无模式)
这个模式和前两者不同。这这个模式下,docker容器拥有自己的network 命名空间,但是并不为docker容器进行任何的网络配置。也就是说,这个docker 容器没有网卡、IP地址、路由信息等,需要我们自己为docker容器添加网卡、配置IP等。
d. bridge模式(桥接模式)
bridge模式是docker 默认的网络模式,此模式会为每一个容器分配network 命名空间、设置IP地址等、并将一个主机上的docker容器连接到一个虚拟网桥上。
3.docker 容器的互连
由于同一个主机中所有的容器都连接在同一个虚拟网桥docker0 上,因此在默认情况下,同一主机中的容器之间是可以互相连接的,
为了便于测试,我这里创建两个centos 的容器,使用ip 命令分别查看网卡信息:
[root@localhost ~]# docker run -itd --name centos01 couchbase/centos7-systemd
43d62f40d9468f8e52cb54f32f90c400e4fdaacb6dcf9c3de8b7b8ad4b22422b
[root@localhost ~]# docker run -itd --name centos02 couchbase/centos7-systemd
70e550457f9691cad15cd2d413ab9ed4d2f2440417cd91230c9b18af9436866f
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70e550457f96 couchbase/centos7-systemd "/usr/sbin/init" 5 seconds ago Up 4 seconds centos02
43d62f40d946 couchbase/centos7-systemd "/usr/sbin/init" 10 seconds ago Up 10 seconds centos01
[root@localhost ~]# docker exec centos01 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
12: eth0@if13: <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/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
[root@localhost ~]# docker exec centos02 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
14: eth0@if15: <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
从上边输出可以看到 ,这俩容器的IP分别为172.17.0.2和 172.17.0.3 ,分别在这两个容器上用ping 探测连通性,看是否可以与对方通信
[root@localhost ~]# docker exec centos01 ping -c3 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.123 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.069 ms
--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.061/0.084/0.123/0.028 ms
[root@localhost ~]# ^C
[root@localhost ~]# docker exec centos02 ping -c3 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.092 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.088 ms
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.073/0.084/0.092/0.011 ms
4. 容器与外部网络的互连
容器与外部网络的互连涉及两个方面,一是容器内部访问外部网络,其次是外部网络访问容器内部,首先从容器内部访问外网测试,这里我们使用mtr 命令访问www.baidu.com .
[root@localhost ~]# docker exec -it centos01 /bin/bash
[root@43d62f40d946 /]# yum -y install mtr
```bash
[root@43d62f40d946 /]# mtr --address 172.17.0.2 -c20 -i0.2 -r -n www.baidu.com
Start: Sat Mar 11 16:10:15 2023
HOST: 43d62f40d946 Loss% Snt Last Avg Best Wrst StDev
1.|-- 172.17.0.1 45.0% 20 0.0 0.1 0.0 0.1 0.0
2.|-- 192.168.124.2 0.0% 20 0.4 0.3 0.2 0.9 0.0
3.|-- 10.253.16.1 0.0% 20 14.4 6.8 2.8 51.8 10.9
4.|-- ??? 100.0 20 0.0 0.0 0.0 0.0 0.0
5.|-- 10.253.219.169 0.0% 20 36.4 7.3 2.4 36.4 10.0
6.|-- 10.253.219.209 0.0% 20 5.8 5.5 3.3 15.0 2.6
7.|-- 10.2.0.185 5.0% 20 3.1 3.5 2.5 4.2 0.0
8.|-- 10.2.0.83 0.0% 20 2.6 3.1 2.3 4.2 0.2
9.|-- 111.202.148.1 0.0% 20 2.8 3.9 2.4 15.3 2.7
10.|-- 124.65.221.121 0.0% 20 4.5 4.0 3.2 5.1 0.2
11.|-- 61.149.203.21 0.0% 20 11.6 8.7 5.0 17.5 2.7
12.|-- 202.96.12.1 45.0% 20 7.8 6.5 5.2 9.4 1.1
13.|-- 219.158.11.94 55.0% 20 7.9 9.6 7.3 19.8 3.8
14.|-- 110.242.66.170 30.0% 20 11.9 12.2 10.4 22.1 2.9
15.|-- 221.194.45.134 0.0% 20 13.7 13.1 10.9 25.9 3.2
16.|-- ??? 100.0 20 0.0 0.0 0.0 0.0 0.0
17.|-- ??? 100.0 20 0.0 0.0 0.0 0.0 0.0
18.|-- ??? 100.0 20 0.0 0.0 0.0 0.0 0.0
19.|-- ??? 100.0 20 0.0 0.0 0.0 0.0 0.0
20.|-- 110.242.68.3 0.0% 19 11.0 11.7 10.7 15.2 0.8
从以上输出结果可以看到 我们从容器本省网卡出去第一跳连接到了虚拟网桥 docker0上,docker0 属于我虚拟机的虚拟网卡 192.168.124.2 是我虚拟机的交换机IP,因此默认情况下容器内部是可以访问外部网络的。
接下来是从外部如何访问容器的内部。通常情况下,外部网络是不可以直接访问容器内部的。如果要访问容器提供的网络服务。就需要通过端口映射实现,也就是说将主机的某个端口映射到容器的网络服务端口。通过端口映射,用户只要访问主机的指定端口就可以了。
例如 , 通过下面的命令创建一个 apache http server的容器,并且将主机的80端口映射到容器的80端口
[root@localhost ~]# docker run -itd -p 80:80 httpd
这时候我们通过浏览器 访问我们主机IP,可以看到访问到了容器默认的web主页