该笔记将简单介绍如何“清理”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 images –filter dangling=true 下面是「悬空镜像」示例: 其他镜像不会引用「悬空镜像」,因此可以安全的删除「悬空镜像」。 使用docker image prune命令可以清除「悬空镜像」。 使用docker image prune -a命令可以清除「悬空镜像」以及「未用镜像」。 使用 docker rmi 与 docker image rm 命令来删除本地镜像(只能删除本地镜像,不能删除在镜像仓库中的镜像): 1)如果镜像具有多个标签,并且将标签作为删除参数,则命令仅删除标签; 如果镜像具有正在使用的容器,则无法删除,需要使用 -f 选项,将删除容器与镜像: 删除全部镜像 | docker image prune –all –force 如果要删除所有镜像可以使用如下命令(可以使用 docker images 的 --filter 选项进行高级过滤): 当镜像具有多个标签时,如果使用 IMAGE ID 删除(docker rmi fd484f19954f),会产生如下错误: 方法一、此时可以使用 -f 选项时,这将删除所有标签并删除镜像: 方法二、使用命令 grep 进行过滤,将结果作为 docker rmi 参数以进行删除; 方法三、使用 docker inspect 命令,命令输出的 RepoTags 字段将显示相关标签,以用于删除; 在镜像仓库(如 Docker Hub 中)中的镜像,目前不能通过命令删除,要在镜像仓库中删除。通过单击按钮来完成删除操作,不涉及特别复杂的内容,所以简单略过; # 08/08/2019 今天在 Docker v19.03 (current) 看到了 docker registry rmi 命令,可以从 Registry 中移除镜像,目前出于实验性质; Find the dependent child images on Docker 问题描述: 问题原因: 解决办法: 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 选项,来进行强制删除; Filter docker images by repository name – Stack Overflow 我们暂时未找到简单方法,需要通过脚本进行实现。
Docker Tip #31: How to Remove Dangling Docker Images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 7848fcc70e7b 4 days ago 362MB
删 | 删除镜像
删除单个镜像 | docker image rm, docker image remove, docker rmi
# 通过 Image ID 删除
docker rmi fd484f19954f
# 下面这两个是等价的,因为 latest 是默认的标签
docker rmi test1:latest
docker rmi test1
2)如果镜像具有唯一标签,命令将删除标签,然后删除镜像;删除被容器使用的镜像
docker image rm --force "REPOSITORY:TAG"
删除所有镜像
# 下面的两个命令是等价的
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 相同的所有镜像
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
docker rmi --force fd484f19954f
删除远程镜像
… image has dependent child images …
在删除某个镜像(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 …
场景 | 根据前缀删除 | Prefix
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 字段,并进行判断是否删除