# 镜像

# 标识符

  • 每个 Docker 镜像有三种标识符:

    • <image_id> :取自其十六进制哈希值的开头 n 位。
      • 因为未指定完整的哈希值,只能用于在本机标识镜像,不能在 pull、push 时标识镜像。
    • <image>@<digest> :镜像名加完整的哈希值。
      • 默认采用 SHA256 哈希算法,因此 <digest> 格式为 sha256:<hash>
    • <image>[:tag] :镜像名加标签,由用户自定义。
      • tag 通常用于表示镜像的版本号。
      • 省略 tag 时,默认取值为 latest 。
        • 制作镜像时,通常将 latest 标签指向最新一个版本,便于用户拉取。
        • 正式使用时,建议不要拉取镜像的 latest 版本,否则在不同时间拉取的 latest 版本可能不同。
  • 悬空镜像(dangling images):一些只有 ID 的镜像,没有镜像名和标签,而是名为 <none>:<none>

# 查看

docker
      image
            ls                # 显示本机的镜像(默认不显示悬空镜像)。可简写为 docker images 命令
                -a            # 显示所有的镜像
            history <image>   # 显示镜像的构建步骤(按时间倒序排列),包含每个步骤增加的 layer 大小
                --no-trunc

            rm <image>...     # 删除本机的镜像(只能删除未被容器使用的),可简写为 docker rmi 命令
            prune             # 删除所有悬空镜像
                -a            # 删除所有未被容器使用的镜像

      tag <image>[:tag] <image>[:tag] # 给镜像添加另一个镜像名和标签。如果已有镜像占用目标标签,则将其标签改为 <none>

# 拉取

docker
      pull    <image>[:tag]         # 从镜像仓库拉取镜像,即下载镜像
          --platform <os/arch>      # 拉取指定平台的镜像。默认只拉取匹配本机操作系统的镜像
      push    <image>[:tag]         # 推送镜像到镜像仓库
      search  <image>               # 在远程镜像仓库中搜索某个镜像
      login  <domain>               # 登录一个镜像仓库。登录凭证会保存在 $HOME/.docker/config.json 中
          -u <username>
          -p <password>
  • 例:

    docker pull nginx:1.20              # 拉取镜像
    docker pull nginx                   # 相当于 docker pull nginx:latest
    docker tag  nginx:1.20 nginx:test   # 添加标签
    
  • 例:拉取镜像的过程

    [root@CentOS ~]# docker pull nginx:1.20
    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   # 检验整个镜像的哈希值
    Status: Downloaded newer image for nginx:1.20
    docker.io/library/nginx:1.20
    
    • 拉取每个 layer 时,分为多个步骤:
      Downloading           # 下载 layer 的压缩包
      Download complete     # 下载完毕
      Extracting            # 解压 layer 并导入,存储在宿主机上
      Pull complete         # 拉取完毕
      
  • 可以将宿主机上存储的 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 镜像由一组 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       # 一个以镜像哈希值为名的 JSON 文件,记录该镜像的详细配置
    c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/
    c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/VERSION
    c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/json
    c0b073121bb2a6106dae6af85ade7274253f26626661e6e3cb20b0fa7fb59475/layer.tar
    manifest.json                                                               # 镜像的内容清单,记录了镜像名、tag、JSON 配置文件的路径、各个 layer 的路径
    repositories
    
    • docker pull 命令会根据 manifest.json 文件拉取镜像的各个 layer 。如果本机已存在,则不必下载。
    • 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 \u003cdocker-maint@nginx.com\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>[:tag]
    
    • 每次 commit 时,会在原镜像外部加上一层新的 layer 。因此 commit 次数越多,镜像的体积越大。
  • 编写 Dockerfile 文件,然后根据它构建镜像。