怎么把docker容器和镜像区别 容器中的文件方法文件镜像中

深入分析Docker镜像原理
发表于 13:50|
作者孙宏亮
摘要:8月20日晚上8点30分,CSDN Container微信群邀请到DaoCloud软件工程师孙宏亮,他带来了Docker镜像原理的深度分享,分享内容包含两个部分:1.Docker镜像的基本知识;2.Dockerfile、Docker镜像与Docker容器的关系。
分享简介:Dockerfile重塑新镜像,定义的不仅仅是镜像中的磁盘文件;Docker镜像是Dockerfile的产
物,自底之上打包软件及其环境,是软件的交付品;容器是镜像的运行态体现,一切信息来源于镜像。本次分享将深入分析Dockerfile、Docker镜
像和Docker容器三者之间的具体关系。分享嘉宾:孙宏亮,硕士,浙江大学毕业,现为DaoCloud软件工程师。主要负责企业级容器云平台的研发工作,《Docker源码分析》作者。
以下为分享全部内容:第一部分:Docker镜像的基本知识1.1 什么是Docker镜像从整体的角度来讲,一个完整的Docker镜像可以支撑一个Docker容器的运行,在
Docker容器运行过程中主要提供文件系统视角。例如一个ubuntu:14.04的镜像,提供了一个基本的ubuntu:14.04的发行版,当然此
镜像是不包含操作系统Linux内核的。
说到此,可能就需要注意一下,linux内核和ubuntu:14.04Docker镜像的区别了。传统虚拟机安装ubuntu:14.04会包含两部分,第一,某一个Linux内核的发行版本,比如Linux
3.8版本的内核;第二,第一个特定的Ubuntu发行版,这部分内容不包含Linux内核,但是包含Linux之外的软件管理方式,软件驱动,如
apt-get软件管理包等。
理解以上内容之后,就可以理解,为什么在一个Linux内核版本为3.8的ubuntu:14.04基础上,可以把Linux内核版本升级到3.18,而ubuntu的版本依然是14.04。最主要的就是:Linux内核版本与ubuntu操作系统发行版之间的区别。
Linux内核+ubuntu操作系统发行版,组成一台工作的机器让用户体验。那么灵活替换ubuntu操作系统发行版,那是不是也可以实现呢。那么Docker很方便的利用了这一点,技术手段就是Docker镜像。
Docker的架构中,Docker镜像就是类似于“ubuntu操作系统发行版”,可
以在任何满足要求的Linux内核之上运行。简单一点有“Debian操作系统发行版”Docker镜像、“Ubuntu操作系统发行版”Docker镜
像;如果在Debian镜像中安装MySQL 5.6,那我们可以将其命名为Mysql:5.6镜像;如果在Debian镜像中安装有Golang
1.3,那我们可以将其命名为golang:1.3镜像;以此类推,大家可以根据自己安装的软件,得到任何自己想要的镜像。
那么镜像最后的作用是什么呢?很好理解,回到Linux内核上来运行,通过镜像来运行时我们常常将提供的环境称为容器。
以上内容是从宏观的角度看看Docker镜像是什么,我们再从微观的角度进一步深入
Docker镜像。刚才提到了“Debian镜像中安装MySQL
5.6,就成了mysql:5.6镜像”,其实在此时Docker镜像的层级概念就体现出来了。底层一个Debian操作系统镜像,上面叠加一个
mysql层,就完成了一个mysql镜像的构建。层级概念就不难理解,此时我们一般debian操作系统镜像称为mysql镜像层的父镜像。
层级管理的方式大大便捷了Docker镜像的分发与存储。说到分发,大家自然会联想到
Docker镜像的灵活性,传输的便捷性,以及高超的移植性。Docker
Hub,作为全球的镜像仓库,作为Docker生态中的数据仓库,将全世界的Docker数据汇聚在一起,是Docker生态的命脉。
Docker有两方面的技术非常重要,第一是Linux
容器方面的技术,第二是Docker镜像的技术。从技术本身来讲,两者的可复制性很强,不存在绝对的技术难点,然而Docker
Hub由于存在大量的数据的原因,导致Docker Hub的可复制性几乎不存在,这需要一个生态的营造。
1.2 Docker镜像的内容大致介绍了Docker镜像是什么,我们来看看Docker镜像中有哪些内容?
介绍之前,我先分享一下,我个人在接触Docker的两年时间中,对Docker镜像内容认识的变化。
第一阶段:初步接触Docker。相信很多爱好者都会和我一样,有这样一个认识:Docker 镜像代表一个容器的文件系统内容;
第二阶段:初步接触联合文件系统。联合文件系统的概念,让我意识到镜像层级管理的技术,每一层镜像都是容器文件系统内容的一部分。
第三阶段:研究镜像与容器的关系:容器是一个动态的环境,每一层镜像中的文件属于静态内
容,然而 Dockerfile 中的 ENV、VOLUME、CMD
等内容最终都需要落实到容器的运行环境中,而这些内容均不可能直接坐落到每一层镜像所包含的文件系统内容中,那此时每一个Docker镜像还会包含
json文件记录与容器之间的关系。
因此,Docker镜像的内容主要包含两个部分:第一,镜像层文件内容;第二,镜像json文件。
1.3 Docker镜像存储位置既然是说镜像存储的位置,那么应该包含:镜像层文件和镜像json文件。如一个ubuntu:14.04镜像,包含4个镜像层,在aufs存储驱动的情况下,在磁盘上的情况可以如以下图所示:
1.3.1 查看镜像层组成:我们可以通过命令 docker history ubuntu:14.04 查看 ubuntu:14.04,结果如下: 1.3.2 镜像层文件内容存储Docker 镜像层的内容一般在 Docker 根目录的 aufs 路径下,为 /var/lib/docker/aufs/diff/,具体情况如下:&图中显示了镜像 ubuntu:14.04 的 4 个镜像层内容,以及每个镜像层内的一级目录情况。需要额外注意的是:镜像层 d2a0ecffe6fa 中没有任何内容,也就是所谓的空镜像。
1.3.3 镜像 json 文件存储对于每一个镜像层,Docker 都会保存一份相应的 json 文件,json 文件的存储路径为 /var/lib/docker/graph,ubuntu:14.04 所有镜像层的 json 文件存储路径展示如下:
除了 json 文件,大家还看到每一个镜像层还包含一个 layersize
文件,该文件主要记录镜像层内部文件内容的总大小。既然谈到了镜像 json 文件,为了给下文铺垫,以下贴出 ubuntu:14.04 中空镜像层
d2a0ecffe6fa 的 json 文件:
Docker镜像存储,就和大家一起先看到这。同时介绍Docker镜像的基本知识也告一段落。以下我们进入此次分享的第二部分。
第二部分 Dockerfile、Docker镜像和Docker容器的关系
Dockerfile 是软件的原材料,Docker 镜像是软件的交付品,而
Docker 容器则可以认为是软件的运行态。从应用软件的角度来看,Dockerfile、Docker 镜像与 Docker
容器分别代表软件的三个不同阶段,Dockerfile 面向开发,Docker 镜像成为交付标准,Docker
容器则涉及部署与运维,三者缺一不可,合力充当 Docker 体系的基石。
简单来讲,Dockerfile构建出Docker镜像,通过Docker镜像运行Docker容器。
我们可以从Docker容器的角度,来反推三者的关系。首先可以来看下图:
我们假设这个容器的镜像通过以下Dockerfile构建而得:
FROM ubuntu:14.04
ADD run.sh /
VOLUME /data
CMD ["./run.sh"]
2.1 Dockerfile与Docker镜像首先,我们结合上图来看看Dockerfile与Docker镜像之间的关系。
FROM ubuntu:14.04:设置基础镜像,此时会使用基础镜像 ubuntu:14.04 的所有镜像层,为简单起见,图中将其作为一个整体展示。
ADD run.sh /:将 Dockerfile 所在目录的文件 run.sh 加至镜像的根目录,此时新一层的镜像只有一项内容,即根目录下的 run.sh。
VOLUME /data:设定镜像的 VOLUME,此 VOLUME
在容器内部的路径为 /data。需要注意的是,此时并未在新一层的镜像中添加任何文件,即构建出的磁层镜像中文件为空,但更新了镜像的 json
文件,以便通过此镜像启动容器时获取这方面的信息。
CMD ["./run.sh"]:设置镜像的默认执行入口,此命令同样不会在新建镜像中添加任何文件,仅仅在上一层镜像 json 文件的基础上更新新建镜像的 json 文件。
因此,通过以上分析,以上的Dockerfile可以构建出一个新的镜像,包含4个镜像层,每一条命令会和一个镜像层对应,镜像之间会存在父子关系。图中很清楚的表明了这些关系。
2.2 Docker镜像与Docker容器的关系Docker镜像是Docker容器运行的基础,没有Docker镜像,就不可能有Docker容器,这也是Docker的设计原则之一。
可以理解的是:Docker镜像毕竟是镜像,属于静态的内容;而Docker容器就不一样了,容器属于动态的内容。动态的内容,大家很容易联想到进程,内存,CPU等之类的东西。的确,Docker容器作为动态的内容,都会包含这些。
为了便于理解,大家可以把Docker容器,理解为一个或多个运行进程,而这些运行进程将占有相应的内存,相应的CPU计算资源,相应的虚拟网络设备以及相应的文件系统资源。而Docker容器所占用的文件系统资源,则通过Docker镜像的镜像层文件来提供。
那么作为静态的镜像,如何才有能力转化为一个动态的Docker容器呢?此时,我们可以想象:第一,转化的依据是什么;第二,由谁来执行这个转化操作。
其实,转化的依据是每个镜像的json文件,Docker可以通过解析Docker镜像的json的文件,获知应该在这个镜像之上运行什么样的进程,应该为进程配置怎么样的环境变量,此时也就实现了静态向动态的转变。
谁来执行这个转化工作?答案是Docker守护进程。也许大家早就理解这样一句
话:Docker容器实质上就是一个或者多个进程,而容器的父进程就是Docker守护进程。这样的,转化工作的执行就不难理解了:Docker守护进程
手握Docker镜像的json文件,为容器配置相应的环境,并真正运行Docker镜像所指定的进程,完成Docker容器的真正创建。
Docker容器运行起来之后,Docker镜像json文件就失去作用了。此时Docker镜像的绝大部分作用就是:为Docker容器提供一个文件系统的视角,供容器内部的进程访问文件资源。
再次回到上图,我们再来看看容器和镜像之间的一些特殊关系。首先,之前已经提及Docker镜像是分层管理的,管理Docker容器的时候,Docker镜像仍然是分层管理的。由于此时动态的容器中已经存在进程,进程就会对文件系统视角内的文件进行读写操作,因此,就会涉及一个问题:容器是否会篡改Docker镜像的内容?
答案自然是不会的。统一来讲,正如上图,所有的Docker镜像层对于容器来说,都是只读的,容器对于文件的写操作绝对不会作用在镜像中。
既然如此,实现的原理就很重要,究其根本:Docker守护进程会在Docker镜像的
最上层之上,再添加一个可读写层,容器所有的写操作都会作用到这一层中。而如果Docker容器需要写底层Docker镜像中的文件,那么此时就会涉及一
个叫Copy-on-Write的机制,即aufs等联合文件系统保证:首先将此文件从Docker镜像层中拷贝至最上层的可读写层,然后容器进程再对读
写层中的副本进行写操纵。对于容器进程来讲,它只能看到最上层的文件。
那最后我们再来说说:Docker容器的文件系统视角中,到底是不是存在一些内容,不是存储于Docker镜像中的?
这次的答案依旧是肯定的。
再次重申一点,Docker镜像中存储的都是一些静态文件。这些文件原则上应该和容器具体信息以及主机信息完全解藕。那么Docker容器中不存在Docker镜像中的内容主要有以下几点:
1./proc以及/sys等虚拟文件系统的内容
2.容器的hosts文件,hostname文件以及resolv.conf文件,这些事具体环境的信息,原则上的确不应该被打入镜像。
3.容器的Volume路径,这部分的视角来源于从宿主机上挂载到容器内部的路径
4.部分的设备文件QA选集:问:为什么一个ubuntu:14.04镜像的镜像层的数量是4个,前三层的内容似乎有相同的,如etc?孙宏亮:这一点,细心的大家肯定发现了。首先,虽然三层
都有,但是会存在两种情况,etc的子目录下有相同路径的文件,那么上层的会覆盖下层的文件;如果内部的文件路径不相同,那么都会存在,都会呈现给最上
层。[可别较真,说目录也是文件哈,意会]问:关于docker安全性问题,对于安全是怎样处理的,如果我从hub下载镜像,能判别是否安全么2.层级之间的依赖会导致一个崩了整个docker
都崩了么?孙宏亮:从流程上来讲,如果一切可控的话,我认为是安全的。但是依然会存在一些隐患,比如Dockerfile中基于的base
images是否完全受信;镜像的传输过程是否受信;自己的private docker resgitry的安全级别达到什么样的层次,这些都有影响。问:如何保证仅有的一个deamon的稳定性健壮性?孙宏亮:这个问题首先需要知道docker
daemon的稳定性在哪些方面,那种场景下比较差?的确,docker
daemon存在弊病。比如,daemon和容器的耦合等,目前general来讲,docker daemon保证绝对的稳定应该还做不到。问:生产环境中怎么用docker备份mysql数据?孙宏亮:数据存储上docker,我目前的建议是:三思。举个简单的例子,官方的mysql镜像运行出来的
容器,密码是明文的,明文的密码存在于:docker inspect container_name,
container.json文件中,容器的环境变量中,甚至在日志文件中都会存在,just think about
it。当然也有办法解决,缓解这种情况。问:如果是多层构建,中间的一个层做了升级或者bugfix,会潜在影响上层吧?孙宏亮:这个bugfix会在上层有体现,但是使用效果是不会有影响的,还有之前的bug会永远留在下层,但是没有影响。(责编/魏伟)想和业内技术牛人探讨Container技术,请扫描下方的二维码,拉入群内& &或关注我们Container官方微信公众号
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
- DockOne官方账号
非常好的问题,正好之前看到过一篇,我把其中的相关内容贴一下:
我们首先来看看 /var/lib/docker/这个文件夹下的内容。打开这个文件夹下的 repositories 文件,你将会看到类似下面这样的JSON 文件:
$&sudo&cat&/var/lib/docker/repositories&|&python&-mjson.tool
“Repositories”:&{
“ubuntu”:&{
“12.04″:&“8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c”,
“12.10″:&“b750fea3c593ef05ba02a62b4accb2c21d589ff2f5f2dc”,
“latest”:&“8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c”,
“precise”:&“8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c”,
“quantal”:&“b750fea3c593ef05ba02a62b4accb2c21d589ff2f5f2dc”
看看,是不是正好和docker images的内容一致呢。
REPOSITORY&TAG&IMAGE&ID&CREATED&SIZE
ubuntu&&&&&&&&&&&&&&12.04&&&&&&&&&&&&&&&8dbd9e392a96&&&&&&&&8&months&ago&&&&&&&&131.3&MB&(virtual&131.3&MB)
ubuntu&&&&&&&&&&&&&&latest&&&&&&&&&&&&&&8dbd9e392a96&&&&&&&&8&months&ago&&&&&&&&131.3&MB&(virtual&131.3&MB)
ubuntu&&&&&&&&&&&&&&precise&&&&&&&&&&&&&8dbd9e392a96&&&&&&&&8&months&ago&&&&&&&&131.3&MB&(virtual&131.3&MB)
ubuntu&&&&&&&&&&&&&&12.10&&&&&&&&&&&&&&&b750fe79269d&&&&&&&&8&months&ago&&&&&&&&24.65&kB&(virtual&179.7&MB)
ubuntu&&&&&&&&&&&&&&quantal&&&&&&&&&&&&&b750fe79269d&&&&&&&&8&months&ago&&&&&&&&24.65&kB&(virtual&179.7&MB)
接着,我们再来看看 /var/lib/docker/graph/这个文件夹:
$&sudo&ls&-al&/var/lib/docker/graph
drwx——&6&root&root&4096&Nov&22&06:52&.
drwx——&5&root&root&4096&Dec&13&04:25&..
drwxr-xr-x&3&root&root&4096&Dec&13&04:26&27cf
drwxr-xr-x&3&root&root&4096&Nov&22&06:52&8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c
drwxr-xr-x&3&root&root&4096&Nov&22&06:52&b750fea3c593ef05ba02a62b4accb2c21d589ff2f5f2dc
drwx——&3&root&root&4096&Nov&22&06:52&_tmp
这些输出结果可能有点晦涩(Not terribly friendly),但是从中我们可以看出Docker是使用repositories JSON文件来记述镜像信息的,此JSON文件包含了仓库名、标签、以及标签对应的镜像ID。
我们有两个来自ubuntu仓库的镜像,这其中标签为12.04,precise和latest都指向的是同一个镜像,其ID为 8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c (这个ID长度为64位,但是我们可以使用其12位的简短模式,比如8dbd9e392a96)。
那么这些以镜像ID命名的文件夹下面又保存了什么东西呢?
$&sudo&ls&-al&/var/lib/docker/graph/8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c
drwxr-xr-x&&3&root&root&4096&Nov&22&06:52&.
drwx——&&6&root&root&4096&Nov&22&06:52&..
-rw——-&&1&root&root&&437&Nov&22&06:51&json
drwxr-xr-x&22&root&root&4096&Apr&11&&2013&layer
-rw——-&&1&root&root&&&&9&Nov&22&06:52&layersize
这个文件夹下的内容如下:
json -保存着关于这个镜像的元数据layersize – 一个整数,表示layer的大小。layer/ – 子文件夹,保存着rootfs该容器的镜像
$&sudo&cat&/var/lib/docker/graph/8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c/json&|&python&-mjson.tool
“comment”:&“Imported&from&-“,
“container_config”:&{
“AttachStderr”:&false,
“AttachStdin”:&false,
“AttachStdout”:&false,
“Cmd”:&null,
“Env”:&null,
“Hostname”:&“”,
“Image”:&“”,
“Memory”:&0,
“MemorySwap”:&0,
“OpenStdin”:&false,
“PortSpecs”:&null,
“StdinOnce”:&false,
“Tty”:&false,
“User”:&“”
“created”:&“T14:13:15.″,
“docker_version”:&“0.1.4″,
“id”:&“8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c”
$&sudo&cat&/var/lib/docker/graph/8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c/layersize
$&sudo&ls&-al&/var/lib/docker/graph/8dbd9e392ad58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c/layer
drwxr-xr-x&22&root&root&4096&Apr&11&&2013&.
drwxr-xr-x&&3&root&root&4096&Nov&22&06:52&..
drwxr-xr-x&&2&root&root&4096&Apr&11&&2013&bin
drwxr-xr-x&&2&root&root&4096&Apr&19&&2012&boot
drwxr-xr-x&&4&root&root&4096&Nov&22&06:51&dev
drwxr-xr-x&41&root&root&4096&Nov&22&06:51&etc
drwxr-xr-x&&2&root&root&4096&Apr&19&&2012&home
drwxr-xr-x&11&root&root&4096&Nov&22&06:51&lib
drwxr-xr-x&&2&root&root&4096&Nov&22&06:51&lib64
drwxr-xr-x&&2&root&root&4096&Apr&11&&2013&media
drwxr-xr-x&&2&root&root&4096&Apr&19&&2012&mnt
drwxr-xr-x&&2&root&root&4096&Apr&11&&2013&opt
drwxr-xr-x&&2&root&root&4096&Apr&19&&2012&proc
drwx——&&2&root&root&4096&Nov&22&06:51&root
drwxr-xr-x&&4&root&root&4096&Nov&22&06:51&run
drwxr-xr-x&&2&root&root&4096&Nov&22&06:51&sbin
drwxr-xr-x&&2&root&root&4096&Mar&&5&&2012&selinux
drwxr-xr-x&&2&root&root&4096&Apr&11&&2013&srv
drwxr-xr-x&&2&root&root&4096&Apr&14&&2012&sys
drwxrwxrwt&&2&root&root&4096&Apr&11&&2013&tmp
drwxr-xr-x&10&root&root&4096&Nov&22&06:51&usr
drwxr-xr-x&11&root&root&4096&Nov&22&06:51&var
看到这里你应该有些顿悟了吧,这就是我们为什么即使我们不和远程索引或者注册表交互也能通过仓库名来使用一个镜像的原理了。因为一旦你将镜像从远程下载到本地,Docker可以通过仓库名称来使用它们。当你创建自己的Dockerfile的时候也不例外。
文章是去年3月份的,引用之前最好与现在版本对一下变动.其次,讨论存储最好说明底层的driver时什么
我这边对devicemapper了解不多,主要说说aufs的
镜像的存储结构主要分两部分,一是镜像ID之间的关联,一是镜像ID与镜像名称之间的关联,前者的结构体叫Graph,后者叫TagStore.
/var/lib/graph/&image id>
下面没有layer目录,只有每个镜像的json描述文件和layersize大小/var/lib/docker/repositories-aufs
TagStore的存储地方,里面有image id与reponame ,tag之间的映射关系. aufs是driver名/var/lib/docker/aufs/diff/&image id or container id>
每层layer与其父layer之间的文件差异,有的为空,有的有一些文件(镜像实际存储的地方)/var/lib/docker/aufs/layers/&image id or container id>
每层layer一个文件,记录其父layer一直到根layer之间的ID,每个ID一行。大部分文件的最后一行都一样,表示继承自同一个layer./var/lib/docker/aufs/mnt/&image id or container id>
有容器运行时里面有数据(容器数据实际存储的地方,包含整个文件系统数据),退出时里面为空
回答的好详细啊,要赞
我的/var/lib/docker/graph/${image_id}目录下貌似只有json和layersize两个文件,没有layer这个目录?
[root@haichuan-vm&docker]#&ls&graph/*&-R
graph/c5a64f264b78b5433614aecdd4d:
json&&layersize
graph/53f858aaaf7df23e311d71aa93ac578ef39aa076f:
json&&layersize
graph/5b12ef8fd3039acc0e7f68e363c15d8abb5caccede8a:
json&&layersize
我的环境:centos7 docker1.3.2
另外,发现/var/lib/docker/devicemapper/devicemapper下有两个大文件:
[root@haichuan-vm&devicemapper]#&ll&/var/lib/docker/devicemapper/devicemapper/
total&881852
-rw-------.&1&root&root&&Feb&&9&10:46&data
-rw-------.&1&root&root&&&&Feb&&9&10:46&metadata
这两个文件都是hole文件,实际占用磁盘空间大小和local images大小总和基本一致:
[root@haichuan-vm&devicemapper]#&du&-h&/var/lib/docker/devicemapper/devicemapper/*
860M&&&&/var/lib/docker/devicemapper/devicemapper/data
2.1M&&&&/var/lib/docker/devicemapper/devicemapper/metadata
并且这两个文件大小会随着image pull、rmi操作而相应的增大、减小。so,我推测这两个文件就是docker pull ${image}之后,image的layers真正存储的地方。如果确实如此,不知为何docker不把image的layer保存为&透明的二进制&文件?或是像上面 所表述的那样保存为一个layer目录(这种方案不好的地方是不太安全,but应该还有其它考虑吧?)
另外,还是没有发现实际存放container的地方。如下:
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89-json.log
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/config.json
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/hostconfig.json
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/hostname
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/hosts
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/resolv.conf
containers/cf84e88a726a7fb76e74cc5eb6cbfbde9ce89/secrets:
把问题总结一下:
1)实际存放images的地方是否是/var/lib/docker/devicemapper/devicemapper目录下的metadata和data两个文件?
2)如果是上面两个文件,为什么要这么存储?而不是存为&透明二进制&或&layer目录&?
3)实际存放container的地方在哪?
看了近期开放的,实际存放images和container的地方就是/var/lib/docker/devicemapper/devicemapper目录下的metadata和data两个文件。
但第二个问题还是不明白,为什么要这么存储?而不是存为一个个的&透明二进制&或&layer目录&(安全考虑?)?
要回复问题请先或
sina攻城狮,docker爱好者
浏览: 51532
关注: 19 人本文原创,原文地址为:
创建镜像的目的
首先说DockerHub或其它一些镜像仓库已经提供了够多的镜像,有最小版本,也有一些安装了mysql、nginx、apache等等第三方软件的版本可以直接拿来使用。虽然已经足够多了,但是有些情况下并不能满足我们的需求,例如需要安装一些比较少用到的第三方软件,这个时候只能先用公共仓库中的镜像,启动容器,然后在容器中按照我们的需求安装软件,修改配置等等操作,之后提交镜像。这些操作在之前的中介绍了。这样操作完成之后,可以用如下两种方式实现定制镜像的目的:
1.用save和export的方式将镜像保存为tar包,然后在需要的时候导入tar镜像包
2.将已经配置好的镜像push到我们的私有仓库()或者已注册过的共有仓库中,需要的时候直接pull下来使用
这两种方式都可以,但是自动化程度低、自由度不够、定制起来比较麻烦。既然如此,那就来说一下更加自动化的创建方式。
Dockerfile结构
dockerfile由4部分信息组成:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user &docker_user & (@docker_user)
MAINTAINER docker_user docker_
# Commands to update the image
RUN echo "deb /ubuntu/ raring main universe" && /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\" && /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
其中#表注释,可以标注一些说明性的文字。
FROM关键字指定镜像的来源,默认为DockerHub,也可以写私有仓库的镜像,例如:localhost:5000/centos:6.7,如果本地已经存在指定的镜像名称,则会从本地缓存直接获取。MAINTAINER 指定镜像的作者,之后为镜像操作执行RUN、ADD等,最后是容器启动时发起的指令。
Dockerfile中的指令
FROM: 指定镜像名称,格式为FROM &image& 或FROM &image&:&tag&,例如FROM ubuntu 或 FROM ubuntu:12.04 
MAINTAINER: 镜像作者 ,格式为&MAINTAINER &name&
RUN:格式为&RUN &command&&或&RUN ["executable", "param1", "param2"]。
前者将在 shell 终端中运行命令,即&/bin/sh -c;后者则使用&exec&执行。指定使用其它终端可以通过第二种方式实现,例如&RUN ["/bin/bash", "-c", "echo hello"]。
每条&RUN&指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用&\&来换行。
CMD:支持三种格式
  1.CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;  2.CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;  3.CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
