Linux:Docker 1.7.0 深度解析
July 13, 2015
早在Docker 1.6.0之际,Docker官方的工程师即宣称:1.7.0版本将会带来很大的变化,包括:Docker的bug修改以及功能添加;并且还体现在Docker的架构上,如网络模块等。
话不多说,赶紧让我们进入Docker 1.7.0的深度解析。从Docker的版本变更日志来看,Docker 1.7.0在四个方面会有或多或少的变动,分别是:Docker运行时(Runtime),Docker的代码变化,Docker的builder模块,以及Docker的bug修复。
本文主要涉及Docker 1.7.0的runtime。
Table of Contents
1. 增添了一个仍然处于试验阶段的特性:支持out of process的数据卷插件。
何为试验性质的特性,换言之Docker的这部分特性还不支持在生产环境中采用,这些特性更多的希望用户仅仅在测试环境,以及沙箱环境中采用。试验性特性完全是Docker 1.7.0的一大亮点。
在以上的基础上理解out-of-process,就容易很多,插件本身与Docker Daemon无耦合,即插即用,在Docker Daemon范畴之外发挥作用。
目前Docker的试验性特性可以从两个方面来描述,首先Docker目前已经支持用户自定义第三方插件的使用;另外在这基础上,Docker自身支持了容器数据卷volume插件。此外,Docker还定义了一整套与插件相关的API,方便用户使用。当然,相信后续在该领域,不论是Docker官方,还是整个社区,都会不断有新的插件诞生。值得一提的,在数据卷volume插件方面,出现了Flocker的身影,这也意味着容器的数据存储问题,真正被提上台面,并由相应合理的解决方案。
2.从docker daemon的角度,添加了userland-proxy的起停开关。
首先介绍userland-proxy一直以来的作用。众所周知,在Docker的桥接bridge网络模式下,Docker容器时是通过宿主机上的NAT模式,建立与宿主机之外世界的通信。然而在宿主机上,一般情况下,进程可以通过三种方式访问容器,分别为:
当时引入userland-proxy时,也许是因为设计者意识到了0.0.0.0地址对容器访问上的功能缺陷。然而,在docker-proxy加入Docker之后相当长的一段时间内。Docker爱好者普遍感受到,很多场景下,docker-proxy并非必需,甚至会带来一些其他的弊端。
影响较大的场景主要有两种:
第一,单个容器需要和宿主机有多个端口的映射。此场景下,若容器需要映射1000个端口甚至更多,那么宿主机上就会创建1000个甚至更多的docker-proxy进程。据不完全测试,每一个docker-proxy占用的内存是4-10MB不等。如此一来,直接消耗至少4-10GB内存,以及至少1000个进程,无论是从系统内存,还是从系统CPU资源来分析,这都会是很大的负担。
第二,众多容器同时存在于宿主机的情况,单个容器映射端口极少。这种场景下,关于宿主机资源的消耗并没有如第一种场景下那样暴力,而且一种较为慢性的方式侵噬资源。
如今,Docker Daemon引入- -userland-proxy这个flag,将以上场景的控制权完全交给了用户,由用户决定是否开启,也为用户的场景的proxy代理提供了灵活性。
3. docker exec命令增加- -user参数,用户控制docker exec在容器中执行命令时所处的用户。
自从docker 1.3.0引入docker exec之后,用户对容器的操纵能力被大大释放,容器对用户而言不再是一个运行的黑盒。然而,docker exec带来巨大好处的同时,我们也能看到这其中的一些瑕疵,当然Docker社区也在不断地完善docker exec。
首先,docker exec在容器中运行的进程会以root权限运行,在权限方面缺乏灵活性的同时,容器的安全很有可能失控。参数- -user恰好弥补了这方面的不足。其次,docker exec的存在打破了容器内进程呈现树状关系的现状,而设计初期Docker容器的很多概念均以树的思想从init process入手,因此目前docker exec的进程并不能和原生态容器进程完全一样地被Docker Daemon管理。
4. 增强Docker容器网关地址的配置广度。
Docker 1.7.0发布之前,在bridge桥接模式下,Docker容器的网关地址是默认生成的,一般为Docker环境中的docker0网桥地址。从容器通信的角度而言,默认的方式已经可以满足需要。但是,我们依然可以发现,这种模式存在一些弊端,比如网络配置的灵活性以及网络安全性。
Docker容器的网络一直广受关注,缺乏可配置的特性,在如今的软件发展中,几乎就意味着封闭。 –default-gateway 以及–default-gateway-v6 这两个参数,很大程度上提高了用户自定义容器网络的灵活性,用户更多场景的覆盖,似乎从Docker的发展中若影若现。结合最近几次新版本,功能的增强与丰富,不难猜测,Docker的企业化以及生产化,已经更上一层楼。
默认网关的设置,为什么说会和容器的网络安全相关呢?过去很长一段时间内,docker0作为容器的网关地址,这种方式将容器与宿主机的耦合关系体现的很彻底。docker0作为宿主机上的网络接口,充当容器与宿主机的桥梁。然而,也正是桥梁的存在,使得容器内部进程很容易穿过网关,到达宿主机,此过程并非对用户透明。
5. 容器CFS quota的支持
完善Docker对内核cgoups的支持,指的是对于一个组内的进程组在一个周期内被内核CFS调度算法调度的时间限额,单位为微秒。该配置项在cgroups中相应的文件为/sys/fs/cgroup/cpu/cpu.cfs_quota_us。
6. 容器磁盘IO限制的支持
众所周知,容器将会为用户提供一个隔离的运行环境,容器内部的进程或者进程组使用资源时将受到限制,这样的资源,包括:内存资源(物理内存以及swap),CPU资源(CPU时间片以及CPU核等),磁盘空间资源等,以上这部分内容或多或少,Docker的新版本之前或多或少都可以实现,然而隔离维度依旧不够完美,这次Docker添加了—blkio-weight参数,实现对容器磁盘IO限制的支持。隔离更加完备,用户也不再需要担心容器间磁盘IO资源的竞争。
7. ZFS支持
Docker 1.7.0 正式宣布支持ZFS文件系统。此举也意味着Docker容器文件系统的支持从原先的5种增加到6种。此前,Docker支持aufs,devmapper,btrfs,ovelayfs,vfs(用于支持volume),如今添加对ZFS的支持。ZFS的支持,不禁让人联想到与Docker的数据卷volume插件的Flocker。错进错出,似乎关系较为微妙。
值得一提的是,除了支持ZFS之外,笔者发现在负责容器文件系统的graph模块中,添加了driver_windows.go,虽然内容极其简易,并非完全实现对windows的全盘支持,但是至少让大家看到Docker支持windows的步伐在不断迈进。
8. docker logs的功能扩展
查看容器日志,相信很多Docker爱好者都体验过,这也是用户查看容器运行状态的重要依据。
可以简单了解Docker容器日志的原理:对于每一个创建的Docker容器,Docker Daemon均会在内部创建一个goroutine来监听容器内部进程的标准输出stdout以及标准错误stderr,并将内容传递至日志文件中。每当用户发通过Docker Client发起查看容器日志的请求docker logs之后,Docker Daemon会将日志文件的内容传递至Docker Client显示。
docker logs的发展,几乎可以分为4个阶段:Docker诞生初期的原生态日志打印;允许用户follow容器的日志;开启容器容器的tail功能,以及容器日志的since功能,打印从某一个时间戳开始之后的容器日志。
虽然容器日志的功能在逐渐增强,但是不可否认的是,容器日志是容器本身与Docker Daemon耦合最大的模块之一,而这涉及Docker设计之初的计划,绝非完美,但的确是短时间内最易用的方案。
9. 容器与宿主机共享UTS命名空间的支持
不同的场景下,容器与宿主机可以完全隔离,容器也可能与宿主机存在共享信息的情况,Docker网络的host模式就是一个很好的例子,该模式下的容器共享宿主机的网络命名空间。
共享UTS命名空间的支持,意味着容器与宿主机的关系越来越微妙。也许目前很多Docker爱好者已经习惯容器与宿主机完全隔离的运行,当然也会有一些用户曾经抱怨完全隔离的运行环境并不能平滑的将传统遗留业务容器化。那么,目前Docker在兼顾两者的情况下,更多地在满足后者的需求,不久的将来,Docker容器的运用场景必将更加丰富,这也是Docker走向企业化以及生产化必须要趟的路。
总体而言,Docker 1.7.0给笔者的感受是:功能上逐渐向企业需求靠拢,在production-ready的路上不断优化,另外在安全方面在不涉及内核基础上也不断完善。
0 Comments