「CONTAINER-IMAGE」- 清理镜像 | Prune Images

该笔记将简单介绍如何“清理”Docker 镜像,以释放系统资源(磁盘),以及 Docker 镜像到底应该如何删除。

除了镜像需要清理之外,容器、卷、网络都需要清理,可以参考 Prune unused Docker objects 文档。

什么是「清理镜像」?

我们讨论的是“清理”,而不是“删除”。这二者有什么区别呢?

“删除”更倾向于一个特定的移除动作。比如,删除某个镜像,删除某个标签。我们通常使用 docker rmi 命令来删除镜像。

“清理”更加的倾向于资源的释放。比如,删除某些未使用的镜像,以释放系统存储空间。可以使用 docker image prune 命令来清理镜像。

相关文档

docker docs/Reference/docker image prune

相关术语

未用镜像

「未用镜像」表示尚未在容器中分配或使用它。例如,当运行docker ps -a时,该命令将列出所有已退出以及当前正在运行的容器。在任何容器中使用的任何镜像都是“使用过的镜像”,而那些从未被创建过容器的镜像就是「未用镜像」。

悬空镜像

What is a dangling image and what is an unused image?
Docker Tip #31: How to Remove Dangling Docker Images

  • 没有被其他镜像引用
  • docker images的输出中,列TAG<none>
  • docker images的输出中,列REPOSITORY为可能有值
  • 「悬空镜像」是与任何有标签镜像无关的层。它们不再有用,除了占用磁盘空间。

    查看「悬空镜像」命令:docker images –filter dangling=true

    下面是「悬空镜像」示例:

    其他镜像不会引用「悬空镜像」,因此可以安全的删除「悬空镜像」。

    删 | 删除镜像

    docker image rm

    使用docker image prune命令可以清除「悬空镜像」。

    使用docker image prune -a命令可以清除「悬空镜像」以及「未用镜像」。

    删除单个镜像 | docker image rm, docker image remove, docker rmi

    使用 docker rmi 与 docker image rm 命令来删除本地镜像(只能删除本地镜像,不能删除在镜像仓库中的镜像):

    # 通过 Image ID 删除
    docker rmi fd484f19954f
    
    # 下面这两个是等价的,因为 latest 是默认的标签
    docker rmi test1:latest
    docker rmi test1
    

    1)如果镜像具有多个标签,并且将标签作为删除参数,则命令仅删除标签;
    2)如果镜像具有唯一标签,命令将删除标签,然后删除镜像;

    删除被容器使用的镜像

    如果镜像具有正在使用的容器,则无法删除,需要使用 -f 选项,将删除容器与镜像:

    docker image rm --force "REPOSITORY:TAG"
    

    删除所有镜像

    删除全部镜像 | docker image prune –all –force

    如果要删除所有镜像可以使用如下命令(可以使用 docker images--filter 选项进行高级过滤):

    # 下面的两个命令是等价的
    docker image rm $(docker image ls -a -q)
    docker rmi $(docker images -a -q)
    
    # 删除有容器的镜像
    docker image rm -f $(docker image ls -a -q)
    

    删除 IMAGE ID 相同的所有镜像

    当镜像具有多个标签时,如果使用 IMAGE ID 删除(docker rmi fd484f19954f),会产生如下错误:

    Error: Conflict, cannot delete image fd484f19954f because it is tagged in multiple repositories, use -f to force 2013/12/11 05:47:16 Error: failed to remove one or more images
    

    方法一、此时可以使用 -f 选项时,这将删除所有标签并删除镜像:

    docker rmi --force fd484f19954f
    

    方法二、使用命令 grep 进行过滤,将结果作为 docker rmi 参数以进行删除;

    方法三、使用 docker inspect 命令,命令输出的 RepoTags 字段将显示相关标签,以用于删除;

    删除远程镜像

    在镜像仓库(如 Docker Hub 中)中的镜像,目前不能通过命令删除,要在镜像仓库中删除。通过单击按钮来完成删除操作,不涉及特别复杂的内容,所以简单略过;

    # 08/08/2019 今天在 Docker v19.03 (current) 看到了 docker registry rmi 命令,可以从 Registry 中移除镜像,目前出于实验性质;

    … image has dependent child images …

    Find the dependent child images on Docker

    问题描述:
    在删除某个镜像(docker rmi a990d6e2b083)的时候,提示如下错误:

    Error response from daemon: conflict: unable to delete d86795370e7b (cannot be forced) - image has dependent child images
    

    问题原因:
    该错误提示删除的镜像有依赖的子镜像;

    解决办法:

    # --------------------------------------------------------- # 简单的做法
    
    docker image inspect --format='{{.Id}} {{.Parent}}' \
        $(docker image ls --all --filter since=a990d6e2b083 -q) | grep a990d6e2b083
    
    # 上述命令:
    #1(docker image ls)找到 a990d6e2b083 之后的镜像
    #2(docker image inspect)然后获取这些镜像的父镜像
    #3(grep)通过过滤父镜像,找到的就是引用该镜像的子镜像;
    
    # --------------------------------------------------------- # 最大的问题在于:子镜像还有子镜像
    
    # 其实要做的事情很简单。通常这个问题是由于有悬空镜像引用了该镜像,所以删除所有“悬空”的镜像即可;
    # 「悬空镜像」可以放心的删除
    
    docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
    
    
    # --------------------------------------------------------- # 通过递归进行删除
    
    # 找到要删除镜像的子镜像的子镜像的子镜像
    # https://stackoverflow.com/questions/38118791/can-t-delete-docker-image-with-dependent-child-images/38119847#38119847#answer-53037893
    
    recursive_remove_image() {
      for image in $(docker images --quiet --filter "since=${1}") # 找到这个镜像之后的镜像。因为子镜像是在这之后的;
      do
        if [ $(docker history --quiet ${image} | grep ${1}) ]   # 查看该子镜像的构建历史,是否包含该镜像。如果是,那就应该删除;
        then
          recursive_remove_image "${image}"                     # 进行递归,删除应用这个镜像的子镜像;
        fi
      done
      # 执行删除
      echo "Removing: ${1}"
      docker rmi -f ${1}
    }
    recursive_remove_image "<image-id>"
    

    … unable to delete … (must be forced) – image is referenced in multiple repositories …

    macos – Cannot remove images even though no container is running – Stack Overflow

    Error response from daemon: conflict: unable to delete 920d876d235e (must be forced) – image is referenced in multiple repositories

    简而言之,该 ID 指向的镜像具有多个 Repository / Tag,需要全部删除之后,才能执行 docker image rm 920d876d235e 进行删除。

    或,通过 -f 选项,来进行强制删除;

    场景 | 根据前缀删除 | Prefix

    Filter docker images by repository name – Stack Overflow

    docker image rm $(docker image ls --filter=reference='registry.example.com/meta-pdn/*:develop-*' -q)
    
    docker images "registry.example.com/meta-pdn/*"
    docker images "registry.example.com/meta-pdn/*/*/*"
    
    docker images "registry.example.com/meta-pdn/**"           # 但其并不支持 ** 来匹配多级,即该语法无效
    

    场景 | 删除悬空镜像 | dangling | <none>

    docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
    
    # 还可以通过 DIGEST 来删除。在 REPOSITORY 与 DIGEST 之间,使用 @ 分隔;
    # 因为使用 DIGEST 拉取的镜像是没有 TAG 的,所以会用到这种方法;
    docker rmi test/busybox@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
    

    场景 | 根据日期删除 | CreatedAt

    我们暂时未找到简单方法,需要通过脚本进行实现。

    • docker images –format json
    • 获取 CreatedAt 字段,并进行判断是否删除