首页
学习
活动
专区
圈层
工具
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

【Docker】深入浅出Docker技术网络化——Docker网络

网络已经无处不在。每当基础设施出现问题时,被抱怨的通常是网络。很大一

部分原因是,网络负责连接一切——无网络,无APP!

在Docker早期阶段,网络设计确实非常复杂——真的很复杂!那时候配置网络几乎是一种乐趣。

本部分我们主要介绍Docker网络体系的基本原理,比如容器网络模型

(Container Network Model, CNM)以及Libnetwork,同时还会进行实际操作来

搭建几种网络。

一、 Docker网络——简介

Docker在容器内部运行应用,这些应用之间的交互依赖于大量不同的网络,这

意味着Docker需要强大的网络功能。

幸运的是,Docker对于容器之间、容器与外部网络和VLAN之间的连接均有相应的解决方案。后者对于那些需要跟外部系统(如虚拟机和物理机)的服务打交

道的容器化应用来说至关重要。

Docker网络架构

Docker网络架构源自一种叫作容器网络模型(CNM)的方案,该方案是开源的并且支持插接式连接。

Libnetwork是Docker对CNM的一种实现,提供了Docker核心网络架构的全部功能。不同的驱动可以通过插拔的方式接入Libnetwork来提供定制化的网络拓扑。

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

Docker生态系统中的合作伙伴通过提供驱动的方式,进一步拓展了Docker的网络功能。

最后要说的是,Libnetwork提供了本地服务发现和基础的容器负载均衡解决方案。

二、 Docker网络——详解

本节内容分为如下几部分:

基础理论

单机桥接网络

多机覆盖网络

接入现有网络

服务发现

Ingress网络

1 基础理论

在顶层设计中,Docker网络架构由3个主要部分构成:CNM、Libnetwork和驱

动。

CNM是设计标准。在CNM中,规定了Docker网络架构的基础组成要素。

Libnetwork是CNM的具体实现,并且被Docker采用。Libnetwork通过Go语言编写,并实现了CNM中列举的核心组件。

驱动通过实现特定网络拓扑的方式来拓展该模型的能力。

下图展示了顶层设计中的每个部分是如何组装在一起的。

接下来具体介绍每部分的细节

1.CNM

一切都始于设计!

Docker网络架构的设计规范是CNM。CNM中规定了Docker网络的基础组成要

素,完整内容见GitHub的docker/libnetwork库。

推荐通篇阅读该规范,不过其实抽象来讲,CNM定义了3个基本要素:沙盒

(Sandbox)、终端(Endpoint)和网络(Network)。

沙盒 是一个独立的网络栈。其中包括以太网接口、端口、路由表以及DNS配置。主要用于隔离容器网络与宿主机网络,形成了完全独立的容器网络环境。

终端 就是虚拟网络接口。就像普通网络接口一样,终端主要职责是负责创建连接。在CNM中,终端负责将沙盒连接到网络

网络 是802.1d网桥(类似大家熟知的交换机)的软件实现。因此,网络就是需要交互的终端的集合,并且终端之间相互独立。Docker 内部的虚拟网路和宿主机网络是存在隔离关系的,其目的主要是形成容器间的安全通讯环境。

下图展示了3个组件是如何连接的:

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

CNM组件与容器进行关联

容器A只有一个接口(终端)并连接到了网络A。容器B有两个接口(终端)并

且分别接入了网络A和网络B。容器A与B之间是可以相互通信的,因为都接入了网

络A。但是,如果没有三层路由器的支持,容器B的两个终端之间是不能进行通信

的。

需要重点理解的是,终端与常见的网络适配器类似,这意味着终端只能接入某

一个网络。因此,如果容器需要接入到多个网络,就需要多个终端。

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

补充:容器网络

容器网络发展到现在,形成了两大阵营:

Docker的CNM

Google、Coreos、Kuberenetes主导的CNI

