一、Docker网络-简介¶
Docker网络架构源自一种叫做容器网络模型(CNM)的方案,该方案是开源的并且支持插接式连接。Libnetwork是Docker对CNM的一种实现,提供了Docker核心网络架构的全部功能。不同的驱动可以通过插拔的方式接入Libnetwork来提供定制化的网络拓扑。
为了实现开箱即用的效果,Docker封装了一系列本地驱动,覆盖了大部分常见的网络需求。其中包括单机桥接网络(Single-Host Bridge Network)、多机覆盖网络(Multi-Host Overlay),并且支持接入现有VLAN。
二、Docker网络-详解¶
2.1 基础理论¶
在顶层设计中,Docker网络架构由3个主要部分构成:CNM、Libnetwork和驱动。
CNM是设计标准。在CNM中,规定了Docker网络架构的基础组成要素。 Libnetwork是CNM的实现,并且被Docker采用。Libnetwork通过Go语言编写,并实现了CNM中列举的核心组件。 驱动通过实现特定网络拓扑的方式来拓展该模型的能力。
2.1.1 CNM¶
在CNM中,规定了Docker网络架构的基础组成要素。详细请查看 CNM完整介绍
CNM定义了三个基本要素:沙盒、终端和网络。 沙盒是一个独立的网络栈。其中包括以太网接口、端口、路由表以及DNS配置。 终端就是虚拟网络接口。跟普通网络接口一样,终端主要职责是负责创建连接。在CNM中,终端负责将沙盒连接到网络。一个终端只能接入一个网络中。 网络是802.1d网桥的软件实现。因此,网络就是需要交互的终端的集合,并且终端之间相互独立。
Docker环境中最小的调度单位就是容器,而CNM负责为容器提供网络功能。
2.1.2 Libnetwork¶
CNM是设计规范文档,Libnetwork是标准的实现。Libnetwork是开源的,采用Go语言编写,它跨平台且被Docker所使用。 Libnetwork实现了CNM中定义的全部3个组件。此外它还实现了本地服务发现、基于Ingress的容器负载均衡,以及网络控制层和管理层功能。
2.1.3 驱动¶
Libnetwork实现了控制层和管理层功能,而驱动负责实现数据层。Libnetwork支持同时激活多个网络驱动。
Docker封装了若干内置驱动,通常被称作原生驱动或者本地驱动。在Linux上包括Bridge、Overlay以及Macvlan,在Windows包括NAT、Overlay、Transport以及L2 Bridge。
其中控制层、管理层与数据层关系如下:

第三方也可以编写Docker驱动,这些驱动叫做远程驱动,如Calico、Contiv、Kuryr以及Weave。每个驱动都负责其上所有网络资源的创建和管理。
2.2 单机桥接网络¶
单机桥接网络是最简单的Docker网络。
Linux docker创建单机桥接网络采用内置的桥接驱动,而Windows docker创建时使用内置的NAT驱动。
每个Docker主机都有一个默认的单机桥接网络。在Linux上网络名称为bridge,在Windows上叫做nat。这里需要注意,默认情况下,新创建的容器都会连接到该网络。除非通过命令行创建容器时指定参数--network。
在Linux主机上,Docker网络由Bridge驱动创建,而Bridge底层是基于Linux内核中的Linux Bridge技术。在Linux Docker主机之上,默认的"bridge"网络被映射到内核中为"docker0"的Linux网桥,该网桥可以通过主机以太网接口的端口映射进行反向关联。
通过使用Linux brctl工具来查看系统的Linux网桥。若没有,则可使用apt-get install bridge-utils命令安装broctl安装包。
如果在相同网络中继续接入新的容器,那么在新接入容器中是可以通过之前加入的容器进行Ping通的。这里需要注意,Linux上默认的Bridge网络是不支持通过Docker DNS服务进行域名解析的。自定义网络可以。
下面进行测试: 1、创建名为"c2"的容器,并接入"c1"所在的localnet网络。
root@zq-virtual-machine:~# docker container run it --name c2 --network localnet alpine sh
2、在"c2"容器中,通过"c1"容器名称执行ping命令。观察到,命令生效。这是因为c2容器运行了一个本地DNS解析器,该解析器将请求转发到了Docker内容DNS服务器当中。DNS服务器中记录了容器启动时通过--name或--net-alias参数指定的名称与容器之间的映射关系。
/ # ping c1
PING c1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=1.701 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.119 ms
^C
--- c1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.119/0.910/1.701 ms
端口映射允许将某个容器端口映射到Docker主机端口上。对于配置中指定的Docker主机端口,任何发送到该端口的流量,都会转发到容器。下面通过示例了解容器上运行着WEB服务的端口80,映射到Docker主机端口的5000的进程。 1、运行一个新的Web服务容器,并将容器80端口映射到Docker主机的5000端口。
root@zq-virtual-machine:~# docker container run -d --name web --network localnet --publish 5000:80 nginx
2、确认端口映射
root@zq-virtual-machine:~# docker port web
80/tcp -> 0.0.0.0:5000
80/tcp -> :::5000
3、通过WEB服务器访问Docker主机的5000端口,验证配置是否生效。

