参考文献

  • 极客时间 <<Kubernetes入门实战课>>

镜像

  • Docker镜像是静态的分层管理的文件组合,镜像底层的实现依赖于联合文件系统(UnionFS)

镜像是如何构建的

  • 构建过程不是由Docker客户端进行的,而是将整个目录的文件上传到Docker守护进程并在那里进行的.Docker客户端和守护进程不要求在同一台机器上.
  • 如果要在一台Linux系统中使用Docker,客户端就运行在你的宿主操作系统上,但是守护进程运行在一个虚拟机内.由于构建目录中的文件都被上传到了守护进程中,如果包含了大量的大文件而且守护进程不在本地运行,上传过程会花费更多的时间.
    • 注意: 不要在构建目录中包含任何不需要的文件,这样会减慢构建的速度-尤其当Docker守护进程运行在一个远端机器的时候.

镜像分层

  • 镜像不是一个大的二进制块,而是由多层组成,不同镜像可能会共享分层,这会让存储和传输变得更加高效.所有组成基础镜像的分层只会被存储一次.拉取镜像的时候,Docker会独立下载一层.一些分层可能已经存储在机器上了,所以Docker只会下载未被存储的分层.
  • 构建镜像时,Dockerfile中每一条单独的指令都会创建一个新层.
  • 容器镜像是由多个只读的Layer构成的,同一个Layer可以被不同的镜像共享,减少了存储和传输的成本

镜像基本操作

搜索镜像

1
docker search [options] image_name
  • --no-trunc: 显示完整的镜像名称
  • --filter=stars=3: 列出收藏数不小于指定值的镜像
  • --filter=automated=true: 只列出automated build类型的镜像

拉取镜像

1
docker pull [registry]/[respository]/[image_name]:[tag]
  • registry: 注册服务器
  • respository: 镜像仓库
  • image_name: 镜像名称
  • tag: 镜像标签,不指定则使用latest
  • 拉取先从本地搜索,若本地搜索不到在到远程仓库拉取

查看镜像

1
docker images
  • -a: 列出所有镜像(含中间镜像层)
  • -q: 只显示镜像ID
  • --digests: 显示摘要信息
  • --no-trunc: 不截断输出,显示完整的镜像ID
  • 查询指定的镜像docker image ls [image_name]或者docker images|grep [image_name]

重命名镜像

1
docker tag [source_image_name][:tag] [target_image][:tag]
  • 示例:docker tag cowboy:latest mycowboy:latest

删除镜像

1
2
docker rmi [imange_name]
docker image rm [image_name]
删除版本号为none的镜像
1
docker images -a | grep none | awk '{print $3}' | xargs docker rmi

构建镜像

  • 通过docker commit -m="描述信息" -a="作者" 容器id 目标镜像名: [TAG]

  • 通过docker build

    1
    2
    3
    #其中 -t 对镜像进行命名,一般的命名法:仓库名字/镜像名字:版本号
    #注意:其中 .号,代表当前目录下的dockerfile文件
    docker build -t 仓库名字/镜像名字:版本号 .
多阶段构建
  • dockerfile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # syntax=docker/dockerfile:1
    FROM ubuntu AS base
    RUN echo "base"

    FROM base AS stage1
    RUN echo "stage1"

    FROM base AS stage2
    RUN echo "stage2"
  • docker build --no-cache -f Dockerfile --target stage2 .

镜像的导入与导出

  • 镜像压缩打包 (主机上进行操作),有两种方式 docker savedocker loaddocker export 与 docker import

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 将现有的镜像压缩打包
    # docker save -o <image_save_path/image_file.tar> <image>
    docker save nginx | gzip > nginx_test_image.tar.gz
    docker save nginx | pv -s $(docker inspect -f '{{.Size}}' nginx) | gzip > nginx_test_image.tar.gz
    # 使用 pigz(多线程压缩工具)替代 gzip 可加速压缩
    docker save nginx | pv | pigz > nginx_test_image.tar.gz

    nohup sh -c 'docker save nginx | pv -s $(docker inspect -f "{{.Size}}" nginx) | gzip > nginx.tar.gz' > logs.log 2>&1 &
    # 压缩的镜像解压
    docker load -i nginx_test_image.tar.gz
    # 加载某个目录下所有的镜像压缩包 find /path/to/images -name '*.tar' -exec docker load -i {} \;
    # 进行查看
    docker images