CNM和CNI是网络规范或者网络体系,并不是网络实现,因此并不关心容器网络的实现方式( Flannel或者Calico等), CNM和CNI关心的只是网络管理

2.Libnetwork

CNM是设计规范文档,Libnetwork是标准的实现。

Libnetwork是开源的,采用Go语言编写,它跨平台(Linux以及Windows),并且被Docker所使用。

在Docker早期阶段,网络部分代码都存在于daemon当中。这简直就是噩梦——daemon变得臃肿,并且不符合UNIX工具模块化设计原则,即既能独立工作,又易于集成到其他项目。所以,Docker将该网络部分从daemon中拆分,并重构为一个叫作Libnetwork的外部类库。

现在,Docker核心网络架构代码都在Libnetwork当中。

正如你所期望,Libnetwork实现了CNM中定义的全部3个组件。

此外它还实现了本地服务发现(Service Discovery)、基于Ingress的容器负载均衡,以及网络控制层和管理层功能。

3.驱动

如果说Libnetwork实现了控制层和管理层功能,那么驱动就负责实现数据层。

比如,网络连通性和隔离性是由驱动来处理的,驱动层实际创建网络对象也是如

此,其关系如下图所示。

在 Docker 安装时,会自动安装一块 Docker 网卡称为 docker0,用于 Docker 各容器及宿主机的网络通信。

Docker封装了若干内置驱动,通常被称作原生驱动或者本地驱动。

在Linux上包括Bridge 、Overlay(跨主机) 以及Macvlan(跨主机)

在Windows上包括NAT、Overlay 、Transport 以及L2 Bridge

第三方也可以编写Docker网络驱动。这些驱动叫作远程驱动,例如Calico 、Contiv 、Kuryr 以及Weave

每个驱动都负责其上所有网络资源的创建和管理。

举例说明,一个叫作“prodfe-cuda”的覆盖网络由Overlay 驱动所有并管理。

这意味着Overlay 驱动会在创建、管理和删除其上网络资源的时候被调用。

为了满足复杂且不固定的环境需求,Libnetwork支持同时激活多个网络驱动。这意味着Docker环境可以支持一个庞大的异构网络。

★个人理解:CNM 就是一个设计文档,指导你怎么去实现容器网络,而 Libnetwork 和驱动则是其具体实现,从而确保容器网络的通信。

2 单机桥接网络(Bridge)

最简单的Docker网络就是单机桥接网络。

从名称中可以看到两点:

单机 意味着该网络只能在单个Docker主机上运行,并且只能与所在Docker主机上的容器进行连接。

桥接 意味着这是802.1.d桥接的一种实现(二层交换机)。

实际上,这两种驱动工作起来毫无差异。

上图展示了两个均包含相同本地桥接网络mynet的Docker主机。虽然网络是相同的,但却是两个独立的网络。这意味着上图中容器无法直接进行通信,因为并不在一个网络当中。

每个Docker主机都有一个默认的单机桥接网络

在Linux上网络名称为bridge

在Windows上叫作nat

除非你通过命令行创建容器时指定参数--network,否则默认情况下,在没有使用 --network 参数指定要加入的 docker 网络时,默认都是加入 Docker 默认的单机桥接网络,也就是下面的 name 为 bridge 的网络。

下面列出了docker network ls 命令在刚完成安装的Docker主机上的输出内容。输出内容做了截取处理,只展示了每个主机上的默认网络。注意,网络的名称和创建时使用的驱动名称是一致的——这只是个巧合。

而默认的 bridge 网络是被映射到内核中为 docker0 的网桥上。

docker network inspect 命令就是一个信息宝藏。如果你对底层细节的内容感兴趣,强烈推荐仔细阅读该命令的输出内容。

docker network inspect bridge

