赞
踩
Docker所宣称的用户可以随心所欲地“Build、Ship and Run”应用的能力,其核心是由Docker image(Docker 镜像)来支撑的。Docker通过把应用的运行时环境和应用打包在一起,解决了部署环境依赖的问题;通过引入分层文件系统这种概念,解决了空间利用的问题。它彻底消除了编译、打包与部署、运维之间的鸿沟,与现在互联网企业推崇的DevOps 理念不谋而合,大大提高了应用开发部署的效率。Docker 公司的理念被越来越多的人理解和认可也就是理所当然的了,而理解 Docker image则是深入理解 Docker 技术的一个关键点。
本章主要介绍 Docker image的使用和相关技术细节。其中3.1节介绍Docker image的基本概念;3.2节从image生命周期的角度介绍其使用方法;3.3 节介绍 Doker image 的组织结构;3.4节介绍Dockerimage相关的一些扩展知识。
3.1 Dockerimage概念介绍简单地说,Docker image 是用来启动容器的只读模板,是容器启动所需要的rootfs,类似于虚拟机所使用的镜像。首先需要通过一定的规则和方法表示 Docker image,如图3-1所示。
图3-1是典型的Docker 镜像的表示方法,可以看到其被“/”分为了三个部分,其中每部分都可以类比Github中的概念。下面按照从左到右的Namespace Repository TagRemote image hub顺序介绍这几个部分以及相关的一些重要概念
Remote-dockerhub.com/namespace/bar:latest
图3-1 Docker 镜像的典型表示法。口Remote docker hub:集中存储镜像的We 服务器地址。该部分的存在使得可以区分从不同镜像库中拉取的镜像。若 Docker 的镜像表示中缺少该部分,说明使用的是默认镜像库,即Docker 官方镜像库
口Namespace:类似于Github 中的命名空间,是一个用户或组织中所有镜像的集合口 Repository:类似于Git 仓库,一个仓库可以有多个镜像,不同镜像通过 tag 来区分口Tag:类似Git仓库中的 tag,一般用来区分同一类镜像的不同版本。
口 Layer:镜像由一系列层组成,每层都用64位的十六进制数表示,非常类似于Git仓库中的commit。
口Image ID:镜像最上层的layer ID 就是该镜像的ID,Repotag 提供了易于人类识别的名字,而ID便于脚本处理、操作镜像。镜像库是 Docker 公司最先提出的概念,非常类似应用市场的概念。用户可以发布自己的镜像,也可以使用别人的镜像。Docker 开源了镜像存储部分的源代码(Docker Registry以及Distribution),但是这些开源组件并不适合独立地发挥功能,需要使用Nginx等代理工具添加基本的鉴权功能,才能搭建出私有镜像仓库。本地镜像则是已经下载到本地的镜像可以使用docker images 等命令进行管理。这些镜像默认存储在/var/lib/docker 路径下,该路径也可以使用dockerdaemon-g参数在启动Daemon时指定。
提Docker 的镜像已经支持更多层级,比如用户的命名空间之前可以包含组织(Remotedockerhub.com/group/namespace/bar:latest)。但是目前Docker 官方的镜像库还不具备该能力。
3.2 使用DockerimageDocker内嵌了一系列命令制作、管理、上传和下载镜像。可以调用RESTAPI给Docker daemon发送相关命令,也可以使用client端提供的CLI命令完成操作。本书的第7章会详细阐述Docker RESTAPI的细节,本节则主要根据功能对涉及image的命令进行说明。下面就从Dockerimage的生命周期角度说明 Dockerimage的相关使用方法:
其中,--filter 用于过滤 docker images 的结果,过滤器采用 key=value 的这种形式。目前支持的过滤器为dangling和label。--filter"dangling-true"会显示所有“悬挂”镜像。“悬挂”镜像没有对应的名称和 tag,并且其最上层不会被任何镜像所依赖。docker commit在一些情况下会产生这种“悬挂”镜像。下面第一条命令产生了一个“悬挂”镜像,第二条命令则根据其特点过滤出该镜像了。图3-2中的d08407d841f3 就是这种镜像。
在上面的命令中,--no-trunc 参数可以列出完整长度的Image ID。若添加参数-q则会只输出Image ID,该参数在管道命令中很有用处。一般来说悬挂镜像并不总是我们所需要的,并且会浪费磁盘空间。可以使用如下管道命令删除所有的“悬挂”镜像。
这里的--digests比较特别,这个参数是伴随着新版本的Docker Registry V2(即Distribution)产生的,在本书接下来的第4章会详细说明。
按照Docker 官方路标和最近的动作Docker 只会保留最核心的image相关命令和功能因此那些非核心功能就会被删除。比如--tree 和--dot已经从Docker 1.7中删掉。官方推荐使用dockerviz@工具分析 Docker image。执行以下命令,可以图形化地展示 Docker image3.2.2 Build:创建一个镜像
创建镜像是一个很常用的功能,既可以从无到有地创建镜像,也可以以现有的镜像为基础进行增量开发,还可以把容器保存为镜像。下面就详细介绍这些方法。
1直接下载镜像
我们可以从镜像仓库下载一个镜像,比如,以下为下载busybox镜像的示例。
S docker pull busybox
Using default tag: latest
Pulling repository docker.io/library/busybox
8c2e06607696:Download complete
cf2616975b4a: Download complete
6ce2e90b0bc7: Download complete
Status: Downloaded newer image for busybox:latest
具体使用镜像仓库的方法,本书会在后续章节详细描述,这里暂不做说明。
2.导入镜像
还可以导人一个镜像,对此,Docker 提供了两个可用的命令docker import和dockeload。docker load一般只用于导人由 docker save 导出的镜像,导入后的镜像跟原镜像完全一样包括拥有相同的镜像ID 和分层等内容。下面的第一行命令可以导出busybox为busybox.tar,第二条命令则是导人该镜像:
$ docker save-o busybox.tar busybox
$ docker load
-i busybox.tar不同于 docker load,docker import 不能用于导人标准的 Docker 镜像,而是用于导人包含根文件系统的归档,并将之变成 Docker 镜像。
3制作新的镜像
前面说过,docker import 用于导人包含根文件系统的归档,并将之变成 Docker 镜像因此,docker import 常用来制作 Docker基础镜像,如Ubuntu 等镜像。与此相对,dockerexport则是把一个镜像导出为根文件系统的归档。
福读者可以使用 Debian提供的 Debootstrap 制作 Debian或Ubuntu的 Base image,可以在Docker 官网找到教程 。Docker 提供的docker commit 命令可以增量地生成一个镜像,该命令可把容器保存为一个镜像,还能注明作者信息和镜像名称,这与git commit类似。当镜像名称为空时,就会形成“悬挂”镜像。当然,使用这种方式每新增加一层都需要数个步骤(比如,启动容器、修改、保存修改等),所以效率是比较低的,因此这种方式适合正式制作镜像前的尝试当最终确定制作的步骤后,可以使用 docker build 命令,通过Dockerfile 文件生成镜像。
3.2.3Ship:传输一个镜像
镜像传输是连接开发和部署的桥梁。可以使用Docker 镜像仓库做中转传输,还可以使用docker export/docker save生成的tar包来实现,或者使用Docker 镜像的模板文件Dockerfile做间接传输。目前托管在Github 等网站上的项目,已经越来越多地包含有Dockerfile 文件;同时 Docker 官方镜像仓库使用了github.com的webhook 功能,若代码被修改就会触发流程自动重新制作镜像。3.24Run:以image为模板启动一个容器
启动容器时,可以使用docker run 命令,该命令在相关章节会详细描述,本节不做深入说明。
图3-3总结了上文提到的 Docker 镜像生命周期管理的相关命令。现阶段 Docker 镜像相关的命令存在一些问题,包括:
口命令间逻辑不一致,比如列出容器使用的是 docker ps,列出镜像使用的是 dockerimageso
口混用命令导致命令语义不清晰,比如查看容器和镜像详细信息的命令都是 dockerinspecto
所以基于这些考虑,Docker 项目的路标中提到会把相关命令归类,并使用二级命令来管理。因此读者可以着重学习命令的用法和其实现的功能,不用过分关心命令的形式。3.3 Dockerimage的组织结构
上节讲到 Docker image是用来启动容器的只读模板,提供容器启动所需要的rootfs.
那么Docker是怎么组织这些数据的呢?
3.31数据的内容
Docker image 包含着数据及必要的元数据。数据由一层层的image layer 组成,元数据则是一些JSON文件,用来描述数据(image layer)之间的关系以及容器的一些配置信息下面使用overlay存储驱动对 Docker image 的组织结构进行分析,首先需要启动Dockerdaemon,命令如下:
# docker daemon -D-s overlay -g /var/lib/docker
这里从官方镜像库下载busybox镜像用作分析。由于前面已经下载过该镜像,所以这里并没有重新下载,而只是做了简单的校验。可以看到 Docker 对镜像进行了完整性校验这种完整性的凭证是由镜像仓库提供的。相关内容会在后面的章节提到,这里不再展开介绍。
2数据和元数据
graph目录和overlay目录包含本地镜像库中的所有元数据和数据信息。对于不同的存储驱动,数据的存储位置和存储结构是不同的,本章不做深入的讨论。可以通过下面的命令观察数据和元数据中的具体内容。元数据含json和 layersize 两个文件,其中json 文件包含了必要的层次和配置信息,layersize 文件则包含了该层的大小。2数据和元数据
graph目录和overlay目录包含本地镜像库中的所有元数据和数据信息。对于不同的存储驱动,数据的存储位置和存储结构是不同的,本章不做深入的讨论。可以通过下面的命令观察数据和元数据中的具体内容。元数据含json和 layersize 两个文件,其中json 文件包含了必要的层次和配置信息,layersize 文件则包含了该层的大小。
可以看到Docker 镜像存储路径下已经存储了足够的信息,Docker daemon 可以通过这些信息还原出 Docker image:先通过repositories-overlay 获得image 对应的layerID;再根据layer 对应的元数据梳理出image 包含的所有层,以及层与层之间的关系;然后使用联合挂载技术还原出容器启动所需要的rootfs 和一些基本的配置信息。
3.32数据的组织
从上节看到,通过repositories-overlay 可以找到某个镜像的最上层layer ID,进而找到对应的元数据,那么元数据都存了哪些信息呢?可以通过 docker inspect 得到该层的元数据。为了简单起见,下面的命令输出中删除了一些与讨论无关的层次信息。
docker inspect 并不是直接输出磁盘中的元数据文件,而是对元数据文件进行了整注理,使其更易读,比如标记镜像创建时间的条目由created 改成了Created;标记容器配置的条目由container config 改成了ContainerConfig,但是两者的数据是完全一致的。
对于上面的输出,有几项需要重点说明一下:口Id:Image的ID。通过上面的讨论,可以看到imageID 实际上只是最上层的layerID,所以 dockerinspect 也适用于任意一层layer。口Parent:该layer的父层,可以递归地获得某个image的所有 layer 信息。口Comment:非常类似于Git的commit message,可以为该层做一些历史记录,方便其他人理解
口Container:这个条目比较有意思,其中包含哲学的味道。比如前面提到容器的启动需要以image为模板。但又可以把该容器保存为镜像,所以一般来说image的每个layer都保存自一个容器,所以该容器可以说是imagelayer的“模板”口Config:包含了该image的一些配置信息,其中比较重要的是:“env”容器启动时会作为容器的环境变量;“Cmd”作为容器启动时的默认命令;“Labels”参数可以用于dockerimages命令过滤
口Architecture:该image对应的CPU体系结构。现在Docker 官方支持amd64,对其他体系架构的支持也在进行中。通过这些元数据信息,可以得到某个image包含的所有layer,进而组合出容器的rootfs,再加上元数据中的配置信息(环境变量、启动参数、体系架构等)作为容器启动时的参数。至此已经具备启动容器必需的所有信息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。