# 镜像
# 标识符
每个 Docker 镜像有三种标识符:
<image_id>
:镜像 id ,取自镜像配置文件的 SHA256 哈希值的开头 n 位。- 这里未指定完整的哈希值,只能在本机标识镜像,不能在 docker pull、docker push 时标识镜像。
<image_name>@<digest>
:镜像名加哈希值。- 这里的 digest 是指镜像 manifest 的哈希值,格式为
sha256:<hash>
。例如:nginx@sha256:11bf3beaa91524d60236685b2c05063c248d22b60247d63b9c35712cf761f7ed
- 这里的 digest 是指镜像 manifest 的哈希值,格式为
<image_name>[:tag]
:镜像名加标签,由用户自定义。- 使用
<image_name>[:tag]
作为镜像的标识符,比<image_id>
和<image_name>@<digest>
更方便用户记忆,但 tag 可能被修改、删除,不一定可靠。 - 镜像名(image_name)是一个字符串,可包含字符
[0-9A-Za-z._-]
。通常用斜杆 / 划分多个层级。例如:nginx:1.23 docker.io/library/nginx:1.23
- 标签(tag)用于区分同一 image_name 的镜像的不同版本。例如:
nginx:1.23 nginx:1.24
- 使用
image_tag 的取值通常来自 git_tag 或 build_date 。
- 省略 image_tag 时,默认指定 image_tag 为 latest 。
- 例如执行
docker pull nginx
相当于docker pull nginx:latest
。 - 有的开源软件制作镜像时,会将 latest 标签指向最新一个版本,便于用户拉取。
- 严格来说,用户不应该拉取 latest 版本的镜像,否则在不同时间拉取的 latest 版本可能不同,即镜像的哈希值不同。
- 例如执行
- 悬空镜像(dangling images):一些只有哈希值的镜像。如果查看其 image_name 和 tag ,则会显示
<none>:<none>
。
- 省略 image_tag 时,默认指定 image_tag 为 latest 。
# manifest
- 在 hub.docker.com 查看一个镜像时,会显示一份镜像索引(image index),说明该镜像支持运行在哪些平台(platform)。
- 这里 platform 是指主机的操作系统和 CPU 架构,比如 linux/amd64 、linux/arm64 。
- 一般的开源软件会为不同 platform 的用户分别编译二进制程序,制作成不同哈希值的镜像。因此 image index 中包含多个镜像。
- image index 中每个镜像都有一份 JSON 格式的镜像清单(image manifest),记录该镜像的简要信息。例如:
{ "schemaVersion": 2, // manifest 的语法版本 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", // 整个 manifest 的数据类型 "config": { "mediaType": "application/vnd.docker.container.image.v1+json", // config 的数据类型 "size": 6844, "digest": "sha256:faff56ad2ef574b8260716706d56e0277520d17103743c3e94355391005e4cdb" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 76097157, "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc" }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 122, "digest": "sha256:77c5a601c050c59fe3abd066973abc67f6278e7ee3bf21f004e991bd82900fb4" } ] }
- 执行 docker pull 命令时,会先获取镜像的 manifest ,然后根据 manifest 拉取镜像的各个 layer ,并检查镜像的 config、layers 的 size、digest 是否正确。
- 上例中的 mediaType 采用 docker 标准,说明该镜像由 docker 命令制作,兼容 OCI 标准,但不是 OCI 标准。
- 严格来说,OCI 镜像不需要声明整个 manifest 的 mediaType ,且 config、layers 的 mediaType 采用 OCI 标准,例如
"mediaType": "application/vnd.oci.image.config.v1+json"
。
- 严格来说,OCI 镜像不需要声明整个 manifest 的 mediaType ,且 config、layers 的 mediaType 采用 OCI 标准,例如
- OCI 标准的 manifest 原本专用于描述容器镜像。但利用 mediaType 字段,可用 manifest 描述其它二进制工件。
- 例如 helm 用
"mediaType": "application/vnd.cncf.helm.config.v1+json"
描述 Chart 。使得 Chart 可以存储到符合 OCI 标准的镜像仓库。
- 例如 helm 用
# 查看
docker
image
ls # 显示本机的镜像(默认不显示悬空镜像)。可简写为 docker images 命令
-a # 显示所有的镜像
history <image> # 显示镜像的构建步骤(按时间倒序排列),包含每个步骤增加的 layer 大小
--no-trunc
rm <image>... # 删除本机的镜像(只能删除未被容器使用的),可简写为 docker rmi 命令
prune # 删除所有悬空镜像
-a # 删除所有未被容器使用的镜像
tag <image> <image_name>[:tag] # 给镜像添加另一个镜像名和标签。如果已有镜像占用目标标签,则将其标签改为 <none>
inspect <image> # 查看本机某个镜像的详细信息
manifest inspect <image> # 查看远程仓库中某个镜像的清单,但只能识别 docker 标准的 mediaType
buildx imagetools inspect <image> # 查看远程仓库中某个镜像的清单
# 拉取
docker
login <domain> # 登录一个镜像仓库。登录凭证会保存在 $HOME/.docker/config.json 中
-u <username>
-p <password>
pull <image> # 从镜像仓库拉取镜像,即下载镜像
--platform <os/arch> # 拉取指定平台的镜像。默认只拉取兼容本机的镜像
push <image> # 推送镜像到镜像仓库
search <name> # 按名称搜索 hub.docker.com 中的镜像
例:
docker pull nginx:1.23 # 拉取镜像 docker pull nginx # 相当于 docker pull nginx:latest docker tag nginx:1.23 nginx:test # 添加标签
例:拉取镜像的过程
[root@CentOS ~]# docker pull nginx:1.23 1.20: Pulling from library/nginx # 开始从镜像仓库拉取镜像 e5ae68f74026: Pull complete # 拉取镜像中的一个 layer ,如果本机已存在则不会拉取 2dc3587e7d0c: Pull complete b8258363a4a3: Pull complete 963807cfb489: Pull complete 5faf54adf667: Pull complete 07bd53fd2d21: Pull complete Digest: sha256:71a1217d769cbfb5640732263f81d74e853f101b7f2b20fcce991a22e68adbc7 # 镜像的 manifest 的哈希值 Status: Downloaded newer image for nginx:1.23 docker.io/library/nginx:1.23
- 拉取每个 layer 时,分为多个步骤:
Downloading # 下载 layer 的压缩包 Download complete # 下载完毕 Extracting # 解压 layer 并导入,存储在宿主机上 Pull complete # 拉取完毕
- 拉取每个 layer 时,分为多个步骤:
可以将宿主机上存储的 docker 镜像,推送到镜像仓库服务器进行存储。
- 默认采用官方镜像仓库 docker.io ,允许未登录用户 pull 公开镜像。
- 官方还提供了 Web 页面 hub.docker.com ,用于搜索镜像。
- 用户也可以自己部署仓库服务器,比如 harbor 。
- 为了区分不同的镜像仓库,需要在镜像名的前面加上仓库地址:
docker pull harbor.test.com/project1/nginx # 使用指定的镜像仓库,格式为 <仓库域名>/<命名空间>/<镜像名>
- 默认采用官方镜像仓库 docker.io 的 library 命名空间中的镜像,因此以下三种写法的指向相同:
docker pull nginx docker pull docker.io/nginx docker pull docker.io/library/nginx
- 默认采用官方镜像仓库 docker.io ,允许未登录用户 pull 公开镜像。
# 导出
- 一个 Docker 镜像在宿主机上存储为一些零散文件,包含一组 layer 和配置文件,可用以下命令导出:
docker save -o image.tar <image>... # 将镜像打包成 tar 格式 docker save <image>... | gzip > image.tgz # 打包并压缩,大概压缩到 40% 大小 docker load -i image.tar # 导入镜像
- 例:
[root@CentOS ~]# docker save -o nginx.tar nginx:latest [root@CentOS ~]# ls -lh total 131M -rw-------. 1 root root 131M Mar 28 16:04 nginx.tar [root@CentOS ~]# tar -tf nginx.tar 28d499c51144128e64b6ffefa6c714bbfaf3e55772b080d1b0636f1971cb3203/ # 每个目录对应一层 layer 。目录名是此时导出文件的哈希值,并不等于 layer.tar 的哈希值 28d499c51144128e64b6ffefa6c714bbfaf3e55772b080d1b0636f1971cb3203/VERSION # 该 layer 的格式规范,目前为 1.0 28d499c51144128e64b6ffefa6c714bbfaf3e55772b080d1b0636f1971cb3203/json # 该 layer 的配置文件,记录了其 id、父级 layer 的 id、构建时的 container_config 28d499c51144128e64b6ffefa6c714bbfaf3e55772b080d1b0636f1971cb3203/layer.tar # 该 layer 包含的所有文件 40aef34ac16b8c7eee6da1869452f5c9b9963ab583415d4999565738c719ded9/ 40aef34ac16b8c7eee6da1869452f5c9b9963ab583415d4999565738c719ded9/VERSION 40aef34ac16b8c7eee6da1869452f5c9b9963ab583415d4999565738c719ded9/json 40aef34ac16b8c7eee6da1869452f5c9b9963ab583415d4999565738c719ded9/layer.tar 456351a127e9a9ce4cc79f7f6ad9f401d1714e514780f1603fa0b263119e329b/ 456351a127e9a9ce4cc79f7f6ad9f401d1714e514780f1603fa0b263119e329b/VERSION 456351a127e9a9ce4cc79f7f6ad9f401d1714e514780f1603fa0b263119e329b/json 456351a127e9a9ce4cc79f7f6ad9f401d1714e514780f1603fa0b263119e329b/layer.tar 9000127bc2e7878a10491bb7a16a4b5874e4bdf6a01952d14211fad55defdd0a/ 9000127bc2e7878a10491bb7a16a4b5874e4bdf6a01952d14211fad55defdd0a/VERSION 9000127bc2e7878a10491bb7a16a4b5874e4bdf6a01952d14211fad55defdd0a/json 9000127bc2e7878a10491bb7a16a4b5874e4bdf6a01952d14211fad55defdd0a/layer.tar b526b761d738d1fba0774ea5af56ae1e664c812c6ce75743d74773cb3867bf7b/ b526b761d738d1fba0774ea5af56ae1e664c812c6ce75743d74773cb3867bf7b/VERSION b526b761d738d1fba0774ea5af56ae1e664c812c6ce75743d74773cb3867bf7b/json b526b761d738d1fba0774ea5af56ae1e664c812c6ce75743d74773cb3867bf7b/layer.tar b8cf2cbeabb915843204ceb7ef0055fecadd55c2b0c58ac030e01fe75235885a.json # 该镜像的配置文件。该文件的 SHA256 哈希值被用作文件名、image id c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/ c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/VERSION c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/json c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/layer.tar manifest.json # 该镜像的清单 repositories
- 镜像的配置文件
<sha256>.json
的内容示例:{ "architecture": "amd64", "config": { // 记录该镜像的配置,主要由 Dockerfile 决定 "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.20.2", "NJS_VERSION=0.7.0", "PKG_RELEASE=1~bullseye"], "Cmd": ["nginx", "-g", "daemon off;"], "Image": "sha256:8e9a1b312fca0584850ce522438997f952010118d95f408add7eed34b8a2462d", "Volumes": null, "WorkingDir": "", "Entrypoint": ["/docker-entrypoint.sh"], "OnBuild": null, "Labels": { "maintainer": "NGINX Docker Maintainers \[email protected]\u003e" }, "StopSignal": "SIGQUIT" }, "container": "d9a5e6a8c2e78750b6e1cf3e1c62542d0d9bac5e5d714744a652974b20b3f987", // 记录构建镜像时的最后一个中间容器 "container_config": { "Hostname": "d9a5e6a8c2e7", ... }, "history": [{ // 记录该镜像的构建步骤,按时间顺序排列 "created": "2021-11-17T02:20:41.91188934Z", "created_by": "/bin/sh -c #(nop) ADD file:a2405ebb9892d98be2eb585f6121864d12b3fd983ebf15e5f0b7486e106a79c6 in / " }, ... { "created": "2021-11-17T10:39:44.423437008Z", "created_by": "/bin/sh -c #(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]", "empty_layer": true }], "os": "linux", "rootfs": { // 记录组成该镜像的各个 layer 的哈希值。创建容器时需要按先后顺序载入这些 layer ,生成 RootFS 文件系统 "type": "layers", "diff_ids": ["sha256:e1bbcf243d0e7387fbfe5116a485426f90d3ddeb0b1738dca4e3502b6743b325", "sha256:72e7342f59d8d99e69f1a39796e9023fee99f2b9c72bfe75cd7cc8c86b43c918", ...] } }
- 镜像的配置文件
# 制作
Docker 镜像主要有两种制作方式:
将一个容器提交为镜像:
docker commit <container> <image_name>[:tag]
- 每次 commit 时,会在原镜像外部加上一层新的 layer 。因此 commit 次数越多,镜像的体积越大。
编写 Dockerfile 文件,然后根据它构建镜像。