介绍Docker网络体系的基本原理,比如容器网络模型(Container Network Model, CNM)以及Libnetwork,同时还会进行实际操作来搭建几种网络。

Docker网络架构源自一种叫作容器网络模型(CNM)的方案,该方案是开源的并且支持插接式连接。Libnetwork是Docker对CNM的一种实现,提供了Docker核心网络架构的全部功能。不同的驱动可以通过插拔的方式接入Libnetwork来提供定制化的网络拓扑。

为了实现开箱即用的效果,Docker封装了一系列本地驱动,覆盖了大部分常见的网络需求。其中包括单机桥接网络(Single-Host Bridge Network)、多机覆盖网络(Multi-Host Overlay),并且支持接入现有VLAN,也有第三房提供的驱动

Libnetwork提供了本地服务发现和基础的容器负载均衡解决方案

Docker网络

内容分为如下几部分。
● 基础理论。
● 单机桥接网络。
● 多机覆盖网络。
● 接入现有网络。
● 服务发现。
● Ingress网络。

基础理论
顶层设计中,Docker网络架构由3个主要部分构成:CNM、Libnetwork和驱动。

CNM是设计标准。在CNM中,规定了Docker网络架构的基础组成要素。Libnetwork是CNM的具体实现,并且被Docker采用。Libnetwork通过Go语言编写,并实现了CNM中列举的核心组件。驱动通过实现特定网络拓扑的方式来拓展该模型的能力。

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图


Docker网络架构的设计规范是CNM,结构如下:

docker 网络环境拓扑图 docker网络架构_Docker_02


Docker环境中最小的调度单位就是容器,而CNM负责为容器提供网络功能。下图展示了CNM组件是如何与容器进行关联的——沙盒被放置在容器内部,为容器提供网络连接。

docker 网络环境拓扑图 docker网络架构_容器_03


容器A只有一个接口(终端)并连接到了网络A。容器B有两个接口(终端)并且分别接入了网络A和网络B。容器A与B之间是可以相互通信的,因为都接入了网络A。但是,如果没有三层路由器的支持,容器B的两个终端之间是不能进行通信的。

需要重点理解的是,终端与常见的网络适配器类似,这意味着终端只能接入某一个网络。因此,如果容器需要接入到多个网络,就需要多个终端

对前面的内容进行拓展,加上了Docker主机。虽然容器A和容器B运行在同一个主机上,但其网络堆栈在操作系统层面是互相独立的,这一点由沙盒机制保证。

docker 网络环境拓扑图 docker网络架构_容器_04


Libnetwork

CNM是设计规范文档,Libnetwork是标准的实现。Libnetwork实现了CNM中定义的全部3个组件。此外它还实现了本地服务发现(Service Discovery)、基于Ingress的容器负载均衡,以及网络控制层和管理层功能。(目前只能初步理解)

驱动

如果说Libnetwork实现了控制层和管理层功能,那么驱动就负责实现数据层。比如,网络连通性和隔离性是由驱动来处理的,驱动层实际创建网络对象也是如此,其关系如图

Docker封装了若干内置驱动,通常被称作原生驱动或者本地驱动。在Linux上包括Bridge、Overlay以及Macvlan,在Windows上包括NAT、Overlay、Transport以及L2 Bridge

docker 网络环境拓扑图 docker网络架构_Docker_05


单机桥接网络

● 单机意味着该网络只能在单个Docker主机上运行,并且只能与所在Docker主机上的容器进行连接。
● 桥接意味着这是802.1.d桥接的一种实现(二层交换机)。

每个Docker主机都有一个默认的单机桥接网络。在Linux上网络名称为bridge,除非通过命令行创建容器时指定参数–network,否则默认情况下,新创建的容器都会连接到该网络

docker 网络环境拓扑图 docker网络架构_Docker_06


docker network ls 命令可以查看容器网络

docker network inspect 则是查看容器详细信息

在Linux Docker主机之上,默认的“bridge”网络被映射到内核中为“docker0”的Linux网桥

Docker默认“bridge”网络和Linux内核中的“docker0”网桥之间的关系如图

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图_07

对的内容进行了扩展,在顶部补充了接入“bridge”网络的容器。“bridge”网络在主机内核中映射到名为“docker0”的Linux网桥,该网桥可以通过主机以太网接口的端口映射进行反向关联。

使用docker network create命令创建新的单机桥接网络,名为“localnet”。

docker network ls命名的输出内容当中。使用Linux,那么在主机内核中还会创建一个新的Linux网桥

通过使用Linux brctl工具来查看系统中的Linux网桥。读者可能需要通过命令apt-get install bridge-utils来安装brctl二进制包

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图_08