2.3 多机覆盖网络¶
覆盖网络适用于多机环境。它允许单个网络包含多个主机,这样不同主机上的容器间就可以在链路层实现通信。覆盖网络是理想的容器间通信方式,支持完全容器化的应用,并且具备良好的伸缩性。
Docker为覆盖网络提供了本地驱动。在docker network create命令中添加-d overlay参数。
2.4 接入现有网络¶
Docker内置的Macvlan驱动实现将容器化应用连接到外部系统以及物理网络。
Macvlan的优缺点: 优点 性能优异,无须端口映射或者额外桥接,可以直接通过主机接口(或者子接口)访问容器接口。
缺点 需要将主机网卡(NIC)设置为混杂模式(Promiscuous Mode),公有云平台不允许。
2.4.1 使用场景¶
一个物理网络上,配置了两个VLAN—VLAN100:10.0.0.0/24和VLAN 200:192.168.3.0/24。
2.4.1.1 需求一:接入容器到VLAN100 1、首先使用Macvlan驱动创建新的Docker网络,Macvlan驱动在连接到目标网络前,需要设置参数 * 子网信息 * 网关 * 可分配给容器的IP范围

2、创建macvlan100网络以及eth0.100子接口。
$ docker network create -d macvlan \
--subnet=10.0.0.0/24 \
--ip-range=10.0.0.0/25 \
--gateway=10.0.0.1 \
-o parent=eth0.100 \
macvlan100
--ip-range:告知Macvlan网络在子网中有哪些IP地址可以分配给容器。这些地址必须保留,不能用于其他节点或者DHCP服务器,因为没有任何管理层功能来检查IP区域重合的问题

