Docker网络管理之容器间的通信

网友投稿 264 2022-10-28

Docker网络管理之容器间的通信

前言

由于docker技术的火爆,导致现在越来越多的企业都在使用docker这种虚拟化技术。企业中使用docker这种虚拟化技术,其目的就是为了让docker中的容器对外提供服务。因此,我们必须深入了解一下docker的网络知识,以满足更高的网络需求。

我们安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host。 * host:容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 * None:该模式关闭了容器的网络功能,相当于一个回环网络。 * Bridge:此模式会为每一个容器分配、设置IP等,并将容器连接到一个叫docker0的虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。

[root@docker ~]# docker network ls //执行该命令查看docker创建的网络 NETWORK ID NAME DRIVER SCOPE 2edf7ef4f9fa bridge bridge local 217d2e9a4785 host host local c0bea73a8e1a none null local

关于上述提到的三个网络解释如下:

Host:相当于Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立的IP地址。众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。基于Host模式启动的容器,在容器内执行ifconfig时,看到的都是宿主机上的信息。该模式不够灵活,容易出现端口冲突问题。 None:该模式将容器放置在它自己的网络栈中,但是并不进行任何配置。实际上,该模式关闭了容器的网络功能,类似于会换地址,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务)。 overlay:顾名思义:覆盖,但它又不是覆盖,它的作用就是在容器原有的网络基础之上,再添加一块网卡,并为其分配一个IP地址,可以将所有的docker容器关联到同一个局域网中,适用于容器与容器是跨主机进行通信的场景。 Bridge:相当于Vmware中的NAT模式,容器使用独立的network Namespace,并且连接到docker0虚拟网卡(默认模式)。通过docker网桥以及IPtables nat表配置与宿主机通信;Bridge模式是Docker默认的网络设置,此模式会为每一个容器分配一个Network nameSpace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥docker0上。

下面详细介绍一下这几种网络模式:

虽然docker模式提供三种网络模式,但其实是有四种网络模式的!

1、none模式

在none模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器只有一个回环地址,不能与外界通信,称为被隔离的网络。使用场景:

none模式被称为隔离的网络,隔离便意味着安全,不能与外部通信,同样外部也不可以访问到使用none模式的容器,使用这种网络模式的容器可以运行于关于安全方面的验证码、校验码等服务。一般用于对安全性要求较高的场景中!

创建使用none网络模式的容器示例:

[root@docker ~]# docker run -itd --name none --network none busybox:latest //使用busybox镜像创建一个名为none的容器,网络采用none模式 [root@docker ~]# docker exec -it none sh //进入刚刚创建的容器 / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1 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、host模式

如果启动容器时使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

使用场景:

由于网络配置与docker宿主机完全一样,性能较好,但是不便之处就是灵活性不高,容易和宿主机出现端口冲突的问题。最好是单个容器时使用,一般情况下不建议使用。

创建使用host网络模式的容器示例:

[root@docker ~]# docker run -itd --name host --network host busybox:latest //使用busybox镜像创建一个名为host的容器,网络采用host模式 [root@docker ~]# docker exec -it host sh //进入创建的容器 / # ip a //进入容器后可以看出容器中的网络与docker主机的网络一模一样 1: lo: mtu 65536 qdisc noqueue qlen 1 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: mtu 1500 qdisc pfifo_fast qlen 1000 link/ether 00:0c:29:06:84:51 brd ff:ff:ff:ff:ff:ff inet 192.168.45.129/24 brd 192.168.45.255 scope global dynamic ens33 valid_lft 1479sec preferred_lft 1479sec inet6 fe80::7992:920f:d01f:6485/64 scope link valid_lft forever preferred_lft forever 3: virbr0: mtu 1500 qdisc noqueue qlen 1000 link/ether 52:54:00:e5:63:9d brd ff:ff:ff:ff:ff:ff inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever 4: virbr0-nic: mtu 1500 qdisc pfifo_fast master virbr0 qlen 1000 link/ether 52:54:00:e5:63:9d brd ff:ff:ff:ff:ff:ff 5: docker0: mtu 1500 qdisc noqueue link/ether 02:42:95:5a:2d:28 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

3.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.0.1/16分配给docker0网桥。

