高相林 分布式实验室

剖析 | 全方位解读Docker 1.9新特性_Java

剖析 | 全方位解读Docker 1.9新特性_Jav_02

十一月四号,Docker对1.9进行了发布,新的发行版中添加了很多有趣的特性,下面我就对新的发行版中的新特性做一个简单的介绍。

  • 跨主机网络:新的网络设备可以支持用户创建基于多个主机的虚拟网络,使容器间可以跨网络通信。

  • 持久化存储:Docker 1.9 包含一个重新设计的完整存储卷管理系统,这使得用户可以更加容易的从前端来管理这些数据卷。

  • Docker Swarm 1.0:修复bug并对其进行大量优化。Docker公司在1000个节点上测试了30000个容器,swarm可以如丝般润滑的运行。

  • Docker Engine 1.9:新的Docker Engine中加入了如下新特性:Dockerfile 的构建时参数、并行镜像 pull、自定义 stop 信号、AWS CloudWatch logging driver和磁盘 I/O metrics。

  • Docker Compose 1.5:Docker Compose 是一个定义并运行多容器应用的工具,它有如下更新:支持 Windows、Compose 文件中的环境变量、对多环境更好的支持、和 networking 集成和Compose file 校验。

  • Docker Toolbox:这个工具可以使Mac和Windows支持这些新特性。

  • Docker Registry 2.2:主要做了以下更新:支持 Google Cloud Storage、只读模式、支持可配置主机名、基于文件的存在配置、可配置的 HTTP 健康检查和可配置的 HTTP 响应 headers。 详细的更新说明可以参照《Docker 1.9 发布:Swarm 和跨主机网络进入 production-ready 阶段》和Announcing Docker 1.9: Production-ready Swarm and Multi-host Networking。

剖析 | 全方位解读Docker 1.9新特性_Jav_03

下面就对我比较感兴趣的几点,做一下详细的介绍。

首先,此次发行版最引人注目的就是Docker的跨主机网络了。早在六月的DockerCon大会上Docker公司就宣布已经开始进行对Docker Network的试验性工作。在1.9中docker network命令脱离了实验分支,正式进入了发行版中。有了新的Networking我们可以创建虚拟网络,然后将container加入到虚拟网络中,以获得最适合所部署应用的网络拓扑结构。

和传统的links模式相比,新的Networking有如下三点改进:

  1. 可以跨越不同的物理和虚拟主机,连接不同的容器。

  2. 用户可以轻松的停止,开启和重启容器,而不用担心破坏容器之间的相互连接。

  3. 用户可以以任何顺序创建容器。

在清楚了新的Networking的特性之后,我们来看一下这部分的实现原理。

Networking的跨主机部分使用的时ovs(Open vSwitch)和VXLAN隧道进行实现。关于容器之间的隔离,则使用了iptables。

要清楚Networking的执行流,首先要清楚如下三个概念:

  1. Sandbox:一个Sandbox包含了一个容器的网络栈。其中包括容器的管理接口,路由表和DNS设置。主要是通过namespace和cgroup进行实现。一个Sandbox可以包括多个Endpoint。

  2. Endpoint:一个Endpoint通过加入一个Sandbox来加入一个Network。Endpoint就相当于一个网卡。

  3. Network:一个Network是一组直接互联的Endpoint组成的。它相当于一个二级网络。

清楚了以上三个基本概念之后我们来看看Network的执行流。

  1. 指定network的驱动和各项相关参数之后调用 libnetwork.New()创建一个NetWorkController实例。这个实例提供了各种接口,Docker可以通过它创建新的NetWork和Sandbox等。

  2. 通过controller.NewNetwork(networkType, “network1″)来创建指定类型和名称的Network。

  3. 通过network.CreateEndpoint(”Endpoint1″)来创建一个Endpoint。在这个函数中Docker为这个Endpoint分配了ip和接口,而对应的network实例中的各项配置信息则会被使用到Endpoint中,其中包括iptables的配置规则和端口信息等。

  4. 通过调用controller.NewSandbox()来创建Sandbox。这个函数主要调用了namespace和cgroup等来创建一个相对独立的沙盒空间。

  5. 调用ep.Join(sbx)将Endpoint加入指定的Sandbox中,则这个Sandbox也会加入创建Endpoint对应的Network中。

总的来说,Endpoint是由Network创建的,隶属于这个创建它的Network,当Endpoint加入Sandbox的时候,就相当于这个Sandbox加入到了这个Network中来。下面的图可以简要说明三者的关系。

剖析 | 全方位解读Docker 1.9新特性_Jav_04

然后是关于Volumes,即持久化存储这一块,Docker也做了比较大的改动。首先最明显的就是–volume不再仅仅作为docker run的一个flag,也作为一个单独的子命令出现在Docker中。底层也为volume添加了诸如ls、create、inspect和rm等volume子命令的api。新的volume子命令可以允许用户先创建volume,然后在启动的container的时候进行挂载,此举也更加方便了volume的管理。

一个简单的例子方便大家快速上手新特性:

$ docker volume create --name hello
hello
$ docker run -d -v hello:/world busybox ls /world

通过这个例子可以创建一个名为hello的volume,然后将其挂载到容器内的/world目录下。

在Docker Engine 1.9中也有一些有趣实用的特性。

前面提到的磁盘 I/O metrics就是一个很实用的特性。这个新特性通过抓取系统内blkio IoServiceBytesRecursive中的信息对磁盘的I/O进行计算,并通过docker state命令打印出来。所以现在通过docker state获得的容器信息就变成了如下的形式:


并行镜像 pull也是一个有趣的新特性。现在可以做到如果用户pull的镜像正在被其他进程 pull,进度条会显示这个信息并正确退出。上一个版本的Docker Engine在处理用户pull的镜像正在被其他进程 pull这个问题中,用的是管道,这样的话就很容易出现管道堵塞,就会卡死在“Layer already being pulled by another client”。

新的版本中使用了名为progressreader的工具包,如果遇到上述问题的话,Docker Engine可以调用这个工具包,通过共享的Broadcaster读取其他进程pull用户所指定镜像的进度,显示这个进度,并做出正确的返回。

最后来讲讲Docker Engine 1.9中的自定义 stop 信号。这个特性可以在Dockerfile 指令中新增 STOPSIGNAL,通过使用这个指令Docker允许用户自定义应用在收到docker stop所收到的信号。这个新特性主要通过重写Docker中signal库内的stopsignal来支持自定义信号的传递,在上层调用时则将用户自定义的信号传入底层函数即可。