3、将容器部署到该网络中,至此容器通过Macvlan接入了现有的VLAN当中。
$ docker container run -d --name mactainer1 \
--network macvlan100 \
alpine sleep 1d
2.4.2 用于故障排除的容器和服务日志¶
如果遇到容器间网络连接问题,检查daemon日志以及容器日志。在windows上,daemon日志存储在AppData\Local\Docker,可以通过Windows事件查看器来浏览。在Linux系统上,daemon日志的存储位置取决于当前系统正在使用的初始化方式。
容器日志生效的前提是应用进程在容器内部PID为1,并且将正常日志输出到STDOUT,将异常日志输出到STDERR。日志驱动就会将这些"日志"转发到日志驱动配置到指定的位置。 2.4.2.1 设置daemon日志详细程度 编辑daemon配置文件(/etc/docker/daemon.json),将debug设置为true,同时设置log-level为下面值: 1、debug:最详细的日志级别
{
<Snip>
"debug":true,
"log-level":"debug",
<Snip>
}
2、info:默认值,次详细日志级别
{
<Snip>
"debug":true,
"log-level":"info",
<Snip>
}
3、warn:第三详细日志级别
{
<Snip>
"debug":true,
"log-level":"warn",
<Snip>
}
4、error:第四详细日志级别
{
<Snip>
"debug":true,
"log-level":"error",
<Snip>
}
5、fatal:最粗略的日志级别
{
<Snip>
"debug":true,
"log-level":"fatal",
<Snip>
}
注意:修改完配置文件后,需要重启!!!
2.4.2.2 常用日志驱动以及如何配置 1、json-file,默认日志驱动 2、journald,只在运行systemd的Linux主机中生效
{
"log-driver":"journald"
}
3、syslog (1)在/etc/docker/daemon.json文件中添加以下内容
{
"log-driver":"syslog"
}
4、splunk
{
"log-driver":"splunk"
}
5、gelf
{
"log-driver":"gelf"
}
2.4.2.3 设置日志策略 在启动的时候通过--log-driver和--log-opts指定特定的日志驱动即可,这样会覆盖掉daemon.json中的配置。
2.5 服务发现¶
服务发现允许容器和Swarm服务通过名称互相定位。唯一的要求就是需要处于同一个网络当中。
底层实现是利用了Docker内置的DNS服务器,为每个容器提供DNS解析功能。
下面展示容器"c1"通过名称ping容器"c2"的过程。 1、ping c2命令调用本地DNS解析器,尝试将"c2"解析为具体IP地址。 2、如果本地解析器在本地缓存中没有找到"c2"对应的IP地址,本地解析器会向Docker DNS服务器发起一个递归查询。本地服务器解析器是预先配置好并知道Docker DNS服务器细节的。 3、Docker DNS服务器记录了全部容器名称和IP地址的映射关系,其中容器名称是容器在创建时通过--name或者--net-alias参数设置的。 4、DNS服务器返回"c2"对应的IP地址到"c1"本地DNS解析器。 5、ping命令发往"c2"对应的IP地址。
2.5.1 参数说明¶
1、--name 用于指定容器名称
$ docker container run -it --name c1 alpine sh
2、--dns 用于指定自定义的DNS服务列表,以防出现内置的Docker DNS服务器解析失败的情况。
$ docker container run -it --name c1 --dns=8.8.8.8 alpine sh
3、--dns-search 用于指定自定义查询时所使用的域名
$ docker container run -it --name c1 --dns-search=dockercerts.com alpine sh
2.6 Ingress网络¶
Swarm支持两种服务发布模式,两种模式均保证服务从集群外可访问。一种是Ingress(默认),另一种是Host模式。
2.6.1 Host模式¶
以Host模式发布的服务只能通过运行服务副本的节点来访问。
下面以Host模式举例:
$ docker service create -d --name svc1 --publish published=5000,target=80,mode=host nginx
针对以上参数进行说明: 1、published=5000表示服务通过端口5000提供外部服务 2、target=80表示发送到published端口5000的请求,映射到服务器副本的80端口 3、mode=host表示只有外部请求发送到运行了服务副本的节点才可以访问该服务
2.6.2 Ingress模式¶
通过Ingress模式发布服务,可以保证从Swarm集群中任一节点(即使没有运行服务的副本)都能访问该服务.在底层,Ingress模式采用名为Service Mesh或者Swarm Mode Service Mesh的四层路由网络来实现。
下面以Ingress模式举例:
$ docker service create -d --name svc1 --network overnet --publish published=5000,target=80,mode=host nginx
针对以上参数进行说明: 1、published=5000表示服务通过端口5000提供外部服务 2、target=80表示发送到published端口5000的请求,映射到服务器副本的80端口 3、mode=host表示只有外部请求发送到运行了服务副本的节点才可以访问该服务 4、network overnet表示指定网络类型为overnet
总结 1、入站流量无论访问哪个swarm节点中的任一一个,结果都是一样。 2、如果存在多个运行中的副本,流量会平均在每个副本之上。
三、Docker网络相关操作¶
3.1 创建网络¶
1、执行docker network create -d bridge localnet命令创建新的单机桥接网络,名为"localnet"。其中-d参数用于指定驱动(网络类型),-o encrypted对数据层进行加密
[root@localhost ~]# docker network create -d bridge localnet
1c731406ff136013a0aef3010053936511a9b6bc8f78b5ccdf329b76d1451503
2、执行docker network ls命令查看网络是否创建成功,这里观察到已成功创建成功
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
1c731406ff13 localnet bridge local
3.2 创建新容器并接入新建网络¶
1、执行docker container run -d --name c1 --network localnet alpine sleep 1d命令创建新容器c1并接入新建网络localnet
[root@localhost ~]# docker container run -d --name c1 --network localnet alpine sleep 1d
3.3 删除网络¶
1、执行docker network rm localnet命令删除名为localnet的网络
[root@localhost ~]# docker network rm localnet
localnet
2、执行docker network ls命令观察网络已删除
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
3.4 删除Docker主机未使用的网络¶
[root@localhost ~]# docker network prune
3.5 列出本地Docker主机网络¶
1、执行docker network ls命令列出运行在本地Docker主机上的全部网络
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f72d8bd25765 bridge bridge local
8e3f74e2f68e docker_gwbridge bridge local
81ab398335fd host host local
2bc306370a20 none null local
3.6 查看Docker网络详细配置¶
1、执行docker network inspect bridge命令提供Docker网络详细配置信息
[root@localhost ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "f72d8bd257657c7a9ef682638a9f79c39dbf1b9fc947553f940b3634fc7eeb18",
"Created": "2022-11-05T08:53:34.516807186+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
3.7 查看docker0网桥¶
1、执行ip link show docker0命令查看docker0网络
[root@localhost ~]# ip link show docker0
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:8b:0f:97:d0 brd ff:ff:ff:ff:ff:ff
3.8 查看Linux网桥¶
1、执行brctl show命令使用brctl工具查看网桥,其中br-a2cf1b22d749与上述新建的网络localnet对应。
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-a2cf1b22d749 8000.0242bd03bc11 no
docker0 8000.02428b0f97d0 no
docker_gwbridge 8000.0242db9678df no
virbr0 8000.5254002fa5d0 yes virbr0-nic