[

{

"Name": "bridge", << 在 Windows 上是nat

"Id": "333e184...d9e55",

"Created": "2018-01-15T20:43:02.566345779Z",

"Scope": "local",

"Driver": "bridge", << 在 Windows 上是nat

"EnableIPv6": false,

"IPAM": {

"Driver": "default",

"Options": null,

"Config": [

{

"Subnet": "172.17.0.0/16"

}

]

},

"Internal": false,

"Attachable": false,

"Ingress": false,

"ConfigFrom": {

"Network": ""

},

<Snip>

}

]

在Linux主机上,Docker网络由Bridge驱动创建,而Bridge底层是基于Linux内核中久经考验达15年之久的Linux Bridge技术。这意味着Bridge是高性能并且非常稳定的!同时这还表示可以通过标准的Linux工具来查看这些网络

$ ip link show docker0

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc...

link/ether 02:42:af:f9:eb:4f brd ff:ff:ff:ff:ff:ff

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

可以通过docker network inspect 命令观察到上面的输出内容。

$ docker network inspect bridge | grep bridge.name

"com.docker.network.bridge.name": "docker0",

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

示。

下图对上图的内容进行了扩展,在顶部补充了接入“bridge”网络的容器。

“bridge”网络在主机内核中映射到名为“docker0”的 Linux 网桥,该网桥可以通过主机以太网接口的端口映射进行反向关联。

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

//Linux

$ docker network create -d bridge localnet

//Windows

> docker network create -d nat localnet

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

接下来通过使用 Linux brctl 工具来查看系统中的 Linux 网桥。小伙伴可能需要通过命令 apt-get install bridge-utils 来安装 brctl 二进制包,或者根据所使用的 Linux 发行版选择合适的命令。

$ brctl show

输出内容中包含了两个网桥。第一行是前文提过的 docker0 网桥,该网桥对应 Docker 中的默认网络 bridge;第二个网桥(br-20c2e8ae4bbb)与新建的“localnet”Docker 桥接网络相对应。

两个网桥目前都没有开启 STP,并且也都没有任何设备接入(对应的 interfaces 列为空)。

目前,主机上的网桥配置如下图所示。

接下来创建一个新的容器,并接入到新建桥接网络 localnet 当中。如果是在 Windows 上进行操作,需要将命令中“alpine sleep 1d”替换为“microsoft/powershell:nanoserver pwsh.exe -Command Start-Sleep 86400”。

$ docker container run -d --name c1 \

--network localnet \

alpine sleep 1d

容器现在接入了 localnet 网络当中。读者可以通过 docker network inspect 命令来确认。

$ docker network inspect localnet --format

'{{json .Containers}}' {

"4edcbd...842c3aa": {

"Name": "c1",

"EndpointID": "43a13b...3219b8c13",

"MacAddress": "02:42:ac:14:00:02",

"IPv4Address": "172.20.0.2/16",

"IPv6Address": ""

}

},

输出内容表明“c1”容器已经位于桥接(Bridge/NAT)网络 localnet 之上。

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

$ brctl show

下图展示了上述关系。

如果在相同网络中继续接入新的容器,那么在新接入容器中是可以通过“c1”的容器名称来 ping 通的。这是因为新容器都注册到了指定的 Docker DNS 服务,所以相同网络中的容器可以解析其他容器的名称。

下面一起来测试一下。

1) 创建名为“c2”的容器,并接入“c1”所在的 localnet 网络。

//Linux

$ docker container run -it --name c2 \

--network localnet \

alpine sh

//Windows

> docker container run -it --name c2 `

--network localnet `

microsoft/powershell:nanoserver

当前终端会切换到“c2”容器中。

2) 在“c2”容器中,通过“c1”容器名称执行 ping 命令。

> ping c1

Pinging c1 [172.26.137.130] with 32 bytes of data:

Reply from 172.26.137.130: bytes=32 time=1ms TTL=128

Reply from 172.26.137.130: bytes=32 time=1ms TTL=128

Control-C

命令生效了!这是因为 c2 容器运行了一个本地 DNS 解析器,该解析器将请求转发到了 Docker 内部 DNS 服务器当中。

