什么是docker镜像
Docker镜像是Docker容器的基础组件,它包含了运行一个应用程序所需的一切,包括代码、运行时环境、系统工具、库和依赖等。Docker镜像的本质,可以分为以下几个方面来解释。
- 文件系统层:Docker镜像是由多个文件系统层(Filesystem Layers)组成的。每个层都是只读的,并且包含了文件和目录的变更。这种分层的文件系统结构使得镜像的构建和复用变得非常高效。每个层只需要存储差异部分,大大减小了镜像的体积。
- 只读性:Docker镜像是只读的,一旦创建就不能被修改。当需要修改一个镜像时,Docker会在原有的镜像上创建一个新的镜像,这个新镜像会包含原有镜像的所有层,并在其基础上添加新的层。
- 分层存储:Docker镜像的文件系统采用分层存储的方式。这意味着多个镜像可以共享同一个文件系统层,从而节省存储空间。当多个容器同时运行时,它们可以共享相同的基础镜像,只需在其基础上添加自己的可写层。
- 镜像的构建与复用:Docker镜像的构建是通过Dockerfile文件来定义的。Dockerfile中包含了一系列构建指令,用于描述如何从基础镜像中构建一个新的镜像。这种构建方式使得镜像的构建过程可自动化,并且易于复用和共享。
从操作系统原理角度来看,Docker镜像可以从操作系统原理角度来看,Docker镜像可以类比为操作系统中的文件系统快照。一个Docker镜像可以看作是一个只读的文件系统快照,它包含了应用程序运行所需的所有文件和目录。每个镜像层都相当于文件系统中的一个增量变更,它们按照层级的方式进行组织,使得镜像的构建和复用更加高效。
类似于操作系统中的进程,Docker容器是基于镜像创建的运行实体。当创建一个Docker容器时,Docker会在镜像的基础上添加一层可写的文件系统层,这个层称为容器层。容器层可以进行读写操作,而镜像层是只读的,这样就实现了镜像的复用和容器的隔离。
镜像相关命令
docker pull
# 用法
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
OPTIONS说明:
- -a:拉取所有 tagged 镜像。
- –disable-content-trust:忽略镜像的校验,默认开启。
- -q: 可简化拉取过程中的日志输出。
除此之外,docker pull 也可通过镜像的 digest 进行拉取。语法格式为 docker pull <repository>@<digest>。
digest,是镜像内容的一个 Hash 值,即所谓的 Content Hash(内容散列)。只要镜像内容发生了变更,其内容散列值就一定会发生改变。注意,digest 是包含前面的 sha256 的,表示该 digest 的产生所采用的 Hash 算法是 SHA256。
docker images
# 用法
docker images [OPTIONS] [REPOSITORY[:TAG]]
OPTIONS说明:
-
-a:列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);
-
–digests:显示镜像的摘要信息;
-
-f:显示满足条件的镜像;
使用reference作为筛选条件
在使用reference时可以使用通配符
*
进行筛选。需要注意的是,reference只能筛选经过认证的镜像,未经过认证的镜像即使满足条件也不会被筛选。使用before作为筛选条件
-f before 用于列举出本地镜像中指定镜像创建时间之前创建的所有镜像。
使用since作为筛选条件
-f since 用于列举出本地镜像中指定镜像创建时间之后的创建的所有镜像。
-
–format:指定返回值的模板文件;
{{ }}里面的内容与Go语言的模板语法一样。
-
–no-trunc:显示完整的镜像信息;
默认的 docker images 显示的镜像 id 是经过截取后的显示结果,仅显示了前 12 位。使用 --no-trunc 参数后显示的是完整的镜像 id。
-
-q:只显示镜像ID。
docker search
# 用法
docker search [OPTIONS] TERM
OPTIONS说明:
- –limit:对结果进行数量限制;
- –format:指定返回值的模板文件;
- –no-trunc:显示完整的镜像描述;
- -f <过滤条件>:列出收藏数大于指定值的镜像。
从 Docker Hub 查找所有镜像名包含 zookeeper,并且收藏数大于 10 的镜像。
参数说明:
-
NAME: 镜像仓库源的名称
-
DESCRIPTION: 镜像的描述
-
OFFICIAL: 是否 docker 官方发布
-
STARTS: 类似 Github 里面的 star,表示点赞、喜欢的意思。
-
AUTOMATED: 自动构建。
AUTOMATED 表示当前镜像是否是“自动化镜像”。什么是自动化镜像?就是使用 Docker Hub 连接一个包含 Dockerfile 文件(专门构建镜像用的文件)的 GitHub 仓库或 Bitbucket 仓库的源码托管平台,然后 Docker Hub 就会自动根据 Dockerfile 内容构建镜像。这种构建出的镜像会被标记为 AUTOMATED,这种构建镜像的方式称为 Trusted Build(受信构建)。只要 Dockerfile文件内容发生变化,那么 Docker Hub 就会构建出新的镜像。
docker rmi
基本使用
rmi,remove images。该命令用于删除指定的本地镜像。镜像通过:指定。如果省略要删除镜像的 tag,默认删除的是 lastest 版本。
删除多个镜像
docker rmi 命令可一次性删除多个镜像,多个要删除的镜像间使用空格分隔。
通过 ImageID 删除镜像
强制删除镜像
默认情况下,对于已经运行了容器的镜像是不能删除的,必须要先停止并删除了相关容器然后才能删除其对应的镜像。不过,也可以通过添加-f 选项进行强制删除。
删除所有镜像
使用组合命令删除所有镜像。当然,如果不携带-f 选项,则不会删除已打开容器的镜像。
# 命令
[root@centos ~]#docker rmi -f $(docker images -q)
导出 / 导入镜像
导出镜像 save
docker save -o "导出的文件名" "多个镜像名,用空格分隔"
导入镜像 load
docker load 用于将一个 tar 文件导入并加载为一个或多个镜像。
docker load -i "要导入的tar文件"
镜像分层
当构建一个Docker镜像时,Docker会将镜像分解为多个文件系统层,每个层都是只读的,并且包含了文件和目录的变更。这种分层的文件系统结构使得镜像的构建和复用变得非常高效。
上面的示意图展示了一个由3个扩展镜像层和一个基础镜像层组成的Docker镜像。在基础镜像层之上的镜像层称为扩展镜像层。顾名思义,其是对基础镜像层功能的扩展。
在 Dockerfile 中,每条指令都是用于完成某项特定功能的,而每条指令都会生成一个扩展镜像层。
当创建一个新的Docker容器时,Docker会在只读层(扩展镜像层)的基础上添加一个可写层,这个可写层会包含容器运行时所需的文件和目录变更。这样,多个容器就可以共享同一个基础镜像层和多个只读层。分层存储的优势在于镜像的复用和共享。如果有多个镜像使用了相同的基础镜像层,它们可以共享这个基础层,只需在其基础上添加自己的可写层。这样就可以大大减小镜像的体积,并提高镜像的构建和复用效率。
在上面的示意图中,Docker镜像的分层存储通过联合文件系统来实现。可写层是容器运行时的一部分,用于存储容器运行时的变更和数据。只读层包含了基础镜像的内容,它们是只读的,并且可以被多个容器共享。
需要注意的是,这里的分层并不是物理上的分层,而是逻辑上的分层。在底层实现上,Docker使用了联合文件系统(UnionFS)来实现镜像的分层存储。联合文件系统是一种特殊的文件系统,它可以将多个不同的文件系统层合并为一个单一的文件系统。在Docker中,常用的联合文件系统有AUFS、OverlayFS和DeviceMapper等。
镜像摘要
Docker镜像摘要是镜像内容的唯一标识符,它是通过对镜像二进制数据进行哈希运算生成的。摘要可以确保所引用的映像在整个生命周期中始终是相同的。如果镜像内容发生改变,即使名字和标签相同
,摘要也会发生改变。
在 docker pull 镜像结束后会给出该拉取的镜像的摘要 digest。并且通过 docker images --digests 命令也可以查看到镜像的摘要信息。
镜像摘要的作用
摘要的主要作用是区分相同<repository>:<tag>的不同镜像。
例如某个镜像在生产运行过程中发现存在一个 BUG。现对其进行了修复,并使用原标签将其 push 回了仓库,那么原镜像被覆盖。但生产环境中遗留了大量运行中的修复前镜像的容器。此时,通过镜像标签已经无法区分镜像是修复前的还是修复后的了,因为它们的标签是相同的。此时通过查看镜像的 digest 就可以区分出修改前后版本,因为内容发生了变化,digest 一定会变。为了确保再次拉取到的是修复后的镜像,可通过 digest 进行镜像拉取。其用法是:docker pull <repository>@<digest>
分发散列值
在 push 或 pull 镜像时,都会对镜像进行压缩以减少网络带宽和传输时长。但压缩会改变镜像内容,会导致经过网络传输后,镜像内容与其 digest 不相符。为了避免该问题,Docker 又为镜像配置了 Distribution Hash(分发散列值)。在镜像被压缩后立即计算分发散列值,然后使该值随压缩过的镜像一同进行发送。在接收方接收后,立即计算压缩镜像的分发散列值,再与携带的分发散列值对比。如果相同,则说明传输没有问题。