标签:
镜像无疑是docker中的一个重要角色。在分析源码之前,我们先要了解下image的一些概念。可以参考 http://www.sel.zju.edu.cn/?p=549
docker中,为了复用image,将image做成,可继承的方式。子image可以继承父image的rootfs,通过分层的方式一层一层的进行继承复用,减少镜像的大小,提高存储复用。
这里有几个概念
1、rootfs,是容器启动的时候,其所可以用到的是文件系统目录。
2、image,是一个只读的文件系统。通过继承父image的文件系统,然后union mount到一起,形成一个rootfs。
3、layer,image的各个分层就属于layer,由于所有的image中的文件系统都是只读的。所以另外再在最顶层的image上面再添加一个read-write层。整个构成容器的运行环境。
在源代码中是怎么实现这些的呢。通过graphdriver与作为中间模块,实现与底层的layer的文件系统打交道。而graph模块通过graphdriver来对image进行管理。下面我们来看源码。
源码在docker\docker\daemon.go
在mainDaemon中初始化
d, err := daemon.NewDaemon(daemonCfg, eng)
我们继续跟进
源码在docker\daemon\daemon.go
在NewDaemonFromDirectory函数中
初始化了graphdriver,初始化流程到这里。下面我们具体的去看下graphdriver源码。
driver一共有两部分的接口
protodriver,是比较基础部分的,直接与文件存储打交道。从上面的函数名我们就能看出来提供的作用。如
Create:用于创建一个新的layer。
Remove:删除一个layer。
就不一一解释了,其英文的注释已经说明了
driver接口其实是继承了protodriver接口,另外还提供了image与父image直接的一些操作,如diff用于获取image与父image之间的差别,等就不一一介绍。
driver通过插件形式来设计的。(这个也是golang设计中,经常用到的模式)
首先在init中初始化了一个map,用于保存插件的入口。然后提供了Register函数,用于插件的注册
另外提供了一个获取插件的函数。
那我们看看是如何New的
通过环境变量DOCKER_DRIVER,driver的类型,然后查找插件列表,然后通过GetDriver获取Driver。
那我们看看docker中都提供了哪些类型的driver
有aufs,btrfs,devmapper,overlay,vfs一共五种方式的driver。
这里我们就以一个最简单的模块来做演示vfs
代码在docker\daemon\graphdriver\vfs\driver.go
首先是注册
这里通过init注册了vfs方式的driver,初始函数为Init
我们来看看这个driver的具体情况(这个比较简单比较适合于演示)
以上不做解释
这里是Create,这里其实就是一个拷贝动作。
另外vfs并没有实现Driver接口,其只实现了protoDriver接口。是通过再封装了一层来实现的Driver接口的
docker\daemon\graphdriver\fsdiff.go
这里就不再详细介绍了。
graphdriver通过插件的方式,提供了多种的底层drvier。
这里的image指的是源码里面的image
代码在docker\image\image.go
先看结构体
很明显这是一个json结构体
我们看看LoadImage
通过读取json文件,然后解析获取image结构体信息。
下面给一个示例
{
"Size": 0,
"os": "linux",
"id": "fef924a0204a00b3ec67318e2ed337b189c99ea19e2bf10ed30a13b87c5e17ab",
"parent": "9a163e0b8d138ec700b5a5f7e62509012f7eb34b9f86cd3bbeb3d183958114a9",
"created": "2016-02-16T22:59:37.407805421Z",
"container": "d23509cd0189de02bef382544ebfab515f29094f3c0e2f161fa7ce09afa8974e",
"container_config": {
"Labels": {},
"OnBuild": null,
"MacAddress": "",
"NetworkDisabled": false,
"Entrypoint": null,
"WorkingDir": "",
"PublishService": "",
"ExposedPorts": null,
"AttachStderr": false,
"AttachStdout": false,
"AttachStdin": false,
"User": "",
"Domainname": "",
"Hostname": "13709f13afe1",
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"/bin/sh",
"-c",
"#(nop) CMD [\"sh\"]"
],
"Image": "9a163e0b8d138ec700b5a5f7e62509012f7eb34b9f86cd3bbeb3d183958114a9",
"Volumes": null,
"VolumeDriver": ""
},
"docker_version": "1.9.1",
"config": {
"Labels": {},
"OnBuild": null,
"MacAddress": "",
"NetworkDisabled": false,
"Entrypoint": null,
"WorkingDir": "",
"PublishService": "",
"ExposedPorts": null,
"AttachStderr": false,
"AttachStdout": false,
"AttachStdin": false,
"User": "",
"Domainname": "",
"Hostname": "13709f13afe1",
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"sh"
],
"Image": "9a163e0b8d138ec700b5a5f7e62509012f7eb34b9f86cd3bbeb3d183958114a9",
"Volumes": null,
"VolumeDriver": ""
},
"architecture": "amd64"
}
以上这个是我在网上下载的一个image
下面是StoreImage
将image的信息,通过json编码保存到文件。
另外还提供了一些操作,就不一一做解读。
源码在docker\daemon\daemon.go
在初始化graphdriver之后,便初始化了graph
下面我们看看graph
代码在docker\graph\graph.go
主要保存了一个driver
graph目录中保存了image信息
我们看看
restore的时候,会扫描所有的目录,并将imageid给记录下来
那我们挑选几个提供的接口看看
get通过name来获取image id,然后通过imageid,来loadImage,从而获取image的信息
以上是整个create的过程,先构建image结构体,然后调用Register。
先查找是否存在,存在则直接返回。
如果不存在则将其他多余无用的重复信息删除,然后最后调用了driver进行Create。
graph还提供其他的很多操作,不再做分析了。
龚浩华
QQ 月牙寂 29185807
2016年5月4日
(版权声明:本文为作者原创,如需转载请通知本人,并标明出处和作者。擅自转载的,保留追究其侵权的权利。)
标签:
原文地址:http://blog.csdn.net/screscent/article/details/51314497