DNS 服务器中记录了容器启动时通过 --name 或者 --net-alias 参数指定的名称与容器之间的映射关系。

如果你仍处于容器中,可以尝试运行一些网络相关的命令。这是一种很好的了解 Docker 容器网络工作原理的方式。

下面的片段是在之前创建的 Windows 容器“c2”中运行 ipconfig 命令的输出内容。可以在前面 docker network inspect nat 命令输出中找到对应的 IP 地址。

> ipconfig

Windows IP Configuration

Ethernet adapter Ethernet:

Connection-specific DNS Suffix . :

Link-local IPv6 Address . . . . . : fe80::14d1:10c8:f3dc:2eb3%4

IPv4 Address. . . . . . . . . . . : 172.26.135.0

Subnet Mask . . . . . . . . . . . : 255.255.240.0

Default Gateway . . . . . . . . . : 172.26.128.1

到目前为止,前面提到的桥接网络中的容器只能与位于相同网络中的容器进行通信。

但是,可以使用端口映射(Port Mapping)来绕开这个限制。

端口映射(Port Mapping)

端口映射允许将某个容器端口映射到 Docker 主机端口上。

对于配置中指定的 Docker 主机端口,任何发送到该端口的流量,都会被转发到容器。

下图中展示了具体流量动向。

如上图所示,容器内部应用开放端口为 80。该端口被映射到了 Docker 主机的 10.0.0.15 接口的 5000 端口之上。最终结果就是访问 10.0.0.15:5000 的所有流量都被转发到了容器的 80 端口。

接下来通过示例了解将容器上运行着 Web 服务的端口 80,映射到 Docker 主机端口 5000 的过程。

示例使用 Linux 的 Nginx。如果使用 Windows 的话,可以将 Nginx 替换为某个 Windows 的 Web 服务镜像。

1) 运行一个新的 Web 服务容器,并将容器 80 端口映射到 Docker 主机的 5000 端口。

$ docker container run -d --name web \

--network localnet \

--publish 5000:80 \

nginx

2) 确认端口映射。

$ docker port web

80/tcp -> 0.0.0.0:5000

这表示容器 80 端口已经映射到 Docker 主机所有接口上的 5000 端口。

3) 通过 Web 浏览器访问 Docker 主机 5000 端口,验证配置是否生效,如下图所示。为了完成测试,需要知道 Docker 主机的 IP 地址或者 DNS 名称。如果使用 Windows 版 Docker 或者 Mac 版 Docker,可以使用 localhost 或者 127.0.0.1。

外部系统现在可以通过 Docker 主机的 TCP 端口 5000,来访问运行在桥接网络上的 Nginx 容器了。

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

举个例子,在只有单一容器的情况下,它可以绑定到主机的任意端口。这意味着其他容器就不能再使用已经被 Nginx 容器占用的 5000 端口了。

这也是单机桥接网络只适用于本地开发环境以及非常小的应用的原因。

https://www.bilibili.com/video/BV1kL411a7LB

Docker网络模式Linux - Bridge | Host | None

https://www.bilibili.com/video/BV1Aj411r71b

3 多机覆盖网络(Overlay)

后面我们还会去专门介绍多机覆盖网络,这里先简单提一下,内容尽量做到简洁。

覆盖网络适用于多机环境。它允许单个网络包含多个主机,这样不同主机上的容器间就可以在链路层实现通信。

覆盖网络是理想的容器间通信方式,支持完全容器化的应用,并且具备良好的伸缩性。

Docker为覆盖网络提供了本地驱动。这使得创建覆盖网络非常简单,只需要在docker network create 命令中添加--d overlay 参数。

4 接入现有网络

能够将容器化应用连接到外部系统以及物理网络的能力是非常必要的。

常见的例子是部分容器化的应用——应用中已容器化的部分需要与那些运行在物理网络和 VLAN 上的未容器化部分进行通信。

