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

前言

因为docker技术的火爆,致使如今愈来愈多的企业都在使用docker这种虚拟化技术。企业中使用docker这种虚拟化技术,其目的就是为了让docker中的容器对外提供服务。所以,咱们必须深刻了解一下docker的网络知识,以知足更高的网络需求。html

咱们安装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

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

  • 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

虽然docker模式提供三种网络模式,但实际上是有四种网络模式的!安全

一、none模式

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

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

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

[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: <LOOPBACK,UP,LOWER_UP> 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

二、host模式

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

使用场景:测试

因为网络配置与docker宿主机彻底同样,性能较好,可是不便之处就是灵活性不高,容易和宿主机出现端口冲突的问题。最好是单个容器时使用,通常状况下不建议使用。spa

建立使用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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <NO-CARRIER,BROADCAST,MULTICAST,UP> 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: <BROADCAST,MULTICAST> 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: <NO-CARRIER,BROADCAST,MULTICAST,UP> 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<UP,BROADCAST,MULTICAST>  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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::30a8:fff:fe8c:41f4  prefixlen 64  scopeid 0x20<link>
        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地址为容器的默认网关;

四、自定义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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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

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

[root@docker ~]# ifconfig br-ac4d9e74e744
br-ac4d9e74e744: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  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<link>
        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

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

[root@docker ~]# ifconfig  br-70bd6d1c3371
br-70bd6d1c3371: flags=4099<UP,BROADCAST,MULTICAST>  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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> 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
//测试与test三、test4通讯,通讯正常

注:此时test2容器并不能够与test三、test4进行通讯!若是须要其通讯,还需给test2添加虚拟my_net2网卡地址(使用案例中的命令便可)!

**注意:**

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

五、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                       
/ # httpd -h /tmp/      //模拟http服务
[root@docker ~]# docker exec  -it web1  sh
/ #  wget -O - -q 127.0.0.1
Thank you
//发如今web1上也可以获取到暴露在外http服务

web1容器:
Docker网络管理之容器间的通讯
web2容器:
Docker网络管理之容器间的通讯
注:这时会发现两个容器,共用的是同一个IP地址服务

相关文章
相关标签/搜索