使用 brctl show 命令会输出内容中包含了两个网桥。根据输出内容两个网桥目前都没有开启STP,并且也都没有任何设备接入(对应的interfaces列为空目前,主机上的网桥配置

docker 网络环境拓扑图 docker网络架构_Docker_09


使用 docker create run -d --name c1 --network localnet alpine sleep

1d 命令将容器加入localnet网络中,然后通过

docker 网络环境拓扑图 docker网络架构_网络_10


可以看到具体信息。

如果再次运行brctl show命令,就能看到c1的网络接口连接到了网桥。

下图展示了上述关系

docker 网络环境拓扑图 docker网络架构_容器_11

如果在相同网络中继续接入新的容器,那么在新接入容器中是可以通过“c1”的容器名称来ping通的。这是因为新容器都注册到了指定的Docker DNS服务,所以相同网络中的容器可以解析其他容器的名称。Linux上默认的Bridge网络是不支持通过Docker DNS服务进行域名解析的。自定义桥接网络可以!

到目前为止,提到的桥接网络中的容器只能与位于相同网络中的容器进行通信。但是,可以使用端口映射(Port Mapping)来绕开这个限制。

端口映射允许将某个容器端口映射到Docker主机端口上,对于配置中指定的Docker主机端口,任何发送到该端口的流量,都会被转发到容器

docker 网络环境拓扑图 docker网络架构_网络_12


端口映射工作原理大致如此,但这种方式比较笨重并且不能扩展

举个例子,在只有单一容器的情况下,它可以绑定到主机的任意端口。这意味着其他容器就不能再使用已经被Nginx容器占用的5000端口了。这也是单机桥接网络只适用于本地开发环境以及非常小的应用的原因。

多机覆盖网络
后续学习

接入现有网络

能够将容器化应用连接到外部系统以及物理网络的能力是非常必要的。常见的例子是部分容器化的应用——应用中已容器化的部分需要与那些运行在物理网络和VLAN上的未容器化部分进行通信。

Docker内置的Macvlan驱动(Windows上是Transparent)就是为此场景而生。通过为容器提供MAC和IP地址

docker 网络环境拓扑图 docker网络架构_docker_13


Macvlan的优点是性能优异,因为无须端口映射或者额外桥接,可以直接通过主机接口(或者子接口)访问容器接口。但是,Macvlan的缺点是需要将主机网卡(NIC)设置为混杂模式(Promiscuous Mode)

但是Macvlan在公有云上并不可行。

假设有一个物理网络,其上配置了两个VLAN——VLAN 100:10.0.0.0/24和VLAN 200:192.168.3.0/24

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图_14


接下来,添加一个Docker主机并连接到该网络

有一个需求是将容器接入VLAN 100。为了实现该需求,首先使用Macvlan驱动创建新的Docker网络。但是,Macvlan驱动在连接到目标网络前,需要设置几个参数。比如以下几点。
● 子网信息。
● 网关。
● 可分配给容器的IP范围。

docker 网络环境拓扑图 docker网络架构_网络_15


● 主机使用的接口或者子接口。

docker 网络环境拓扑图 docker网络架构_网络_16


该命令会创建macvlan100网络以及eth0.100子接口

docker 网络环境拓扑图 docker网络架构_Docker_17


Macvlan采用标准Linux子接口,需要为其打上目标VLAN网络对应的ID。在本例中目标网络是VLAN 100,所以将子接口标记为 .100(etho.100)。

通过–ip-range参数告知Macvlan网络在子网中有哪些IP地址可以分配给容器。这些地址必须被保留,不能用于其他节点或者DHCP服务器,因为没有任何管理层功能来检查IP区域重合的问题。

macvlan100网络已为容器准备就绪,执行以下命令将容器部署到该网络中

docker 网络环境拓扑图 docker网络架构_容器_18


切记,下层网络(VLAN 100)对Macvlan的是不知情的,即macvlan对下层网络透明,只能看到容器的MAC和IP地址。在该基础之上,mactainer1容器可以ping通任何加入VLAN 100的系统,并进行通信。

注: 如果上述命令不能执行,可能是因为主机NIC不支持混杂模式。切记公有云平台不允许混杂模式。

docker 网络环境拓扑图 docker网络架构_网络_19


Macvlan也支持VLAN的Trunk功能。这意味着可以在相同的Docker主机上创建多个Macvlan网络

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图_20


用于故障排除的容器和服务日志

当认为遇到容器间网络连接问题时,检查daemon日志以及容器日志(应用日志)是非常有必要的。

在Linux上,daemon日志的存储位置取决于当前系统正在使用的初始化方式。如果是Systemd,日志会存储在Journald,并且可以通过journalctl -u docker.service命令查看;如果不是Systemd读者需要查看如下位置。
● Ubuntu系统:upstart:/var/log/upstart/docker.log。
● RHEL系列:systems:/var/log/messages。
● Debian:/var/log/daemon.log。

还可以设置daemon日志的详细程度。可以通过编辑daemon配置文件(daemon.json),将debug设置为true,并同时设置log-level为下面的某个值。
● debug:最详细的日志级别。
● info:默认值,次详细日志级别。
● warn:第三详细日志级别。
● error:第四详细日志级别。
● fatal:最粗略的日志级别。
修改配置文件之后,需要重启Docker才会生效。这就是daemon日志了

可以通过docker container logs命令查看单独的容器日志,通过docker service logs可以查看Swarm服务日志。但是,Docker支持多种日志驱动,并不是每种都能通过docker logs命令查看的。

就像引擎日志的驱动和配置一样,每个Docker主机也为容器提供了默认的日志驱动以及配置。其中包括json-file(默认)、journald(只在运行systemd的Linux主机中生效)、syslog、splunk和gelf。

json-file和journald可能是较容易配置的,并且均可通过doker logs和docker service logs命令查看。具体命令格式为docker logs 和docker service logs

可以为某个容器或者服务配置单独的日志策略,只需在启动的时候通过–log-driver和–log-opts指定特定的日志驱动即可。这样会覆盖掉daemon.json中的配置。

通常是很有可能在daemon日志或者容器日志中找到网络连接相关异常的。

服务发现
作为核心网络架构,Libnetwork还提供了一些重要的网络服务。服务发现(Service Discovery)允许容器和Swarm服务通过名称互相定位。唯一的要求就是需要处于同一个网络当中。

其底层实现是利用了Docker内置的DNS服务器,为每个容器提供DNS解析功能。

但是,服务发现是受网络限制的。这意味着名称解析只对位于同一网络中的容器和服务生效。如果两个容器在不同的网络,那么就不能互相解析。

用户可以为Swarm服务和独立容器进行自定义的DNS配置。举个例子,–dns参数允许读者指定自定义的DNS服务列表,以防出现内置的Docker DNS服务器解析失败的情况。此外也可以使用–dns-search参数指定自定义查询时所使用的域名(例如当查询名称并非完整域名的时候)。在Linux上,上述工作都是通过在容器内部/etc/resolve.conf文件内部增加条目来实现的。

下面的例子会启动一个新的容器,并添加8.8.8.8 Google DNS服务器,同时指定dockercents.com作为域名添加到非完整查询当中。

docker 网络环境拓扑图 docker网络架构_网络_21


Ingress网络

● Ingress模式(默认)
● Host模式。

通过Ingress模式发布的服务,可以保证从Swarm集群内任一节点(即使没有运行服务的副本)都能访问该服务;以Host模式发布的服务只能通过运行服务副本的节点来访问,如果需要以Host模式发布服务,则读者需要使用–publish参数的完整格式,并添加mode=host

docker 网络环境拓扑图 docker网络架构_容器_22


Host模式的例子:

docker 网络环境拓扑图 docker网络架构_docker 网络环境拓扑图_23


完整格式如–publish published=5000,target=80,mode=host。该方式采用逗号分隔多个参数,并且逗号前后不允许有空格

通常使用Ingress模式。在底层,Ingress模式采用名为Service Mesh或者Swarm Mode Service Mesh的四层路由网络来实现。下图展示了Ingress模式下一个外部请求是如何流转,最终访问到服务的。

docker 网络环境拓扑图 docker网络架构_容器_24


说明:

● 图中最上方命令部署了一个名为“svc1”的Swarm服务。该服务连接到了overnet网络,并发布到5000端口。
● 按上述方式发布Swarm服务(--publish published=5000,target=80)会在Ingress网络的5000端口进行发布。因为Swarm全部节点都接入了Ingress网络,所以这个端口被发布到了Swarm范围内。
● 集群确保到达Ingress网络中任意节点的5000端口的流量,都会被路由到80端口的“svc1”服务。
● 当前“svc1”服务只部署了一个副本,集群中有一条映射规则:“所有访问Ingress网络5000端口的流量都需要路由到运行了“svc1”服务副本的节点之上”。
● 红线展示了访问Node的15000端口的流量,通过Ingress网络,被路由到了Node2节点正在运行的服务副本之上。

入站流量可能访问4个Swarm节点中的任意一个,但是结果都是一样的,了解这一点很重要。这是因为服务通过Ingress网络实现了Swarm范围内的发布。

此外,还有一点很重要:如果存在多个运行中的副本,流量会平均到每个副本之上,如图展示的一样。

docker 网络环境拓扑图 docker网络架构_docker_25


Docker网络——命令

● Docker网络有自己的子命令,主要包括以下几种。

● docker network ls用于列出运行在本地Docker主机上的全部网络。

● docker network create创建新的Docker网络。默认情况下,在Windows上会采用NAT驱动,在Linux上会采用Bridge驱动。读者可以使用-d参数指定驱动(网络类型)。docker network create -d overlay overnet会创建一个新的名为overnet的覆盖网络,其采用的驱动为Docker Overlay。

● docker network inspect提供Docker网络的详细配置信息。

● docker network prune删除Docker主机上全部未使用的网络。

● docker network rm删除Docker主机上指定网络。