核心问题:如何将容器化的应用与运行在 VLAN 上的未容器化部分进行通信?

在传统的网络架构中,VLAN 用于将一个物理网络划分为多个逻辑上的局域网,以便隔离流量和提高安全性。

当企业的部分应用已经容器化,而其他部分仍然在传统的 VLAN 上运行时,就需要一种方法来让容器化的应用能够与 VLAN 上的应用通信。

允许 Docker 容器获得独立的 MAC 地址,这样它们就可以直接在物理网络上进行通信,就像它们是网络上的独立物理设备一样。

当容器化的应用能够直接接入现有的 VLAN 网络,实现与 未容器化应用的无缝通信,而不需要通过任何中间代理或网关。这种方法特别适用于需要将容器化应用逐步集成到现有 IT 基础设施中的场景。

Macvlan网络驱动使用详解

Docker 内置的 Macvlan 驱动(Windows 上是 Transparent)就是为此场景而生。通过为容器提供 MAC 和 IP 地址,让容器在物理网络上成为“一等公民”。

macvlan 是在 宿主机 网卡上创建多个子网卡,并分配独立的 IP 地址和 MAC 地址,把子网卡分配给容器实例来实现实例与物理网络的直通,并同时保持容器实例的隔离性。Host 收到数据包后,则根据不同的 MAC 地址把数据包从转发给不同的子接口,在外界来看就相当于多台主机。(物理网卡相当于交换机)

macvlan 网络会独占物理网卡,也就是说一张物理网卡只能创建一个 macvlan 网络

根据 macvlan 子接口之间的通信模式,macvlan 有四种网络模式:

vepa(virtual ethernet port aggregator) 模式

passthru 模式

private 模式

bridge 模式(默认模式,最常用的方式)

下图展示了具体内容。

Macvlan 的优点是性能优异,因为无须端口映射或者额外桥接,可以直接通过主机接口(或者子接口)访问容器接口。

但是,Macvlan 的缺点是需要将主机网卡(NIC)设置为混杂模式(Promiscuous Mode),这在大部分公有云平台上是不允许的。

所以 Macvlan 对于公司内部的数据中心网络来说很棒(假设公司网络组能接受 NIC 设置为混杂模式),但是 Macvlan 在公有云上并不可行。

接下来通过图片和一个假想场景加深对 Macvlan 的理解。

假设有一个物理网络,其上配置了两个 VLAN——

VLAN 100:10.0.0.0/24

VLAN 200:192.168.3.0/24

接下来,添加一个 Docker 主机并连接到该网络,如下图所示。

有一个需求是将容器接入 VLAN 100。为了实现该需求,首先使用 Macvlan 驱动创建新的 Docker 网络。但是,Macvlan 驱动在连接到目标网络前,需要设置几个参数。

比如以下几点:

子网信息

网关

可分配给容器的IP范围

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

下面的命令会创建一个名为 macvlan100 的 Macvlan 网络,该网络会连接到 VLAN 100

$ docker network create -d macvlan \

--subnet=10.0.0.0/24 \

--ip-range=10.0.00/25 \

--gateway=10.0.0.1 \

-o parent=eth0.100 \

macvlan100

该命令会创建 macvlan100 网络以及 eth0.100 子接口。当前配置如下图所示。

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

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

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

$ docker container run -d --name mactainer1 \

--network macvlan100 \

alpine sleep 1d

当前配置如下图所示。但是切记,下层网络(VLAN 100)对 Macvlan 的魔法毫不知情,只能看到容器的 MAC 和 IP 地址。在该基础之上,mactainer1 容器可以 ping 通任何加入 VLAN 100 的系统,并进行通信。

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

目前已经拥有了 Macvlan 网络,并有一台容器通过 Macvlan 接入了现有的 VLAN 当中。但是,这并不是结束。Docker Macvlan 驱动基于稳定可靠的同名 Linux内核驱动构建而成。

