Docker的前景被普遍看好。最近恰好在研究Docker。看了孙宏亮大牛的《Docker源码解析》更是很有感触。于是我就在想,可不可以写一个学习体会来做一个阶段性总结。我的想法就是如何从起名字的角度来阐释Docker架构的一些内部原理。曾经有一位大牛导师给我说过这样几句话:
“你给每个类起的名字代表了你对这个实现逻辑的最高理解”。
原话不记得了。但大体意思就是这样,这句话也成了我后来判断一个人代码质量的重要依据。
先上一张Docker的架构图(该图取自孙宏亮《Docker源码分析》)
Docker架构图 (以下图片单击可见高清)
是歪的,借此机会活动下脖子。
Docker的总架构图就是这样。架构中主要有DockerClient、DockerDaemon、Docker Registry、Graph 、 Driver、libcontainer 以及Docker Container。
Docker Client
首先我们来看看DockerClient。这个名字很显然。是一个客户端。这时候你是不是联想到了命令、浏览器等等。不说了。client表示好伤人。。。
Docker Daemon
Docker Daemon。Daemon是守护的意思。读“滴萌”。前面说了client。那么就可能会联想到这应该是一个server。没错这个就是一个server。但docker的大牛们为什么不叫DockerServer呢?是有原因的。因为Daemon中不仅仅有server,还有其他的。还有Engine。这个Engine中有很多的job。DockerDaemon内部所有的任务都是由Engine中的一个个Job来完成。
事实上DockerDaemon就做两件事情:
- 接受并处理Docker Client发送的请求
- 管理所有的docker容器
容器是个什么鬼后面会讲。
Docker Daemon 的架构主要有三部分:Docker Server、Engine 和 Job 。
Docker Server
是走的http协议。你就姑且就把它理解为httpserver。ok?什么handler了。路由表啦都是标配。就是你常用的类似servlet那套。
Engine
一看名字就是很重要是不是。作为一个轻量级容器。自然要做很多的事情。这些事情由谁来做呢。就是由Engine来做。因为Engine手底下管理着众多的Job。那这么多的job是怎么进行管理的呢?在docker源码中,有个叫handlers的对象。你可以认为是一个map的数据结构。举例来说,有其中一项为{“create”,daemon.ContainerCreate},就说明执行一个“create”的job的时候,执行的就是daemon.ContainerCreate这个handler。这个设计思路是不是和前面说的server类似。其实就是一个路由或者叫映射。英文一般叫route或者map之类的。engine做的比较有名的一件事情就是管理我们的容器。
Job
job你可以认为是docker中做事情的最小单元。每个action都是一个job。比如:在docker容器内部运行一个进程要创建一个job;创建一个容器,要创建一个job;在网络上下载一个文档,是一个job;创建一个server服务,这也是一个job。
有关job的接口设计与unix进程非常的像。比如说,job有自己名称、有运行时参数、有环境变量、有个标准输出,有标准错误以及返回状态等。
Job就像java中的runnable一样。runnable有run()。 Job也有自己的运行函数也叫Run()。
Docker Registry
现在来看Docker Registry。
这个就是一个仓库。用来存储image的仓库。既然是仓库。你可能就会想到maven等等。自然有公用仓库,私有仓库。其中大家熟知的Docker Hub,就是是全球最大的共有仓库。用户也可以构建自己的私有仓库。在国内,也有很多的容器厂商为我们构建了公有仓库,比如daocloud、caicloud等等。真是为我们提供了极大的便利啊。
Graph
先上一段英文:
modern word-forming element meaning "instrument for recording; that which writes, marks, or describes; something written," from Greek -graphos "-writing, -writer" (as in autographos "written with one's own hand"), from graphe "writing, the art of writing, a writing," from graphein "to write, express by written characters," earlier "to draw, represent by lines drawn" (see -graphy). Adopted widely (Dutch -graaf, German -graph, French -graphe, Spanish -grafo). Related: -grapher; -graphic; -graphical.
从上面对graph的解释以及词源挖掘。我们发现,graph类似一个画板。你可以在上面写写画画。在graph上有很多个元素。比如有很多点等等。
所以你就能理解docker作者的苦心了。在docker中,graph负责管理所有的image,也就是镜像。这些image,你可以任性的组装叠加来构建出新的镜像。就好像你在画板上画了一层又一层。画了一个鸟,又画了一个鸭。这些鸟和鸭就是一个个image。既然是image,就得存储啊。在docker中,支持多种image的存储方式。比如:aufs、devicemapper、Btrfs等等。
Driver
现在来看看driver。这个太明显了。既然是一个容器。自然会有驱动了。你是不是已经在想什么网络、存储等等了。没错,这个感觉就对了。
docker现在为我们提供了Execution Drivers、graphdriver、networkdriver。
这些驱动都是为容器的运行环境提供服务的。
Execution Drivers
先来看看这个Execution Drivers。在0.9发布以后,Solomon Hykes 兴致勃勃的在docker官网写了一文,在那里介绍了Execution Drivers:
以下是那天他说的一些话:
Fellow Dockers, Today we are happy to introduce Docker 0.9. ............blabla.................... First, we are introducing an execution driver API which can be used to customize the execution environment surrounding each container. This allows Docker to take advantage of the numerous isolation tools available, each with their particular tradeoffs and install base: OpenVZ, systemd-nspawn, libvirt-lxc, libvirt-sandbox, qemu/kvm, BSD Jails, Solaris Zones, and even good old chroot. This is in addition to LXC, which will continue to be available as a driver of its own. There are already several projects underway to develop more drivers. Want to join the fun? Come say hi on #docker-dev on Freenode, and we’ll help you get started.
大体就说自己很开心。说这个driver可以让docker利用多种隔离的工具。这个driver在当时是一个独立的子项目。在0.9之前是通过linux的lxc工具来管理容器的创建。现在我们可以使用这个driver来做这些事情。lxc我们还会继续支持。
与此同时我们还将会开发更多的driver。
你发现没?很多开发都是这样。一开始就用一些现成的。后来就自己开发一套,另起炉灶。好吧,继续。。。
networkdriver
比如接下来要介绍的networkdriver。
这个主要做的事情就是完成容器的网络环境的配置。比如docker daemon启动的时候创建网桥;以及容器创建的时候为容器分配网络接口资源;以及为容器分配ip、端口并和宿主机做nat端口影射;设置容器的防火墙等等。
graphdriver
对了,还有一个graphdriver。前面我们说了graph。说graph是容器镜像的管理者。那是不是需要开发个driver来把这些image存储起来呢?于是docker作者们就开发graphdriver来负责image的存储事宜。你现在不看源码都能想象到这个driver会干些什么事情。比如从本地存储目录get一个image,把一个image存到本地目录等等和image存储以及获取有关的事情。
在graphdriver初始化之前,已经有多种fs或者类fs的driver在docker daemon中严阵以待了。它们分别是aufs、btrfs、vfs、overlayFS和devmapper。其中前面四种都是可以用于image的管理的(后面那种是管理volume用的)。docker在初始化的时候,就会从系统环境变量“DOCKER_DRIVER”中获取指定的driver,之后就一直用这个driver来管理image了。比如配置的是overlayFS。
libcontainer
先来一张图:
上面介绍的那个execution driver把过去用的LXC driver扔到了边上。这个driver就是基于libcontainer之上的。libcontainer是一个纯粹的go语言包。我们的开发可以直接通过libcontainer来访问kernel的容器api,而不用其他任何依赖。
很感谢libcontianer。让我们可以开箱即用。不需要任何依赖。不用依赖什么LXC或者其他的底层的一些包,我们就可以使用namespaces,control groups,capabilities,apparmor profiles, network interfaces 以及 firewalling rules。而且libcontainer夹在中间还起到了解耦的作用。现在libcontainer已经是docker默认选择了。在0.9之后,LXC变为optional。只是一种可选。如果你想回到LXC driver,你只需要restart一下daemon就回到了熟悉而陈旧的过去,通过命令:docker -d -e lxc。虽然libcontainer已经被我们选为皇后。但LXC driver也没有被打入冷宫,在未来的版本中大牛们依然会翻她的牌子。
上面说到了libcontainer的解耦。这也是意味着底层可以不仅仅是linux,也可以是其他的平台。
Docker Container
最后来说说压轴的 Docker Container。container既然是一个包含某些东西的器皿,肯定会承载一些内容,比如image,你可以这样理解。docker最终是以容器的方式呈现给大家的。就像你运行一个tomcat,当然tomcat是另外一个层面的的cotainer。但container们都是大同小异的。这时候你也许会想到vm(虚拟机)。docker 的container长得和虚拟机很像。而且操作起来也和vm一样一样的,包括资源、环境都是和外界隔离的。
container运行了指定的image之后,就具备服务大家的能力了。比如运行个nginx,tomcat、redis等等。什么都可以。
至于container和image的关系。这二者之间的关系很紧密。image是静态的,
contianer是动态的。不多说了。你就记住只要是container都是要start的,要处于运行态的。容器中运行什么,就是image这个模板里的东西了。
总之
总之,
技术变化太快,也许在撸此文的时候,人家又升级了,比如又开发了一个新的 *driver或者 *graph或者lib*。
end了,有不对的地方指出来。
声明:文中涉嫌打广告的内容只是想向每一位努力的人致敬,别无他意。