[root@docker ~]# ifconfig docker0 docker0: flags=4099 mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:95:5a:2d:28 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@docker ~]# brctl show //查看桥接网络 bridge name bridge id STP enabled interfaces docker0 8000.0242955a2d28 no //如果没有创建桥接模式的容器,默认是空的。 virbr0 8000.525400e5639d yes virbr0-nic

创建使用bridge网络模式的容器示例:

[root@docker ~]# docker run -itd --name bridge busybox:latest /bin/sh //创建一个名为bridge的容器,如果没有指定网络模式,默认便是bridge模式 [root@docker ~]# docker exec -it bridge /bin/sh //进入创建的容器中 / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1 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 6: eth0@if7: mtu 1500 qdisc noqueue link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever //可以看出eth0@if7这块虚拟网卡上的地址与docker宿主机的docker0网络属于同一网段 [root@docker ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.0242955a2d28 no veth33eb530 virbr0 8000.525400e5639d yes virbr0-nic /可以看到桥接模式的接口下出现了一个新的接口,当创建一个容器便会出现一个接口 //这个接口便是容器在docker宿主机创建一个虚拟网卡,用于容器与docker通信 [root@docker ~]# ifconfig veth33eb530 veth33eb530: flags=4163 mtu 1500 inet6 fe80::30a8:fff:fe8c:41f4 prefixlen 64 scopeid 0x20 ether 32:a8:0f:8c:41:f4 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 24 bytes 2618 (2.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

当使用bridge网络模式,docker的工作步骤大致为:(1)在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备;(2)Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth811d20c这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看(上述实例已经验证);(3)从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关;

4、自定义bridge网络

细心一点可以发现,创建的容器默认情况IP地址为172.17.0.0/16网段,那么我们可不可以自定义一个网段供容器使用呢?答案肯定是可以的,方法如下:

[root@docker ~]# docker network create -d bridge my_net //创建一个桥接网络,名称为my_net,如果没有指定网段,默认是172.18.0.0/16,按docker0的网段自动递增 [root@docker ~]# docker network ls //查看docker支持的网络类型 NETWORK ID NAME DRIVER SCOPE 2edf7ef4f9fa bridge bridge local 217d2e9a4785 host host local ac4d9e74e744 my_net bridge local c0bea73a8e1a none null local //可以看出,刚才创建的my_net已经出现在列表中

[root@docker ~]# docker inspect my_net 查看默认IP地址 [ { "Name": "my_net", "Id": "ac4d9e74e744a140ed8be2a7db0f0b0c5913d08258154fadb0f2ec64a61549db", "Created": "2020-08-01T10:03:46.543981931+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", //如果没有指定网段,默认是172.18.0.0/16 "Gateway": "172.18.0.1"

[root@docker ~]# docker run -itd --name test1 --network my_net busybox:latest /bin/sh [root@docker ~]# docker run -itd --name test2 --network my_net busybox:latest /bin/sh //使用刚才创建的网络模式创建两个容器 [root@docker ~]# docker exec -it test1 /bin/sh //进入test容器 / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1 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 9: eth0@if10: mtu 1500 qdisc noqueue link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever //查看其IP地址,发现确实是172.18.0.0/16网段的 / # ping test2 //测试通过容器名称ping test2容器 PING test2 (172.18.0.3): 56 data bytes 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.057 ms 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.148 ms 64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.143 ms [root@docker ~]# ip a

[root@docker ~]# ifconfig br-ac4d9e74e744 br-ac4d9e74e744: flags=4163 mtu 1500 inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255 //通过IP地址网段就可看出 inet6 fe80::42:3dff:fe79:ee81 prefixlen 64 scopeid 0x20 ether 02:42:3d:79:ee:81 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

**自定义网络的优点:** * 可以通过容器的名称进行通信; * 自定了ContainerDNSserver功能;

以上方法按照默认的方式创建一个桥接模式的网络,可以发现网段地址并不是我们自定义的。

接下来我们通过指定具体网段的方式创建网卡。方法如下:

[root@docker ~]# docker network create -d bridge --subnet 172.20.16.0/24 --gateway 172.20.16.1 my_net2 //自定义网络模式的地址时,必须要明确指定其IP网段及网关信息 [root@docker ~]# ip a