因此,Macvlan 也支持 VLAN 的 Trunk 功能。这意味着可以在相同的 Docker 主机上创建多个 Macvlan 网络,并且将容器按照下图的方式连接起来。

以上内容基本能涵盖 Macvlan。

Windows 也提供了类似的解决方案 Transparent 驱动。

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

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

daemon 日志

在 Windows 上,daemon 日志存储在 ∼AppData\Local\Docker,可以通过 Windows 事件查看器来浏览。

在 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。

Mac 版 Docker:∼/Library/Containers/com.docker.docker/Data/com. docker.driver.amd64-linux/console-ring。

还可以设置 daemon 日志的详细程度。可以通过编辑 daemon 配置文件(daemon.json),将 debug 设置为 true,并同时设置 log-level 为下面的某个值。

debug:最详细的日志级别。

info:默认值,次详细日志级别。

warn:第三详细日志级别。

error:第四详细日志级别。

fatal:最粗略的日志级别。

下面的片段摘自 daemon.json,其中开启了调试模式,并设置日志级别为 debug。该配置在所有 Docker 平台均有效。

{

  <Snip>

  "debug":true,

  "log-level":"debug",

  <Snip>

}

修改配置文件之后,需要重启 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 <container-name> 和 docker service logs <service-name>

如果采用了其他日志驱动,可以通过第三方平台提供的原生工具进行查看。

下面的片段为 daemon.json 文件的一部分,展示如何配置 Docker 主机使用 syslog 方式。

{

  "log-driver": "syslog"

}

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

容器日志生效的前提是应用进程在容器内部 PID 为 1,并且将正常日志输出到 STDOUT,将异常日志输出到 STDERR。日志驱动就会将这些“日志”转发到日志驱动配置指定的位置。

如果应用日志是写到某个文件的,可以利用符号链接将日志文件重定向到 STDOUT 和 STDERR。

下面的例子展示了通过运行 docker logs 命令查看某个使用 json-file 日志驱动,并且名为 vantage-db 容器的日志。

$ docker logs vantage-db

1:C 2 Feb 09:53:22.903 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

1:C 2 Feb 09:53:22.904 # Redis version=4.0.6, bits=64, commit=00000000, modi\

fied=0, pid=1

1:C 2 Feb 09:53:22.904 # Warning: no config file specified, using the defaul\

t config.

1:M 2 Feb 09:53:22.906 * Running mode=standalone, port=6379.

1:M 2 Feb 09:53:22.906 # WARNING: The TCP backlog setting of 511 cannot be e\

nforced because...

1:M 2 Feb 09:53:22.906 # Server initialized

1:M 2 Feb 09:53:22.906 # WARNING overcommit_memory is set to 0!

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

5 服务发现

作为核心网络架构,Libnetwork还提供了一些重要的网络服务。

服务发现(Service Discovery) 允许容器和Swarm服务通过名称互相定位。唯一的要求就是需要处于同一个网络当中。

我门为什么需要服务发现?

https://www.bilibili.com/video/BV1Zs4y1h7ku

服务发现(Service Discovery)是分布式系统中的一个关键组件,它允许系统中的不同服务或组件能够自动地发现并互相定位。在容器化和微服务架构中,服务发现尤其重要,原因包括:

动态环境:在容器化环境中,容器可能会频繁地启动、停止或重新部署。服务发现机制可以确保服务之间的连接能够适应这种动态变化,自动更新并维护准确的服务位置信息。

负载均衡:服务发现可以与负载均衡策略配合使用,将请求分发到多个服务实例上,从而提高系统的可用性和性能。

弹性伸缩:在微服务架构中,服务可能需要根据负载动态地扩展或缩减。服务发现确保新加入的实例能够被其他服务发现,同时下线的实例能够被及时移除。

简化服务间通信:服务发现允许服务通过名称(而不是硬编码的IP地址)进行通信,这简化了服务间的交互,并减少了因IP地址变化导致的配置更新。

