第三章 Docker镜像
1. 镜像基础
1.1 镜像简介
镜像是一种轻量级、可执行的独立软件包,也可以说是一个精简的操作系统。镜像中包含应用软件及应用软件的运行环境。具体来说镜像包含运行某个软件所需的所有内容,包括代码、库、环境变量和配置文件等。几乎所有应用,直接打包为Docker
镜像后就可以运行。
由于镜像的运行时是容器,容器的设计初衷就是快速和小巧,所以镜像通常都比较小,镜像中不包含内核,其共享宿主机的内核;镜像中只包含简单的Shell
,或没有Shell
。
1.2 镜像仓库分类
镜像中心中存储着大量的镜像仓库Image Repository
,每个镜像仓库中包含着大量相关镜像。根据这些镜像发布者的不同,形成了四类不同的镜像仓库。
Docker Official Image
Docker
官方镜像仓库。该类仓库中的镜像由Docker
官方构建发布,代码质量较高且安全,有较完善的文档。该类仓库中的镜像会及时更新。一般常用的系统、工具软件、中间件都有相应的官方镜像仓库。例如,Zookeeper
、Redis
、Nginx
等。官方镜像仓库的名称
<repository>
一般直接为该类软件的名称<software-name>
。Verified Publisher
已验证发布者仓库。该类仓库中的镜像由非
Docker
官方的第三方发布。但该第三方是由Docker
公司审核认证过的,一般为大型企业、团体或组织。审核通过后,Docker
公司会向其颁发“VERIFIED PUBLISHER
”标识。这种仓库中镜像的质量还有有保证的。除了官方镜像仓库,其它都是非官方镜像仓库。非官方镜像仓库名称<repository>
一般由发布者用户名与软件名称两部分构成,形式为:<username>/<software-name>
。Sponsored OSS
由
Docker
公司赞助开发的镜像仓库。该类仓库中的镜像也由非Docker
官方的第三方发布,但该镜像的开发是由Docker
公司赞助的。该类型的第三方一般为个人、团队或组织。这种仓库中镜像的质量也是有保证的。无认证仓库
没有以上任何标识的仓库。这种仓库中镜像的质量良莠不齐,质量上无法保证,在使用时需谨慎。
1.3 第三方镜像中心
镜像中心默认使用的都是Docker
官方的**Docker Hub
**。不过,镜像中心是可配置的,可以使用指定的第三方镜像中心。对于第三方镜像中心中的仓库名称<repository>
由三部分构成:<domain-name>/<username>/<software-name>
。其中的<domain-name >
指的是第三方镜像中心的域名或IP。
1.4 镜像定位
对于任何镜像,都可通过<repository>:<tag>
进行唯一定位。其中<tag>
一般称为镜像的版本号。<tag>
中有一个比较特殊的版本——latest
。如果不指定,默认<tag>
即为latest
。不过,虽然其字面意思是最新版,一般其也的确存放的是最新版,但并不能保证其真的就是最新版。
2. 镜像相关命令
2.1 docker pull
2.1.1 基本用法
通过docker pull
命令可以将指定的镜像从docker hub
拉取到本地。如果没有指定镜像则会抛出一个Error
。
例如,下面的命令是拉取zookeeper
的3.7.1版本镜像。
pull命令中的<tag>
也可以不写,此时默认的<tag>
为latest
。
此时可以查看到,当前宿主机中已经包含了刚刚下载的两个zookeeper
镜像。
2.1.2 简化日志输出
加上选项-q
后就可简化拉取过程中的日志输出。
2.1.3 通过digest拉取
docker pull
可通过镜像的digest
进行拉取。语法格式为docker pull <repository>@<digest>
。
digest
,是镜像内容的一个Hash
值,即所谓的Content Hash
(内容散列)。只要镜像内容发生了变更,其内容散列值就一定会发生改变。注意,digest
是包含前面的sha256
的,表示该digest
的产生所采用的Hash
算法是SHA256
。使用该拉取方式的具体场景或用途,后面会详解。
从Docker Hub
中具体镜像中可查看到其digest
。
2.2 docker images
2.2.1 基础用法
通过docker images
命令可查看本地所有镜像资源信息。这些镜像会按照镜像被创建的时间由近及远排序。
REPOSITORY
:镜像仓库名称TAG
:镜像版本号IMAGE ID
:镜像的唯一标识CREATE
:镜像的创建时间SIZE
:镜像大小
2.2.2 查看指定镜像
docker images
可以查看指定镜像的信息。
2.2.3 查看完整镜像ID
默认的docker images
显示的镜像id
是经过截取后的显示结果,仅显示了前12位。使用--no-trunc
参数后显示的是完成的镜像id。
2.2.4 查看镜像digest
--digests
选项可以查看所有镜像或指定镜像的digest
信息。关于digest
后面会详细学习。
2.2.5 仅显示镜像ID
-q
选项可仅显示本地所有镜像的ImageID
。该主要是将来与其它命令联合使用。
2.2.6 过滤镜像
-f
选项用于过滤指定条件的镜像。下面例举一些常用的过滤条件。例如,
dangling=true
用于过滤出悬虚镜像,即没有Repository
与Tag
的镜像。对于悬虚镜像的REPOSITORY
与TAG
,显示的是<none>
。
-f before
用于列举出本地镜像中指定镜像创建时间之前创建的所有镜像。
-f since
用于列举出本地镜像中指定镜像创建时间之后的创建的所有镜像。
-f reference
用于列举出<repository>:<tag>
与指定表达式相匹配的所有镜像。
2.2.7 格式化显示
该选项用于格式化输出docker images
的内容,格式需要使用GO
模板指定。例如,
2.3 docker search
2.3.1 基础用法
通过docker search
命令可以从docker hub
上查看指定名称的镜像。
AUTOMATED
表示当前镜像是否是“自动化镜像”。什么是自动化镜像?就是使用Docker Hub
连接一个包含Dockerfile
文件(专门构建镜像用的文件)的GitHub
仓库或Bitbucket
仓库的源码托管平台,然后Docker Hub
就会自动根据Dockerfile
内容构建镜像。这种构建出的镜像会被标记为AUTOMATED
,这种构建镜像的方式称为Trusted Build
(受信构建)。只要Dockerfile
文件内容发生变化,那么Docker Hub
就会构建出新的镜像。
2.3.2 过滤检索结果
用于过滤查询结果。例如,下面的是仅查询出官方提供的镜像。
2.3.3 限制检索数量
默认docker search
显示25条检索结果,可通过--limit
选项来指定显示的结果数量。
2.3.4 到hub官网查看
以上检索方式与从docker hub
官网https://hub.docker.com查看是一样的,但没有官网查看的直观。
2.4 docker rmi
2.4.1 基本用法
rmi(remove images)
。该命令用于删除指定的本地镜像。镜像通过<repository>:<tag>
指定。如果省略要删除镜像的tag
,默认删除的是lastest
版本。
2.4.2 删除多个镜像
docker rmi
命令可一次性删除多个镜像,多个要删除的镜像间使用空格分隔。
docker rmi 镜像1 镜像2 镜像3...
2.4.3 通过ImageID删除镜像
docker rmi
也可通过ImageID
指定要删除的镜像。
docker rmi ImageID
2.4.4 强制删除镜像
默认情况下,对于已经运行了容器的镜像是不能删除的,必须要先停止并删除了相关容器然后才能删除其对应的镜像。不过,也可以通过添加-f选项进行强制删除。
docker rmi -f 镜像名称
2.4.5 删除所有镜像
使用组合命令删除所有镜像。当然,如果不携带-f
选项,则不会删除已打开容器的镜像。
docker rmi -f $(docker images -q)
docker images -q
:列出所有 Docker 镜像的 ID$(docker images -q)
:把上面命令列出来的所有 ID 作为参数传递给下一个命令。docker rmi -f
:强制删除 Docker 镜像,即使有容器正在使用它们。docker rmi -f $(docker images -q)
:删除 Docker 主机上所有的镜像。
2.5 导出和导入镜像
我们在本地生成一个镜像,想将其导出后在另一电脑上使用,则可通过导出/导入镜像来完成。
2.5.1 导出镜像save
docker save
命令用于将一个或多个镜像导出为tar
文件。例如,下面的命令是将redis
镜像导出到当前/root
目录的myRedis.tar
文件中。
2.5.2 导入镜像load
docker load
用于将一个tar
文件导入并加载为一个或多个镜像。
运行完毕后,可以看到redis
镜像恢复了。
3. 镜像分层
3.1 什么是分层
Docker
镜像由一些松耦合的只读镜像层组成,Docker Daemon
负责堆叠这些镜像层,并将它们关联为一个统一的整体,即对外表现出的是一个独立的对象。通过docker pull
命令拉取指定的镜像时,每个Pull complete
结尾的行就代表下载完毕了一个镜像层。例如,下面的redis:latest
镜像就包含6个镜像层。
3.2 为什么分层
采用这种分层结构的优势很多,例如,每个分层都是只读的,所有对分层的修改都是以新分层的形式出现,并不会破坏原分层内容;再如,每个分层只记录变更内容,所以有利于节省存储空间等。
不过,分层结构的最大的好处是,在不同镜像间实现资源共享,即不同镜像对相同下层镜像的复用。对于docker pull
命令,其在拉取之前会先获取到其要拉取镜像的所有ImageID
,然后在本地查找是否存在这些分层。如果存在,则不再进行拉取,而是共享本地的该分层。大大节点的存储空间与网络带宽,提升了拉取效率。
3.3 镜像层构成
每个镜像层由两部分构成:镜像文件系统与镜像json文件。这两部分具有相同的ImageID
。
镜像文件系统就是对镜像占有的磁盘空间进行管理的文件系统,拥有该镜像所有镜像层的数据内容。而镜像json
文件则是用于描述镜像的相关属性的集合,通过docker inspect
[镜像]就可以直观看到。
3.4 镜像FS构成
一个docker
镜像的文件系统FS
由多层只读的镜像层组成,每层都完成了特定的功能。而这些只读镜像层根据其位置与功能的不同可分为两类:基础镜像层与扩展镜像层。
基础镜像层
所有镜像的最下层都具有一个可以看得到的基础镜像层
Base Image
,基础镜像层的文件系统称为根文件系统rootfs
。而rootfs
则是建立在Linux系统中“看不到的”引导文件系统bootfs
之上。扩展镜像层
在基础镜像层之上的镜像层称为扩展镜像层。顾名思义,其是对基础镜像层功能的扩展。在
Dockerfile
中,每条指令都是用于完成某项特定功能的,而每条指令都会生成一个扩展镜像层。容器层
一旦镜像运行了起来就形成了容器,而容器就是一个运行中的
Linux
系统,其也是具有文件系统的。容器的这个文件系统是在docker
镜像最外层之上增加了一个可读写的容器层,对文件的任何更改都只存在于容器层。因此任何对容器的操作都不会影响到镜像本身。容器层如果需要修改某个文件,系统会从容器层开始向下一层层的查找该文件,直到找到为止。任何对于文件的操作都会记录在容器层。例如,要修改某文件,容器层会首先把在镜像层找到的文件copy到容器层,然后再进行修改。删除文件也只会将存在于容器层中的文件副本删除。
可以看出,
Docker
容器就是一个叠加后的文件系统,而这个容器层称为Union File System
,联合文件系统。
3.5 LinuxOS启动过程(扩展)
现代操作系统都是C/S
模式的微内核架构的,由两大部分构成:内核(Server
)与服务模块(Client
)。
Linux
的bootfs
文件系统由两部分构成:bootloader
与kernel
。
各个容器中的rootft
就是由宿主机的kernel
驱动的。
3.6 镜像摘要
每个镜像都有一个长度为64位的16进制字符串作为其摘要digest
。
查看摘要
在
docker pull
镜像结束后会给出该拉取的镜像的摘要digest
。通过
docker inspect
命令可以查看指定镜像的详细信息。其中就包含该镜像的摘要信息。通过
docker images --digests
命令也可以查看到镜像的摘要信息。摘要是什么
摘要,即
digest
,是镜像内容的一个Hash值,即所谓的Content Hash
(内容散列)。只要镜像内容发生了变更,其内容散列值就一定会发生改变。也就是说,一个镜像一旦创建完毕,其digest
就不会发生改变了,因为镜像是只读的。Docker
默认采用的Hash算法是SHA256
,即Hah
值是一个长度为256位的二进制值。Docker
使用16进制表示,即变为了长度为64位的字符串。摘要有何用
摘要的主要作用是区分相同
<repository>:<tag>
的不同镜像。例如镜像
xxx:2.8
在生产运行过程中发现存在一个BUG
。现对其进行了修复,并使用原标签将其push
回了仓库,那么原镜像被覆盖。但生产环境中遗留了大量运行中的修复前镜像的容器。此时,通过镜像标签已经无法区分镜像是修复前的还是修复后的了,因为它们的标签是相同的。此时通过查看镜像的digest
就可以区分出修改前后版本,因为内容发生了变化,digest
一定会变。为了确保再次拉取到的是修复后的镜像,可通过
digest
进行镜像拉取。其用法是:docker pull <repository>@<digest>
不过,不方便的是,镜像的摘要需要由运维人员在本地进行手工维护。
分发散列值
在
push
或pull
镜像时,都会对镜像进行压缩以减少网络带宽和传输时长。但压缩会改变镜像内容,会导致经过网络传输后,镜像内容与其digest
不相符。出现问题。为了避免该问题,Docker
又为镜像配置了Distribution Hash
(分发散列值)。在镜像被压缩后立即计算分发散列值,然后使该值随压缩过的镜像一同进行发送。在接收方接收后,立即计算压缩镜像的分发散列值,再与携带的分发散列值对比。如果相同,则说明传输没有问题。
3.7 多架构镜像
Multi-architecture Image
,即多架构镜像,是某<repository>
中的某<tag>
镜像针对不同操作系统/系统架构的不同镜像实现。即多架构镜像中包含的镜像的<repository>:<tag>
都是相同的,但它们针对的操作系统/系统架构是不同的。
多架构镜像原理
无论用户使用的是什么操作系统/系统架构,其通过
docker pull
命令拉取到的一定是针对该操作系统/系统架构的镜像,无需用户自己考虑操作系统/系统架构问题。Docker Hub
能够根据提交pull
请求的Docker
系统的架构自动选择其对应的镜像。在
Docker Hub
中,镜像的多架构信息保存在Manifest
文件中。在拉取镜像时,Docker
会随着pull
命令将当前Docker
系统的OS
与架构信息一并提交给Docker Hub
。Docker Hub
首先会根据镜像的<repository>:<tag>
查找是否存在Manifest
。如果不存在,则直接查找并返回<repository>:<tag>
镜像即可;如果存在,则会在Manifest
中查找是否存在指定系统/架构的镜像。如果存在该系统/架构,则根据Manifest
中记录的地址找到该镜像的位置。