docker savedocker export的区别
  • 对于docker save方法,会保存该镜像的所有历史记录;
  • 对于docker export 方法,不会保留历史记录,即没有commit历史;
  • docker save保存的是镜像(image),docker export保存的是容器(container);
  • docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像;
  • docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称.

Docker Hub上镜像命名的规则

  • 通常来说,镜像标签的格式是应用的版本号加上操作系统.

  • 版本号基本上都是主版本号+次版本号+补丁号的形式,有的还会在正式发布前出rc版(候选版本,release candidate).而操作系统的情况略微复杂一些,

    • Alpine、CentOS的命名比较简单明了,就是数字的版本号,像这里的 alpine3.15 ,而Ubuntu、Debian则采用了代号的形式.比如Ubuntu 18.04是 bionic,Ubuntu 20.04是 focal,Debian 9是 stretch,Debian 10是 buster,Debian 11是 bullseye.

    • 另外,有的标签还会加上 slimfat,来进一步表示这个镜像的内容是经过精简的,还是包含了较多的辅助工具.通常 slim 镜像会比较小,运行效率高,而 fat 镜像会比较大,适合用来开发调试.

  • nginx:1.21.6-alpine,表示版本号是1.21.6,基础镜像是最新的Alpine.

  • redis:7.0-rc-bullseye,表示版本号是7.0候选版,基础镜像是Debian 11.

  • node:17-buster-slim,表示版本号是17,基础镜像是精简的Debian 10.

构建多架构镜像

使用步骤

  • 使用Docker buildx来构建镜像,若系统中没有buildx可以在Github Release下载对应平台的二进制文件,并将其放置到对应目录

    OS Binary name Destination folder
    Linux docker-buildx $HOME/.docker/cli-plugins
    macOS docker-buildx $HOME/.docker/cli-plugins
    Windows docker-buildx.exe %USERPROFILE%\.docker\cli-plugins
  • 开启binfmt_misc来运行非本地架构的Docker 镜像

    1
    docker run --privileged --rm tonistiigi/binfmt --install all
  • 将默认 Docker 构建器切换为多架构构建器

    1
    docker buildx create --use --name mybuilder
  • 查看新的多架构构建器是否生效,需执行docker buildx ls

    1
    2
    3
    4
    5
    6
    # docker buildx ls
    NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
    mybuilder * docker-container
    mybuilder0 unix:///var/run/docker.sock running v0.21.1 linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
    default docker
    default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
  • 构建多架构镜像,通过命令 docker buildx build -t <image-name> --platform=linux/arm64,linux/amd64 . --push,构建一个支持 arm64amd64 架构的多架构镜像,并推送至仓库

遇到的问题

  • docker buildx build时报错ERROR: failed to solve: failed to push xxxxxxx:9091/test/test:latest: failed to do request: Head "https://xxxxxxx:9091/v2/test/test/blobs/sha256:a569e7431bce80cb7abd105bb0b25850948575d985ffd93967acd347eab3b1a2": http: server gave HTTP response to HTTPS client

  • 解决方法

    • 删除原有builder,设置支持HTTP,重新创建builder

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      docker buildx rm mybuilder

      cat <<EOF > ./config.toml
      debug = true
      # root is where all buildkit state is stored.
      root = "/var/lib/buildkit"
      # insecure-entitlements allows insecure entitlements, disabled by default.
      insecure-entitlements = [ "network.host", "security.insecure"]

      [registry."你的仓库地址"]
      http = true
      insecure = true

      EOF

      docker buildx create --use --name=mybuilder --config=./config.toml --bootstrap
    • 然后重新构建