提高容错性:当某个服务实例失败时,服务发现可以帮助其他服务快速地找到健康的服务实例,从而提高系统的容错性和稳定性。

简化运维管理:服务发现减少了手动配置和管理服务间连接的需要,从而降低了运维的复杂性和出错率。

支持云原生应用:在云原生环境中,服务发现是实现应用的可移植性和可扩展性的关键技术之一,它支持应用在不同的云环境或本地数据中心中无缝运行。

Docker的网络服务发现与负载均衡功能在Docker Swarm模式下特别有用。Swarm是Docker的一个集群管理工具,它允许将多个Docker主机组合成一个单一的、可伸缩的、高可用的集群。在这个集群中,可以部署和管理服务,而Docker Swarm会负责在集群节点之间分配和调度容器。

服务发现是Swarm的一个核心功能,它允许服务自动找到彼此,而不需要硬编码主机名或IP地址。Swarm使用DNS和VIP(虚拟IP地址)来实现服务发现。每个服务在Swarm中都会被分配一个唯一的名称和VIP,其他服务可以通过这个名称或VIP来访问该服务,而不需要知道底层容器的实际IP地址。

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

下图展示了容器“c1”通过名称 ping 容器“c2”的过程。

Swarm 服务原理相同:

下面逐步分析整个过程:

1) ping c2 命令调用本地 DNS 解析器,尝试将“c2”解析为具体 IP 地址。每个 Docker 容器都有本地 DNS 解析器。

2) 如果本地解析器在本地缓存中没有找到“c2”对应的 IP 地址,本地解析器会向 Docker DNS 服务器发起一个递归查询。本地服务解析器是预先配置好并知道 Docker DNS 服务器细节的。

3) Docker DNS 服务器记录了全部容器名称和 IP 地址的映射关系,其中容器名称是容器在创建时通过 --name 或者 --net-alias 参数设置的。这意味着 Docker DNS 服务器知道容器“c2”的 IP 地址。

4) DNS 服务返回“c2”对应的 IP 地址到“c1”本地 DNS 解析器。之所以会这样是因为两个容器位于相同的网络当中,如果所处网络不同则该命令不可行。

5) ping 命令被发往“c2”对应的 IP 地址。

每个启动时使用了 --name 参数的 Swarm 服务或者独立的容器,都会将自己的名称和 IP 地址注册到 Docker DNS 服务。这意味着容器和服务副本可以通过 Docker DNS 服务互相发现。

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

关于服务发现和名称解析最后要说一点。

用户可以为 Swarm 服务和独立容器进行自定义的 DNS 配置。举个例子,--dns 参数允许读者指定自定义的 DNS 服务列表,以防出现内置的 Docker DNS 服务器解析失败的情况。

此外也可以使用 --dns-search 参数指定自定义查询时所使用的域名(例如当查询名称并非完整域名的时候)。

在 Linux 上,上述工作都是通过在容器内部 /etc/resolve.conf 文件内部增加条目来实现的。

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

6 Ingress网络

Docker Swarm支持两种服务发布模式,两种模式均保证服务从集群外可访问。

Ingress模式(默认)

Host模式

通过 Ingress 模式发布的服务,可以保证从 Swarm 集群内任一节点(即使没有运行服务的副本)都能访问该服务;

以 Host 模式发布的服务只能通过运行服务副本的节点来访问。

下图展示了两种模式的区别

Ingress 模式是默认方式,这意味着任何时候读者通过 -p 或者 --publish 发布服务的时候,默认都是 Ingress 模式;如果需要以 Host 模式发布服务,则读者需要使用 --publish 参数的完整格式,并添加 mode=host。下面一起来看 Host 模式的例子。

$ docker service create -d --name svc1 \

--publish published=5000,target=80,mode=host \

nginx

关于该命令的一些说明。docker service mode 允许读者使用完整格式语法或者简单格式语法来发布服务。简单格式如 -p 5000:80,前面已经多次出现。但是,读者不能使用简单格式发布 Host 模式下的服务。

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

