1. 概述
在这篇教程中,我们将学习如何获取Docker中给定父级图像的所有子级图像列表。首先,我们将看到何时需要获取此类信息。然后,我们将根据所使用的Docker构建器检索它。
2. 动机
当一个Docker镜像基于另一个镜像构建时,前者称为子级,后者称为父级。特别地,当我们遇到Docker阻止删除父级的情况时,获取给定父级的所有子级图像往往很有帮助:
$ docker rmi 645c04d243f1
Failed to remove image (a3550c143d4e): Error response from daemon: conflict: unable to delete a3550c143d4e (cannot be forced) - image has dependent child images
在这里,Docker提示我们删除父级a3550c143d4e的子级图像。使用–force标志绕过错误并不能解决问题。因此,找到这些子级图像变得至关重要。接下来的几节将解决这个问题。
获取给定父级镜像的依赖子级图像取决于所使用的Docker构建器。从Docker Desktop和Docker Engine 23.0起,BuildKit是Linux镜像的新默认构建器。之前的版本使用的是旧构建器。我们将区分这两种构建器。
3. 使用Docker旧版构建器
旧版构建器是低于23.0版本的Docker的默认构建器。无论如何,我们可以通过设置*DOCKER_BUILDKIT=0.*来选择它。
让我们在上一节中重现错误。
3.1. 案例研究
首先,我们将定义一个父级镜像:
$ DOCKER_BUILDKIT=0 docker build -t parent -<<EOF
FROM scratch
LABEL kind=parent
EOF
重要的是要注意,旧版构建器将在未来的发布中被移除:
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0
environment-variable.
我们需要记住这个警告,并准备好迁移到BuildKit。
接下来,让我们基于父级创建一个子级镜像:
$ DOCKER_BUILDKIT=0 docker build -t child -<<EOF
FROM parent:latest
LABEL kind=child
EOF
现在我们已经准备好了所有内容,让我们检索父级ID:
$ docker images ls --filter reference=parent
REPOSITORY TAG IMAGE ID CREATED SIZE
parent latest a3550c143d4e 4 minutes ago 0B
尝试删除父级时,我们会遇到最初的错误:
$ docker rmi 645c04d243f1
Failed to remove image (a3550c143d4e): Error response from daemon: conflict: unable to delete a3550c143d4e (cannot be forced) - image has dependent child images
这是由于旧版构建器的工作方式。它缓存用于构建其他镜像的图像,并阻止它们的删除。这些信息保存在子级图像的Parent元数据中:
$ docker inspect --format '{{json .Parent}}' child
"sha256:a3550c143d4e25331167344afeb842b21780ef7d3f20dabe748390d89c16cf8b"
旧版构建器跟踪了父级。我们将使用此信息根据父级镜像ID获取依赖子级图像。
3.2. 获取依赖子级图像
让我们创建一个get_children_of.sh脚本来获取依赖子级图像:
#!/bin/sh
set -e
parent=$1
echo "Dependent Child IDs of $parent "
docker inspect --format='{{.Id}} {{.Parent}}' $(docker images --all --quiet --filter since=$parent) \
awk -v parent=$parent '{ if($2 == parent) print $1 }'
让我们分解一下发生了什么。首先,脚本以参数形式接受父级镜像ID。接下来,我们列出在父级之后构建的所有图像:
docker images --all --quiet --filter since=$parent
–all标志很重要,用于列出即使是隐藏的图像。–quiet标志仅显示镜像ID。接下来,我们检查这些图像的元数据:
docker inspect --format='{{.Id}} {{.Parent}}'
命令docker images的结果用作docker inspect的参数。我们将输出格式缩小到仅获取子级图像ID和父级ID。接着,我们使用*awk*打印依赖子级图像:
awk -v parent=$parent '{ if($2 == parent) print $1 }'
这里,条件检查至关重要,仅打印相关父级ID的子级图像。
最后,脚本获取我们所需的父级图像a3550c143d4e的依赖子级图像:
$ ./get_children_of.sh sha256:a3550c143d4e25331167344afeb842b21780ef7d3f20dabe748390d89c16cf8b
Dependent Child IDs of sha256:a3550c143d4e25331167344afeb842b21780ef7d3f20dabe748390d89c16cf8b
sha256:ddfecb5979b759ff56c1a730e5fc957e963cfd5bbe2108c72244b65182857bab
传递长形式ID到脚本是至关重要的。我们可以通过传递–no-trunc标志描述父级镜像来获取它。
有了这些子级图像ID,我们可以删除它们以解决我们的初始错误。
4. 使用BuildKit
BuildKit自23.0版本起成为Linux镜像的默认Docker构建器。它带来了许多从旧版构建器改进的功能。
特别是,它不缓存父级图像。这意味着当我们使用BuildKit时,我们将不会遇到之前遇到的错误。
让我们激活Buildkit在我们之前的案例研究中,使用DOCKER_BUILDKIT=1。另一种实现方法是通过更新Docker守护程序配置文件*/etc/docker/daemon.json*:
{
"features":{
"buildkit":true
}
}
现在让我们重新创建父级和子级镜像。首先,我们将定义父级镜像:
$ DOCKER_BUILDKIT=1 docker build -t parent -<<EOF
FROM scratch
LABEL kind=parent
EOF
接下来,让我们基于父级创建子级镜像:
$ DOCKER_BUILDKIT=0 docker build -t child -<<EOF
FROM parent:latest
LABEL kind=child
EOF
我们将立即注意到,我们无法从子级镜像中获取任何关于父级图像的信息:
$ docker inspect --format '{{json .Parent}}' child
""
BuildKit不会设置Parent字段。删除父级图像不会导致任何错误。
$ docker rmi sha256:a3550c143d4e25331167344afeb842b21780ef7d3f20dabe748390d89c16cf8b
Untagged: parent:latest
Deleted: sha256:a3550c143d4e25331167344afeb842b21780ef7d3f20dabe748390d89c16cf8b
无论使用哪种构建器,我们都可以始终从系统中删除废弃和未使用的图像:
$ docker image prune --all --force
使用–force标志在不需要确认的情况下非常有用。
5. 总结
在这篇文章中,我们看到了如何在Docker中获取依赖子级图像的列表。首先,我们讨论了它的主要用途。然后,我们了解了根据使用的Docker构建器来实现结果的方法。