为什么docker 重新启动容器容器启动不了

关于docker容器和镜像的关系
无论容器里做什么操作,写文件,删文件。该容器的基本镜像都不会有任何改变。这是因为Docker从父镜像建立增量镜像,只存储每个容器的更改。因此,如果你有一个300MB的父镜像,如果你在容器中安装了50MB的额外应用或服务,你的容器只有50MB,父镜像还是300MB。 但是可以使用Dockfile或commit命令来,把增量镜像和父镜像一起生成一个新的镜像。
dock top -- 显示容器中运行的进程
查看容器的root用户名和密码
docker logs &容器名orID& 2&&1 | grep '^User: ' | tail -n1
因为docker容器启动时的root用户的密码是随机分配的。所以,通过这种方式就可以得到redmine容器的root用户的密码了。
实时查看容器日志
docker logs -f &容器名orID&
删除所有容器
docker rm $(docker ps -a -q)
停止、启动、杀死一个容器
docker stop &容器名orID&
docker start &容器名orID&
docker kill &容器名orID&
查看所有镜像
docker images
删除所有镜像
docker rmi $(docker images | grep none | awk '{print $3}' | sort -r)
移除所有的容器和镜像(大扫除)
用一行命令大扫除:
docker kill $(docker ps -q) ; docker rm $(docker ps -a -q) ; docker rmi $(docker images -q -a)
注:shell 中的 $() 和command类似,会先执行这里面的内容,上面的脚本会出现如下 docker kill "pids" ; docker kill 在 docker 中用于停止容器,docker rm 删除容器, docker rmi 删除镜像
当没有运行的容器或者是根本没有容器的时候,这只会提示一个警告信息。当你想尝试的时候,这就是个非常好的单行命令。如果你仅仅想删除所有的容器,你可以运行如下命令:
docker kill $(docker ps -q) ; docker rm $(docker ps -a -q)
不在 Shell 上运行命令
如果你使用需要Shell 的扩展项的 docker run 命令处理某些事情,比如 docker run --rm busybox ls '/var/log/', 这个命令将失败。这个失败的原因我花了工夫才弄明白。这个陷阱在这里:你原来没有 Shell , 而 ``` 是 Shell 的扩展项,因此你需要一个能使用的 Shell 。正确方法为docker run --rm busybox sh -c 'ls /var/log/*'
多程序开机自动运行方法:
docker容器每次启动时,开机自启动的命令都要在启动容器前指定。如 docker run -I -t debian /bin/bash命令,只会运行/bin/bash程序,其它的程序都不会运行,对于要跑多个程序的容器特别纠结。可把前面所说的启动命令换成
dockerrun -I -t debian /etc/rc.local
在容器中把所有需要开机自的启动命令放在/etc/rc.local中,就可以达到多程序开机自启动了。
后台运行则是:
docker run -d -p 50001:22 debian /etc/rc.local
退出时删除容器
如果你仅仅想在一个容器中快速的运行一个命令,然后退出,并且不用担心容器状态,把 --rm 参数加入 run 命令后面,这将结束很多你保存了的容器,并且清理它们。
docker run --rm -i -t busybox /bin/bash
运行一个新容器,同时为它命名、端口映射、文件夹映射。以redmine镜像为例
docker run --name redmine -p 9003:80 -p 9023:22 -d -v /var/redmine/files:/redmine/files -v /var/redmine/mysql:/var/lib/mysql sameersbn/redmine
一个容器连接到另一个容器
docker run -i -t --name sonar -d -link mmysql:db
tpires/sonar-server
容器连接到mmysql容器,并将mmysql容器重命名为db。这样,sonar容器就可以使用db的相关的环境变量了。
镜像的保存与导入
当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。 机器a
docker save busybox-1 & /home/save.tar
使用scp将save.tar拷到机器b上,然后:
docker load & /home/save.tar
构建自己的镜像
docker build -t &镜像名& &Dockerfile路径&
如Dockerfile在当前路径:
docker build -t xx/gitlab .
重新查看container的stdout
# 启动top命令,后台运行
$ ID=$(sudo docker run -d ubuntu /usr/bin/top -b)
# 获取正在running的container的输出
$ sudo docker attach $ID
top - 02:05:52 up
load average: 0.01, 0.02, 0.05Tasks:
1 running,
0 sleeping,
0 stopped,
0.0%ni, 99.7%id,
373572k total,
355560k used,
18012k free,
27872k buffers
786428k total,
786428k free,
221740k cached
$ sudo docker stop $ID
后台运行(-d)、并暴露端口(-p)
docker run -d -p 127.0.0.1:33301:22 centos6-ssh
从container中拷贝文件出来
sudo docker cp 7bb0e258aefe:/etc/debian_version .
拷贝7bb0e258aefe中的/etc/debian_version到当前目录下。 注意:只要7bb0e258aefe没有被删除,文件命名空间就还在,可以放心的把exit状态的container的文件拷贝出来
文章来源:http://89ao.gitcafe.io/docker-tricks/
记住登录状态
重复输入密码匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。966,690 六月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
剖析Docker文件系统:Aufs与Devicemapper
剖析Docker文件系统:Aufs与Devicemapper
日. 估计阅读时间:
智能化运维、Serverless、DevOps......2017年有哪些最新运维技术趋势?!
相关厂商内容
相关赞助商
CNUTCon全球运维技术大会,9月10日-9月11日,上海&光大会展中心大酒店,
Docker容器是建立在Aufs基础上的,Aufs是一种Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念。Aufs将挂载到同一虚拟文件系统下的多个目录分别设置成read-only,read-write以及whiteout-able权限,对read-only目录只能读,而写操作只能实施在read-write目录中。重点在于,写操作是在read-only上的一种增量操作,不影响read-only目录。当挂载目录的时候要严格按照各目录之间的这种增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载,待所有目录挂载结束了,继续挂载一个read-write目录,如此便形成了一种层次结构。
Docker镜像的典型结构如下图。传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write,然后我们就可以在rootfs上进行写和读的操作了。但Docker的镜像却不是这样,它在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。
为了形象化Docker的镜像结构,我们docker pull一个ubuntu:14.04的镜像,使用docker images -tree查看结果如下:
[root@qingze qingze]# docker images -tree
Warning: '-tree' is deprecated, it will be removed soon. See usage.
└─c5a Virtual Size: 0 B
└─3b363fd9d7da Virtual Size: 192.5 MB
└─607c5d1cca71 Virtual Size: 192.7 MB
└─f62feddc05dc Virtual Size: 192.7 MB
└─8eaa4ff06b53 Virtual Size: 192.7 MB Tags: ubuntu:14.04,
可以看到Ubuntu的镜像中有多个长ID的layer,且以一种树状结构继承下来,如下图。其中,第n+1层继承了第n层,并在此基础上有了自己的内容,直观上的表现就是第n+1层占用磁盘空间增大。并且,不同的镜像可能会有相同的父镜像。例如,图中Tomcat和Nginx 继承于同一个Vim 镜像,这种组织方式起到共享的作用,节约了镜像在物理机上占用的空间。
接下来,使用docker save命令将镜像具体化并查看其结构:
[root@qingze qingze]# docker save -o ubuntu.tar ubuntu:14.04
[root@qingze qingze]#
tar -tf ubuntu.tar
3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a/
3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a/VERSION
3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a/json
3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a/layer.tar
c5a64f264b78b5433614aecdd4d/
c5a64f264b78b5433614aecdd4d/VERSION
c5a64f264b78b5433614aecdd4d/json
c5a64f264b78b5433614aecdd4d/layer.tar
607c5d1cca71dd3b6ce5ea9ac7ec2aa/
607c5d1cca71dd3b6ce5ea9ac7ec2aa/VERSION
607c5d1cca71dd3b6ce5ea9ac7ec2aa/json
607c5d1cca71dd3b6ce5ea9ac7ec2aa/layer.tar
8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a/
8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a/VERSION
8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a/json
8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a/layer.tar
f62feddc05dc67da9bd7ae72a32e355ced/
f62feddc05dc67da9bd7ae72a32e355ced/VERSION
f62feddc05dc67da9bd7ae72a32e355ced/json
f62feddc05dc67da9bd7ae72a32e355ced/layer.tar
repositories
我们可以发现在具体化的镜像中对应树型结构中的每一个layer都有一个文件夹存在,且每个文件夹中包含VERSION,json,layer.tar三个文件,另外还有一个repositories文件。长ID为 c5a 的layer没有继承任何layer,因此被称为base镜像,一般来说镜像都是以这个layer开始的, 其内容也为空。
[root@qingze 3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a]# ls
[root@qingze 3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a]# cat json | python -m json.tool
&architecture&: &amd64&,
&checksum&: &tarsum.dev+sha256:ac1a98a5c6dacecb36cfcc44&,
&config&: {
&AttachStderr&: false,
&AttachStdin&: false,
&AttachStdout&: false,
&Cmd&: null,
&CpuShares&: 0,
&Cpuset&: &&,
&Domainname&: &&,
&container&: &8c41fcbc2d072ff33e10d7cb57af3eec3cd3c7fb308a6f24a3e0&,
&container_config&: {
&created&: &T01:25:18.Z&,
&docker_version&: &1.4.1&,
&id&: &3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a&,
&os&: &linux&,
&parent&: &c5a64f264b78b5433614aecdd4d&
由于篇幅关系,这里只截取了json文件的一部分内容,可以看出json文件包含了镜像以及其对应的容器的配置信息,其中有docker run时指定的,如CpuShares,Cpuset,也有系统生成的,如parent,正是这个属性记录了继承结构,从而实现了layer的层次关系。
另外,每个文件夹下都有一个layer.tar的文件,这个压缩文件存放的正是rootfs的内容,不过是增量存放的而已。为了证明这一点我们找到Ubuntu:14.04对应的layer是8eaa4ff06b53,进入该目录,查看layer的内容,未发现任何东西。
[root@qingze 8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a]# tar -tf layer.tar
[root@qingze 8eaa4ff06b53ffa7e21bdee064ca2d5d90d757dc7ea040a]#
然后,我们使用该镜像启动一个容器,创建一个100M的文件并docker commit该容器,具体化为ubuntu:test后重新查看。结果显示,ubuntu:test相比ubuntu:14.04多了一个7e64c2624ce0文件夹,且该文件夹下的layer.tar中有了test文件。读者亦可对比各个长ID中的内容,以加深理解。
[root@qingze qingze]# docker images -tree
Warning: '-tree' is deprecated, it will be removed soon. See usage.
└─c5a Virtual Size: 0 B
└─3b363fd9d7da Virtual Size: 192.5 MB
└─607c5d1cca71 Virtual Size: 192.7 MB
└─f62feddc05dc Virtual Size: 192.7 MB
└─8eaa4ff06b53 Virtual Size: 192.7 MB Tags: ubuntu:14.04
└─7e64c2624ce0 Virtual Size: 297.5 MB Tags: ubuntu:test
[root@qingze qingze]# docker save -o ubuntu-test.tar ubuntu:test
[root@qingze qingze]# mkdir ubuntu-test
[root@qingze qingze]# tar -xf ubuntu-test.tar -C ubuntu-test/
[root@qingze qingze]# cd ubuntu-test/
[root@qingze ubuntu-test]# cd 7e64cb893ecfbec4e47eb3bd80dd30e615e376ac37405a7baa4e4c/
[root@qingze 7e64cb893ecfbec4e47eb3bd80dd30e615e376ac37405a7baa4e4c]# tar -tf layer.tar
root/.bash_history
Aufs与Devicemapper的应用
Aufs是Docker最初采用的文件系统,由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了Devicemapper的支持。目前,除少数版本如Ubuntu,Docker基本运行在Devicemapper基础上。这一节中,我们将着重介绍Aufs和Devicemapper的应用。
Aufs是Another Union File System的缩写,是一种Union FS,支持将多个目录挂载到同一个虚拟目录下。由于上文已有介绍,不再赘述。这里主要介绍一下Aufs的使用。默认采用的是Ubuntu:14.04。
Aufs的使用非常简单,只需简单的mount命令即可:
root@Standard-PC:/tmp# tree
├── aufs
├── dir1
│&& └── file1
└── dir2
└── file2
root@Standard-PC:/tmp# sudo mount -t aufs -o br=/tmp/dir1=ro:/tmp/dir2=rw none /tmp/aufs
mount: warning: /tmp/aufs seems to be mounted read-only.
root@Standard-PC:/tmp#
假设在/tmp目录下存在如上文件结构,file1的内容为:this is dir1,file2的内容为:this is dir2。我们使用mount命令将dir1和dir2挂载到/tmp/aufs目录下,其中:
-o 指定mount传递给文件系统的参数
br 指定需要挂载的文件夹,这里包括dir1和dir2
ro/rw 指定文件的权限只读和可读写
none 这里没有设备,用none表示
经过以上命令,文件夹dir1和dir2被挂载到了/tmp/aufs目录下,且file1只有只读属性:
root@Standard-PC:/tmp/aufs# echo hello & file1
bash: file1: Read-only file system
root@Standard-PC:/tmp/aufs# echo hello & file2
root@Standard-PC:/tmp/dir2# cat file2
写到这里有的读者可能会问,如果dir1和dir2两个文件夹含有相同的文件会发生什么情况,我们来试一下,将dir1中的file1更名为file2, 重新挂载。我们发现/tmp/aufs目录中仅有file2,且内容为dir1中file2的内容。由此可见,在挂载的过程中,mount命令按照命令行中给出的文件夹顺序挂载,若出现有同名文件的情况,则以先挂载的为主,其他的不再挂载。这也说明了的Docker镜像为什么采用增量的方式:完全是利用Aufs的特性达到节约空间的目的。
root@wangqingze-Standard-PC:/tmp/aufs# ls
root@wangqingze-Standard-PC:/tmp/aufs# cat file2
this is dir1
root@wangqingze-Standard-PC:/tmp/aufs#
Devicemapper
在介绍Devicemapper之前,我们首先介绍几个Linux下有关Logical Volume的概念以及他们的特性,以帮助后文的理解,主要包括:Snapshot,Thinly-Provisioned Snapshot。
Snapshot是Lvm提供的一种特性,它可以在不中断服务运行的情况下为the origin(original device)创建一个虚拟快照(Snapshot),它具有以下几个特点:
当the origin内容发生变化时,snapshot对变化的部分做一个拷贝以用来对the origin进行重构。
因为只对变化的部分做拷贝,所以Lvm的Snapshot在读操作频繁而写操作不频繁的情况下占用很少的一部分空间便能完成特定任务。
当Snapshot大小耗尽或者远大于实际需求时,我们可以对其大小进行调节。
当对Snapshot的数据进行写操作的时候,Snapshot实施相应操作,并丢弃从the origin的拷贝,以后的操作以写操作之后Snapshot中的数据为准。
在某些发行版的Linux系统下,可以使用lvconvert的--merge选项将Snapshot合并回the origin。
对Snapshot的应用诸如:对数据进行实时备份,利用其可读写的特性创建Snapshot供测试等等。
Thin-Provisioning是一项利用虚拟化方法减少物理存储部署的技术,可最大限度提升存储空间利用率。下图中展示了某位用户向服务器管理员请求分配10TB的资源的情形。实际情况中这个数值往往是峰值,根据使用情况,分配2TB就已足够。因此,系统管理员准备2TB的物理存储,并给服务器分配10TB的虚拟卷。服务器即可基于仅占虚拟卷容量1/5的现有物理磁盘池开始运行。这样的&始于小&方案能够实现更高效地利用存储容量。
Thin-provisioning Snapshot结合Thin-Provisioning和Snapshot两种技术,允许多个虚拟设备同时挂载到一个数据卷以达到数据共享的目的。Thin-Provisioning Snapshot的特点如下:
可以将不同的snaptshot挂载到同一个the origin上,节省了磁盘空间。
当多个Snapshot挂载到了同一个the origin上,并在the origin上发生写操作时,将会触发COW操作。这样不会降低效率。
Thin-Provisioning Snapshot支持递归操作,即一个Snapshot可以作为另一个Snapshot的the origin,且没有深度限制。
在Snapshot上可以创建一个逻辑卷,这个逻辑卷在实际写操作(COW,Snapshot写操作)发生之前是不占用磁盘空间的。
Thin-Provisioning Snapshot虽然有诸多优点,但是也有很多不足之处,例如大小固定等问题,如若有兴趣可以阅读参考文献,这里不再赘述。
Thin-Provisioning Snapshot是作为device mapper的一个target在内核中实现的。Device mapper 是Linux 2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制。在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,如条带化,镜像,快照等。
Device Mapper主要包含内核空间的映射和用户空间的device mapper库及dmsetup工具。Device Mapper库是对ioctl、用户空间创建删除Device Mapper逻辑设备所需必要操作的封装,dmsetup是一个提供给用户直接可用的创建删除device mapper设备的命令行工具。
我们以dmsetup命令来介绍一下Thin-Provisioning Snapshot时如何实现的。Thin-Provisioning Snapshot需要一个data设备和一个metadata设备分别用来存放实际数据和元数据。有两种方式可以更改metadta:一种时通过函数调用,另一种则是通过dmsetup message命令。这里,我们创建两个稀疏文件作为data和metadata设备:
[root@qingze dev]# dd if=/dev/zero of=/tmp/metadata bs=1K count=1 seek=2G
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0. s, 6.7 MB/s
[root@qingze dev]# dd if=/dev/zero of=/tmp/data bs=1K count=1 seek=100G
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 8.7567e-05 s, 11.7 MB/s
以上命令分别创建了2G的metadata和100G的data两个稀疏文件。注意命令中seek选项,其表示为略过of选项指定的输出文件的前2G空间再写入内容。这2G在硬盘上是没有占有空间的,占有空间只有1k的内容。当向其写入内容时,才会在硬盘上为其分配空间。
文件创建完成后,我们将其挂载到loopback设备上,以供使用:
[root@qingze dev]# losetup /dev/loop0 /tmp/metadata
[root@qingze dev]# losetup /dev/loop1 /tmp/data
[root@qingze dev]# losetup -a
/dev/loop0: [ (/tmp/metadata)
/dev/loop1: [ (/tmp/data)
准备工作就绪后,我们开始正式创建Snapshot。首先创建一个thin-pool,指定要用哪些设备来创建Snapshot,此命令的作用为:将逻辑设备的0~之间的sector映射到/dev/loop1上,元数据信息存储到/dev/loop1:
[root@qingze dev]# dmsetup create pool --table &0
/dev/loop0 /dev/loop1 128 32768 1 skip_block_zeroing&
pool指定thin-pool的名字
--table指定dmsetup的一个target,其中:
0表示起始sector
表示sector的数量
thin-pool表示target的名字
/dev/loop0 /dev/loop1为设备名
128表示一次可分配的最小sector数
32768表示空闲空间的阈值
1特征参数的个数
skip_block_zeroing特征参数,表示略过用0填充的块
在继续创建Snapshot前,需要创建一个Thinly-Provisioned volume并格式化:
[root@qingze dev]# dmsetup message /dev/mapper/pool 0 &create_thin 0&
[root@qingze dev]# dmsetup create thin --table &0 2097152 thin /dev/mapper/pool 0&
[root@qingze dev]# mkfs.ext4 -E discard,lazy_itable_init=0 /dev/mapper/thin
命令的格式为:
message device_name sector message
Send message to target. If sector not needed use 0
'0' is an identifier for the volume
官方文档中对Snapshot有internal和external之分,说明如下:
internal snapshot:
Once created, the user doesn't have to worry about any connection
between the origin and the snapshot. Indeed the snapshot is no
different from any other thinly-provisioned device and can be
snapshotted itself via the same method. It's perfectly legal to
have only one of them active, and there's no ordering requirement on
activating or removing them both. (This differs from conventional
device-mapper snapshots.)
external snapshot:
You can use an external read only device as an origin for a
thinly-provisioned volume. Any read to an unprovisioned area of the
thin device will be passed through to the origin. Writes trigger
the allocation of new blocks as usual.
读者可以根据自己的需求选择,这里我们只建立一个internal snapshot:
[root@qingze dev]# dmsetup suspend /dev/mapper/thin
[root@qingze dev]# dmsetup message /dev/mapper/pool 0 &create_snap 1 0&
[root@qingze dev]# dmsetup resume /dev/mapper/thin
[root@qingze dev]# dmsetup create snap --table &0 2097152 thin /dev/mapper/pool 1&
[root@qingze dev]# dmsetup status
thin: 0 2097152 thin 1
fedora-swap: 0 8126464 linear
fedora-root: 0
snap: 0 2097152 thin 1
thin-pool 0 280//163840 - rw discard_passdown queue_if_no_space
fedora-home: 0
至此,我们已经成功建立了一个Snapshot,接下来可以将其挂载到任何一个文件夹下,并对其进行创建文件等操作:
[root@qingze dev] mount /dev/mapper/snap dv
我们在dv文件夹下创建一个内容为this is snap的文件file,然后,在snap的基础上再创建一个snap1,观察发生了什么:
[root@qingze tmp]# dmsetup message /dev/mapper/pool 0 &create_snap 2 1&
[root@qingze tmp]# dmsetup create snap1 --table &0 2097152 thin /dev/mapper/pool 2&
[root@qingze tmp]# mkdir dv1
[root@qingze tmp]# mount /dev/mapper/snap1 dv1
[root@qingze dv]# cd ..
[root@qingze tmp]# cd dv1
[root@qingze dv1]# ls
lost+found
[root@qingze dv1]# cat file
this is snap
可以发现包含同样的文件和内容。事实上,Docker正是以这种方式来创建容器的,读者可以执行以下命令来验证:
#在`metadata`文件夹下查看某个某个容器的`device_id`
[root@qingze metadata]# cat 3b363fd9d7dab4dbf43e806f6fa6f7edf4b84eadb0685a
{&device_id&:910,&size&:,&transaction_id&:1817,&initialized&:false}
#创建并挂载snapshot
[root@qingze tmp]# dmsetup message /dev/mapper/docker-253:1-3020991-pool 0 &create_snap &
[root@qingze tmp]# dmsetup create snap6 --table &0
thin /dev/mapper/docker-253:1-3020991-pool 6001&
[root@qingze tmp]# mkdir dv6
[root@qingze tmp]# mount /dev/mapper/snap6 dv6
[root@qingze tmp]# cd dv6/
[root@qingze dv6]# ls
lost+found
[root@qingze dv6]# cd rootfs/
[root@qingze rootfs]# ls
在继续介绍之前,我们首先介绍一下Docker的系统架构。Docker采用一种c/s的结构,如下图所示,由Client和Daemon两部分组成。Docker Daemon主要由三部分:Server,Engine和Job。Docker Daemon启动时首先进行一些初始化操作,如初始化network,创建grapdriver,创建execdriver等等。在初始化结束后,Docker Daemon启动Server负责监听Docker Client发来的请求,Server的运行以及其对Docker Client端请求的响应都是以Job的形式在Engine中完成的。Engine是Docker架构中的运行引擎,同时也是Docker的核心模块。一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个Job。Docker Client负责对命令行的解析,其运用反射机制将命令行转换成函数的形式通过某种协议与Docker Daemon进行通信,通过以RPC的方式调用Server相应功能。目前,Docker支持三种调用方式:fd,unix socket,http。值得注意的是,Docker Daemon向Docker Client提供的所有功能都是以Handler的方式注册到Engine中的。举例说明,Engine的Handler对象中有一项为:{&create&:daemon.ContainerCreate},则说明当名为&create&的Job在运行时,执行的是daemon.ContainerCreate的Handler。这样做的好处是增加了系统的可扩展性,添加新功能时只需简单的将其注册到Engine中即可。
在Docker中为了实现对多种不同文件系统的支持,增加系统的可扩展性,Docker采用多态的方式将文件系统的操作抽象为ProtoDriver接口:
type ProtoDriver interface {
// String returns a string representation of this driver.
String() string
// Create creates a new, empty, filesystem layer with the
// specified id and parent. Parent may be &&.
Create(id, parent string) error
// Remove attempts to remove the filesystem layer with this id.
Remove(id string) error
// Get returns the mountpoint for the layered filesystem referred
// to by this id. You can optionally specify a mountLabel or &&.
// Returns the absolute path to the mounted layered filesystem.
Get(id, mountLabel string) (dir string, err error)
// Put releases the system resources for the specified id,
// e.g, unmounting layered filesystem.
Put(id string)
// Exists returns whether a filesystem layer with the specified
// ID exists on this driver.
Exists(id string) bool
// Status returns a set of key-value pairs which give low
// level diagnostic status about this driver.
Status() [][2]string
// Cleanup performs necessary tasks to release resources
// held by the driver, e.g., unmounting all layered filesystems
// known to this driver.
Cleanup() error
ProtoDriver包含了各文件系统所需的基本操作,如Crate,Remove等,不同的文件系统只需简单的实现各接口即可。Go语言在语法上与其他语言有些不同,如果不理解具体实现,请参阅相关文档。Docker中文件系统的继承结构如下图(仅Aufs和Devivcemapper画出):
Docker文件系统功能分为两部分:文件系统初始化和创建容器。我们首先介绍初始化部分Aufs和Devicemapper实现的功能,然后再介绍如何创建容器。
文件系统初始化过程是在Docker Daemon启动时进行的。 在Docker Daemon启动时会调用函数,继而调用完成相关环境的设置与初始化。其中,以下为初始化文件系统的操作:
// Load storage driver
driver, err := graphdriver.New(config.Root, config.GraphOptions)
if err != nil {
return nil, err
log.Debugf(&Using graph driver %s&, driver)
graphdriver.New函数会调用daemon/graphdriver/driver.go中的GetDriver函数:
func GetDriver(name, home string, options []string) (Driver, error) {
if initFunc, exists := drivers[name]; exists {
return initFunc(path.Join(home, name), options)
return nil, ErrNotSupported
GetDriver函数中通过drivers[name]返回物理机所支持的文件系统的初始化函数,进行初始化操作。整个流程如下图:
Aufs在初始化函数中主要完成了以下几个操作:
调用函数加载Aufs模块。
调用在系统中为/var/lib/docker/aufs创建一个挂载点。这里的实现原理与mount --bind命令一样,只不过mount命令的源文件夹和目的文件夹一样,在系统中只创建了挂载点而已。并且这个挂载点的内容即不受源文件夹的影响也不影响源文件夹。
最后,在/var/lib/docker/aufs创建mnt, diff, layers文件夹。mnt文件夹为容器的挂载点目录,每一个容器在mnt下都有一个长ID目录,对应为该容器的rootfs的挂载点。diff有着与mnt中对应的长ID目录,这里的每个目录对应Docker 镜像的一个layer层,里面存放的是该layer相比较于父layer变化的内容。注意: 这里才是存放我们在容器中看到的内容的地方,比如/usr, /bin等等。
相比之下,Devicemapper的初始化操作相对麻烦一些。Devicemapper在使用的过程中需要需要一个24-bit的标识来唯一标记一个Snapsot,同时为了保证向metadata中数据的一致性,Dokcer定义了和结构,通过对属性DeviceId和TransactionId加锁来实现一致性。DeviceSet和DevInfo是一对多的关系,每一个Snapshot都会对应一个DevInfo在DeviceSet中。
在初始化的过程中,Init首先调用函数构建一个DeviceSet对象,配置相关选项,接着调用函数完成Devicemapper的初始化操作,大致分为四个步骤:
创建一个thin pool。为了创建thin pool,需要两个块设备。默认情况下,Docker启动时为我们在/var/lib/docker/devicemapper/devicemapper下创建了100G的metadata和2G的data两个文件,并挂载到loopback设备/dev/loop0和/dev/loop1,用loop0,loop1创建thin pool。由于它们是稀疏文件,所以直到被使用,否则它们是不占用空间的。
在中调用创建一个external snapshot用作Snapshot的the origin。
在中调用激活步骤2创建的的设备。
在中调用对步骤2创建的的设备格式化。
在我们使用docker run创建一个容器时,如下图,这个过程分为两个步骤:1.创建容器,2.启动容器。以下两个小节中,我们将以创建ID为4ff06b5335f4为例说明创建容器的过程。
Aufs在创建容器的过程中调用函数创建容器运行所需的rootfs,具体步骤如下:
在调用函数mnt和diff文件夹下创建4ff06b5335f4-init文件夹。
在调用函数将容器4ff06b5335f4所依赖的layer按照继承顺序挂载到mnt/4ff06b5335f4-init文件夹下。如下图所示,假设继承顺序为c5a&-3b363fd9d7da&-607c5d1cca7&-f62feddc05dc&-8eaa4ff06b53&-4ff06b5335f4,则按照这个顺序连同diff/4ff06b5335f4-init文件夹一起挂载。强调一点是,这里除diff/4ff06b5335f4-init文件夹外,所有权限都是read-only,这样我们向容器进行写操作时,所有的内容都会出现在diff/4ff06b5335f4-init文件夹。
在再次调用函数在mnt和diff文件夹下创建4ff06b5335f4文件夹。
当容器创建完成后,进入容器启动阶段:
在调用函数将容器4ff06b5335f4所依赖的layer按照继承顺序挂载到mnt/4ff06b5335f4文件夹下,操作如同第一次挂载。
Devmapper在创建容器的过程中同样调用函数创建容器运行所需的rootfs,具体步骤如下:
在调用函数,其调用-&-&创建一个Snapshot 4ff06b5335f4-init,下图中以Snap-init-3表示。其继承了Snap-1,Snap-2的所有内容,并且只保存相对于Snap-2,Snap-3变化的内容。这里为了简单未画出所有的snapshot,读者可以认为已经存在了容器4ff06b5335f4所需的所有layer,而Snap-2是容器4ff06b5335f4的父layer。
在调用函数, 其调用-&完成设备的激活,最后调用将Snapshot 4ff06b5335f4-init挂载到mnt/4ff06b5335f4-init文件夹下。
在调用函数创建Snapshot 4ff06b5335f4,即图中的Snap-3。
当容器创建完成后,进入启动阶段:
在调用函数,其调用-&完成设备的激活,最后调用将Snapshot 4ff06b5335f4挂载到mnt/4ff06b5335f4文件夹下。
Aufs实现起来比较简单,但是由于其迟迟不能加入linux内核,导致兼容性差。目前,仅有Ubuntu支持。Devicemapper虽然实现起来复杂,但兼容性好。其存在的一点不足是,当metadata和data空间被耗尽时,需要重启Docker来扩充空间。
,软件开发工程师。东北大学硕士,研究方向为基于Hadoop的多样性标签推荐。2014年毕业后就职于京东,参与并完成了基于Docker的Falcon自动化运维项目,因而对Docker产生了浓厚兴趣。邮箱:
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博(,),微信(微信号:)关注我们,并与我们的编辑和其他读者朋友交流。
Author Contacted
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
Re: device and resource busy
对 dd seek=2G 的解释不对吧
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
<及所有内容,版权所有 &#169;
C4Media Inc.
服务器由 提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司
京ICP备号-7
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。}

我要回帖

更多关于 docker 启动容器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信