当项目大规模使用 Docker 时,容器通信的问题也就产生了。要解决容器通信问题,必须先了解很多关于网络的知识。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker 同样有着很多不完善的地方,网络方面就是 Docker 比较薄弱的部分。因此,我们有必要深入了解 Docker 的网络知识,以满足更高的网络需求。
Docker网络也是docker学习中重要的一部分,前门我们说介绍的docker操作均是在单宿主机上安装和使用docker,然后再生产环境中单节点的使用是没有实际意义的,因此这一章节我们将详细介绍docker的网络使用,已经docker跨宿主机网络使用。
安装 Docker 以后,会默认创建三种网络,可以通过 docker network ls
查看。
[root@clinet ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
44ccbd74a658 bridge bridge local
016f4cda7f9a host host local
6167ccead0c4 none null local
网络模式介绍:
桥接网络是Docker默认的网络模式,也是最常用的一种。在桥接网络中,Docker主机上的所有容器都连接到同一个虚拟网络中。Docker会为每个容器分配一个唯一的IP地址,容器之间可以相互通信。
主机网络模式让容器直接使用主机的网络栈,容器中的应用程序可以直接访问主机上的网络接口。这种模式的好处是容器的网络性能非常高,但是容器之间无法直接通信,因此这种模式一般只用于需要访问主机网络的应用程序。
none网络模式让容器没有网络接口,也没有IP地址。这种模式通常用于测试和调试,或者需要在容器中运行一些不需要网络连接的应用程序。
container模式,也称为容器网络模式。在这种模式下,容器可以共享另一个容器的网络栈,即两个容器可以使用同一个虚拟网络接口和IP地址。
docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。可自定义的网络类型:bridge,overlay,macvlan。
在该模式中,Docker 守护进程创建了一个虚拟以太网桥 docker0
,新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。
默认情况下,守护进程会创建一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。
容器安装后默认会创建一个docker0的网桥,网桥的默认ip段为172.17.0.1/16,我们可以通过docker的配置文件(/etc/docker/daemon.json)自定义网桥ip段。
[root@clinet ~]# cat /etc/docker/daemon.json
{
"bip": "172.18.0.1/16",
"registry-mirrors": ["https://mqkiky4e.mirror.aliyuncs.com"]
}
[root@clinet ~]#
我们通过创建一个busybox的容器,使用bridge网络,来深入了解brige网络的连接。
[root@clinet ~]# docker run -it --network bridge busybox /bin/sh
(--network bridge是默认的,可以不指定。)
创建完容器之后,我们在宿主机上通过ip add查看的信息如下:
此时宿主机上创建了一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名。如上图所示,9表示为连接到容器内的网络连接(eth0@if9),
同时通过在宿主机上可以查看到相关网桥的信息:
综上所述,bridge网络的拓扑如下:
Bridge 桥接模式的实现步骤主要如下:
1. 启动一个容器的时候,Docker Daemon会在宿主机上创建一对虚拟网络接口,该接口相当于网线的两端,一端在宿主机上(veth pair格式),另外一端在容器内为eth0;
2. 同时Docker Daemon 会将宿主机上的 veth pair 接口桥接到 Docker Daemon 创建的 docker0 网桥上。
3. docker0网桥同时也类似为dhcp,会给容器的eth0网卡分配相同段的ip。
[root@clinet ~]# docker inspect 53a |grep -A 30 Networks
[root@clinet ~]# docker network inspect bridge
# 通过如下命令会列出bridge网络的相关信息,其中"Containers"字段的表示是信息是指当前节点上有哪些容器使用了该网络。
docker run -d --name web1 --net bridge nginx
默认情况下,基于bridge网络容器即可访问外部网络,这是因为默认情况下,docker使用了iptables的snat转发来实现容器对外部的访问(需要内核开启net.ipv4.ip_forward=1)
如果想让外界可以访问到基于bridge网络创建的容器提供的服务,则必须要告诉docker要使用的端口。
可以通过如下方法查看镜像会使用哪些端口:
docker inspect nginx | jq .[]."ContainerConfig"."ExposedPorts"
在创建容器的时候可以指定这个容器的端口与主机端口的映射关系:
docker run -d --name web -P nginx
-p: 可以指定主机与容器的端口关系,冒号左边是主机的端口,右边是映射到容器中的端口
-P:该参数会分配镜像中所有的会使用的端口,并映射到主机上的随机端口
这种端口映射基于iptables的dnat实现
查看容器的端口情况:
docker port 容器名称
或
docker ps -a (找到对应容器)
--net host
或者 --network host
指定;
- 可以通过如下方式查看host网络信息:
docker inspect host
- 创建一个使用host网络容器的示例:
# 可以看到该容器没有自己的IP地址,因为它直接使用宿主机IP地址 docker run -it --network host busybox
host网络拓扑
--net none
或者 --network none
指定;
- 查看none网络信息:
docker network inspect none
- 创建一个使用none网络容器的示例:
docker run -it --network none busybox
--net container:已运行的容器名称|ID
或者 --network container:已运行的容器名称|ID
指定;
- 创建一个基础容器nginx,该容器使用bridge模式
[root@clinet ~]# docker run -d --name nginx --network bridge nginx
5acd9628840da292a618b44499fdf4c6ac56f11b4c64390411ec41f7b59a8b9f
[root@clinet ~]#
此时基础容器的网络情况如下:
- 创建一个busybox容器,使用container网络模式
docker run -it --network container:nginx busybox
此时busybox容器的网络如下:
此时宿主机上的网络情况如下:
总上所述,Docker 守护进程只创建了一对对等虚拟设备接口,busybox容器完全的使用了nginx容器的网络。
- 当我们停止掉nginx容器的时候,网络信息如下:
此时busybox容器只剩下了lo网络接口。
Docker除了提供三种的默认网络模式之外,也允许用户针对一些特定的应用场景去创建一些自定义的网络。这样属于这个网络的容器就可以单独隔离出来,它们之间可以相互通信,而不在这个网络的容器就不能直接访问到它们。一个容器可以属于多个网络,同一个自定义网络下的容器可以通过各自的容器名访问到对方,因为会使用到docker内嵌的一个dns功能。
Docker提供三种自定义网络驱动:
1. bridge
2. overlay
3. macvlan
- 创建一个叫作
my_net
的自定义网络docker network create --driver bridge my_net #--driver用于指定网络类型,不指定默认为bridge
- 通过指定子网和网关的方式创建my_net2网络:
docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
- 查看网络信息
- 宿主机上会产生两个类似docker0的网桥。
- 删除自定义网络
docker network rm my_net1 (自定义网络名称)
注意:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。
示例:
docker run -it --network my_net1 busybox
docker run -it --network my_net2 busybox
- 创建一个基于默认的
bridge
网络模式的容器.
docker run -d --name nginx_test nginx
- 创建另一个基于默认的
bridge
网络模式的容器,并测试连通性
经过测试,从结果得知两个属于同一个网络的容器是可以进行网络通信的。
- 创建一个基于自定义的my_net1网络模式的容器.
docker run -d --name nginx --network my_net1 nginx
- 创建另一个基于my_net1网络模式的容器,并测试连通
docker run -it --name busybox --network my_net1 busybox ping 172.17.0.2
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name 为容器命名即可。
但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
- 创建一个基于默认的
bridge
网络模式的容器.
docker run -d --name nginx_1 nginx
- 创建一个基于自定义的my_net2网络模式的容器. 并测试互通。
docker run -it --network my_net2 busybox ping 172.18.0.3
此时网络不通。
- 通过
docker network connect 网络名称 容器名称
为容器连接新的网络模式。
docker network connect my_net2 nginx_1
- 测试网络连通性
- 此时两个网络结果如下:
此时相当于nginx_1容器存在两个网卡,一个是桥接到docker0的,一个是桥接到my_net2的,因此实现原理还是两容器处于相同的ip段。
- 断开网络
docker network disconnect my_net2 nginx_1
1. 上述所说的容器互通均是同一宿主机上的场景;
2. 互通的根本原理是让两个容器处于同一个桥接网卡上,即:相同的ip段。
后面我们将单独说说跨主机容器互通的问题。
- 创建一个新的Docker网络
docker network create my_network
语法如下:
docker network create [OPTIONS] NETWORK 其中,OPTIONS是一些可选参数,NETWORK是要创建的网络的名称。 下面是一些常用的OPTIONS参数: --driver:指定网络驱动程序,默认为bridge。 --subnet:指定网络的IPv4子网。 --gateway:指定网络的IPv4网关。 --ipv6:启用IPv6支持。 --ip-range:指定网络的IPv4地址范围。 --ipam-driver:指定IP地址管理驱动程序。 --ipam-opt:指定IP地址管理驱动程序的选项。
1.1 创建一个名为my_network的桥接网络,其中IPv4子网为172.18.0.0/16,网关为172.18.0.1
docker network create --driver bridge --subnet=172.18.0.0/16 --gateway=172.18.0.1 my_network
1.2 将创建一个名为my_network的桥接网络
docker network create --driver bridge my_network
- 将名为my_container的容器连接到名为my_network的Docker网络
docker network connect my_network my_container
- 将名为my_container的容器从名为my_network的Docker网络中断开连接,
docker network disconnect my_network my_container 或 ## --force 强制 docker network disconnect --force my_network my_container
- 查看网络的详细信息
docker inspect 网络名称
- 列出所有的Docker网络
docker network ls
- 删除一个Docker网络
docker network rm 网络名
- 删除所有未使用的Docker网络
docker network prune
为my_container的容器从名为my_network的Docker网络中断开连接,
docker network disconnect my_network my_container 或 ## --force 强制 docker network disconnect --force my_network my_container
- 查看网络的详细信息
docker inspect 网络名称
- 列出所有的Docker网络
docker network ls
- 删除一个Docker网络
docker network rm 网络名
- 删除所有未使用的Docker网络
docker network prune