[root@docker ~]# ifconfig br-70bd6d1c3371 br-70bd6d1c3371: flags=4099 mtu 1500 inet 172.20.16.1 netmask 255.255.255.0 broadcast 172.20.16.255 ether 02:42:d8:9e:20:0b txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 //可以看出其IP地址就是我们指定的IP地址 [root@docker ~]# docker run -itd --name test3 --network my_net2 --ip 172.20.16.3 busybox:latest [root@docker ~]# docker run -itd --name test4 --network my_net2 --ip 172.20.16.4 busybox:latest //基于刚才创建的网络创建出两个容器并指定其固定的IP地址 [root@docker ~]# docker exec -it test3 sh //进入test3容器 / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1 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 16: eth0@if17: mtu 1500 qdisc noqueue link/ether 02:42:ac:14:10:03 brd ff:ff:ff:ff:ff:ff inet 172.20.16.3/24 brd 172.20.16.255 scope global eth0 valid_lft forever preferred_lft forever //发现其IP地址确实我们刚才指定的IP地址 / # ping test4 //测试发现确实是可以和test4通信的 PING test4 (172.20.16.4): 56 data bytes 64 bytes from 172.20.16.4: seq=0 ttl=64 time=0.089 ms 64 bytes from 172.20.16.4: seq=1 ttl=64 time=0.146 ms 64 bytes from 172.20.16.4: seq=2 ttl=64 time=0.105 ms / # ping test1 //测试发现无法与第一次创建的my_net网络进行通信 ping: bad address 'test1'

使用相同的网络创建的容器是可以相互通信的,但是发现无法与其他容器进行通信,这主要是因为iptables规则的原因,创建docker网络时,iptables规则就会随着自动添加的。

举例说:尝试把iptables规则清空(iptables -F),是可以实现我们想要的效果的。但是这种命令的作用不亚于“rm -rf /*”,显然在现实环境中是不可以使用的!(iptables -F)

那么就需要使用下面的方法来实现了,方法如下:

[root@docker ~]# docker network connect my_net2 test1 [root@docker ~]# docker exec -it test1 sh //进入test1容器 / # ip a 1: lo: mtu 65536 qdisc noqueue qlen 1 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 9: eth0@if10: mtu 1500 qdisc noqueue link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0 valid_lft forever preferred_lft forever 20: eth1@if21: mtu 1500 qdisc noqueue link/ether 02:42:ac:14:10:02 brd ff:ff:ff:ff:ff:ff inet 172.20.16.2/24 brd 172.20.16.255 scope global eth1 valid_lft forever preferred_lft forever //可以查询到确实多了一块虚拟网卡,网段确实和my_net2属于同一网段 / # ping test3 PING test3 (172.20.16.3): 56 data bytes 64 bytes from 172.20.16.3: seq=0 ttl=64 time=0.140 ms 64 bytes from 172.20.16.3: seq=1 ttl=64 time=0.057 m / # ping test4 PING test4 (172.20.16.4): 56 data bytes 64 bytes from 172.20.16.4: seq=0 ttl=64 time=0.192 ms 64 bytes from 172.20.16.4: seq=1 ttl=64 time=0.143 ms //测试与test3、test4通信,通信正常

注:此时test2容器并不可以与test3、test4进行通信!如果需要其通信,还需给test2添加虚拟my_net2网卡地址(使用案例中的命令即可)!

**注意:** * 容器之间可以使用容器名进行通信,但前提必须是使用自定义网络,比如案例中创建的my_net、my_net2; * 如果在创建自定义网络时,指定了该网络的网段,那么使用此时的容器也可以指定容器的IP地址,若没有指定该网络的网段,则不可指定容器的IP地址;

5、container(共享网络协议栈)

这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。使用场景:由于这种网络的特殊性,一般在运行同一个服务,并且合格服务需要做监控,以及日志收集、或者网络监控的时候,可以选择这种网络。创建使用container网络模式的容器示例:

[root@docker ~]# docker run -itd --name web1 busybox:latest [root@docker ~]# docker run -itd --name web2 --network container:web1 busybox:latest [root@docker ~]# docker exec -it web2 sh / # echo "Thank you" > /tmp/index.html / # -h /tmp/ //模拟http服务 [root@docker ~]# docker exec -it web1 sh / # wget -O - -q 127.0.0.1 Thank you //发现在web1上也能够获取到暴露在外http服务

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:kubernetes(十三) k8s 业务上线流程(手动版)
下一篇:JDK源码白话解读之ThreadLocal篇
相关文章

 发表评论

暂时没有评论,来抢沙发吧~