published=5000 表示服务通过端口 5000 提供外部服务。

target=80 表示发送到 published 端口 5000 的请求,会映射到服务副本的 80 端口之上。

mode=host 表示只有外部请求发送到运行了服务副本的节点才可以访问该服务。

通常使用 Ingress 模式。

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

上图中最上方命令部署了一个名为“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 network 命令用于管理网络。可以使用 docker network 的子命令创建,列出,检查,删除,连接和断开网络。

语法格式如下:

docker network COMMAND

主要的 Docker 网络子命令如下表所示

docker network create -d overlay overnet 会创建一个新的名为overnet的覆盖网络,其采用的驱动为Docker Overlay

小结

容器网络模型(CNM)是Docker网络架构的主要设计文档,它定义了Docker网络中用到的3个主要结构——沙盒、终端以及网络。

Libnetwork是开源库,采用Go编写,实现了CNM。Docker使用了该库,并且Docker网络架构的核心代码都在该库当中。Libnetwork同时还提供了Docker网络控制层和管理层的功能。

驱动通过实现特定网络类型的方式扩展了Docker网络栈(Libnetwork),例如桥接网络和覆盖网络。Docker内置了几种网络驱动,同时也支持第三方驱动。

单机桥接网络是基本的Docker网络类型,对于本地开发和小型应用来说也十分适用。单机桥接网络不可扩展,并且对外发布服务依赖于端口映射。Linux Docker使用内置的Bridge 驱动实现单机桥接网络,而Windows Docker使用内置的NAT驱动来实现。

覆盖网络是当下流行的方式,并且是一种出色的多机容器网络方案。

Macvlan 驱动(在Windows中是Transparent )允许容器接入现存物理网络以及VLAN。通过赋予容器MAC和IP地址的方式,让容器成为网络中的“一等公民”。不过,该驱动需要主机的NIC支持混杂模式,这意味着该驱动在公有云上无法使用。

Docker使用Libnetwork实现了基础服务发现功能,同时还实现了服务网格,支持对入站流量实现容器级别负载均衡。

——The  End——

记得点赞、分享,让更多的朋友一起探索这个IT世界的新篇章!

AIGC周边正在发布,关注生活,冻龄青春

推荐阅读

【Docker】深入浅出Docker基础——容器发展之路

【Docker】深入浅出Docker基础——走进Docker

【Docker】深入浅出Docker基础——Docker安装

【云部署】Win11+WSL2+Ubuntu+Docker-Desktop 支持GPU的深度学习环境搭建

【Docker】深入浅出Docker基础进阶——纵观Docker的运维与开发

【Docker】深入浅出Docker技术——Docker引擎(理论)

【Docker】深入浅出Docker技术——Docker镜像

【Docker】深入浅出Docker技术——Docker容器

【Docker】深入浅出Docker技术——应用的容器化(单节点Dockerfile)

【Docker】从零开始:教你将程序打包成 Docker 镜像

【Docker】秒懂 Dockerfile:构建容器镜像的必备利器

【Docker】深入浅出Docker技术——应用的容器化(单引擎多容器Docker Compose编排部署应用)

【Docker】深入浅出Docker技术——容器的规模化(跨主机单服务Docker Swarm集群)

【Docker】Docker容器在两个不同网络间实现连通

【网络通信】MacVlan 让 Docker 实现上网自由

【NAS】Docker 配置Macvlan网络模式,实现单个容器限速与管控

【Docker】125个Docker的常用命令

【NAS】轻松搞定绿联UGOS系统的Docker远程访问!

【Docker】Play with Docker 在线实验室 使用教程

【NAS】NAS用户必备应用,170+ 好玩的docker容器应用,好用docker新年汇总

【网络通信】虚拟机三种网络模式:桥接模式,NAT模式,仅主机模式

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OdFPGYPvfaM5AbU0tqWjNO4g0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券