EXPOSE:格式为&EXPOSE &port& [&port&...]。
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。
ENV:格式为&ENV &key& &value&。 指定一个环境变量,会被后续&RUN&指令使用,并在容器运行时保持。这就对应程序语言中的变量定义,可在需要的时候引用。例如:
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL /postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && &
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD:格式为&ADD &src& &dest&。
该命令将复制指定的&&src&&到容器中的&&dest&。 其中&&src&&可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
COPY:格式为&COPY &src& &dest&。
复制本地主机的&&src&(为 Dockerfile 所在目录的相对路径)到容器中的&&dest&。当使用本地目录为源目录时,推荐使用&COPY。
COPY和ADD的不同就是:ADD多了自动解压和支持URL路径的功能。
ENTRYPOINT:
两种格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2(shell中执行)。
配置容器启动后执行的命令,并且不可被&docker run&提供的参数覆盖。
每个 Dockerfile 中只能有一个&ENTRYPOINT,当指定多个时,只有最后一个起效。
CMD和ENTRYPOINT比较:两个命令都是只能使用一次,并且都是在执行docker run指令时运行,如果有多个,只执行最后一条。
两者的不同在于参数的传递方式,如果在Dockerfile中定义如下指令
CMD echo hello
ENTRYPOINT ["echo","hello"] 
那么在运行命令docker run containerId echo hello时,指定了CMD的输入结果为world,可以看出Dockerfile中指定的命令被覆盖了,而指定了ENTRYPOINT时,输出结果为hello echo world,可以看出指定的命令被作为ENTRYPOINT指定指令的参数了。
VOLUME:格式为&VOLUME ["/data"]。创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。不过此属性在Dockerfile中指定并没有什么意义,因为没有办法指定本地主机的目录。如果需要指定挂载点可以在执行docker run命令时指定:
docker run -it -v /home/fengzheng/ftp/:/data
c6d /bin/bash
USER:格式为&USER daemon。指定运行容器时的用户名或 UID,后续的&RUN&也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用&gosu,而不推荐&sudo。
WORKDIR:格式为&WORKDIR /path/to/workdir。为后续的&RUN、CMD、ENTRYPOINT&指令配置工作目录。可以使用多个&WORKDIR&指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
则最终路径为&/a/b/c。
ONBUILD:格式为&ONBUILD [INSTRUCTION]。
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像&image-A。
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
如果基于 image-A 创建新的镜像时,新的Dockerfile中使用&FROM image-A指定基础镜像时,会自动执行ONBUILD&指令内容,等价于在后面添加了两条指令。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用&ONBUILD&指令的镜像,推荐在标签中注明,例如&ruby:1.9-onbuild。
基于CentOS6.7并源码安装nginx
首先准备了nginx-1.9.9.tar.gz安装包和CentOS6-Base-163.repo(163源),将这两个文件放到同一目录下,并在此目录下创建名称为Dockerfile的文件。之后在此文件中实现源替换、nginx编译安装、及一些依赖包的安装,Dockerfile内容如下:
# this is a test ubuntu 12.04 image dockerfile
# Author:fengzheng
# Base image,this must be set as the first line
#localhost:5000/centos:6.7是我的私有仓库的镜像,可替换为centos:6.7(DockerHub中的镜像)
FROM localhost:5000/centos:6.7
MAINTAINER fengzheng
# Commands to update the image
RUN mkdir /usr/nginx1.9.9
ADD nginx-1.9.9.tar.gz /usr/nginx1.9.9/
#RUN yum -y install tar
#RUN tar -zxvf /usr/nginx1.9.9/nginx-1.9.9.tar.gz
RUN cd /etc/yum.repos.d/ && mv CentOS-Base.repo CentOS-Base.repo.bak
ADD CentOS6-Base-163.repo /etc/yum.repos.d/
RUN cd /etc/yum.repos.d/ && mv CentOS6-Base-163.repo CentOS-Base.repo \
&& yum clean all && yum makecache \
&& yum -y install gcc \
&& yum -y install yum install -y pcre-devel \
&& yum -y install zlib zlib-devel \
&& yum -y install openssl openssl--devel \
&& cd /usr/nginx1.9.9/nginx-1.9.9/ && ./configure && make && make install
#如果设置 nginx无法启动
#RUN echo "\" && /etc/nginx/nginx.conf
# Commands when creating a new container
# 启动nginx 需进入/usr/local/nginx/sbin 执行./configure
CMD /bin/bash
最后执行命令"docker build -t nginx-centos:6.7 ."
其中.表示在当前目录下搜索Dockerfile文件,-t参数指定镜像名称和tag。
阅读(...) 评论()

我要回帖

更多关于 docker 容器生成镜像 的文章

 

随机推荐