如何查找正在运行的Docker容器中所有图像标签?

23

我有一堆在服务器上运行的Docker容器,我对所有容器都使用了"latest"标签或者没有标签。现在我想要冻结这些镜像的版本,但是我不知道我何时拉取了这些镜像,所以我无法确定"latest"指的是哪个版本。docker ps只显示容器使用了"latest"或没有标签,就像这样:

# docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS               NAMES
371d6675888b        node:latest            "npm start"              6 days ago          Up 2 hours                              project_xyz_1
ca5a75425a34        selenium/node-chrome   "/usr/bin/nohup go..."   6 days ago          Up 2 hours                              project_xyz-chrome_1
...

我使用的所有图像都是来自Docker Hub的公共图像。
我原本想使用 "docker ps" 显示的十六进制ID作为所有容器的ID,但后来发现这些ID是容器ID而不是镜像ID。
也许可以获取所有正在运行的容器的镜像ID或哈希值,然后扫描所有匹配的标签或类似的内容?
Docker版本:18.09.1,构建4c52b90
编辑:
已经有一些回答显示如何获取镜像的ID(摘要),但我需要以某种方式找到这些镜像的实际标签。
经过一些研究,我发现Docker Hub有一个API,并且有一种方法可以获取给定图像的所有标签,还有一种方法可以获取给定图像+标签的摘要。在查看了API和来自stackoverflow的许多示例之后,我得出了以下结论: (它还包括获取本地映像的摘要所需的代码,从下面的答案中获得)
function getDigestByImageNameWithTag () {
    TARGET_IMAGE_NAME_WITH_TAG="$1" # works with and without tag
    docker image inspect --format '{{index .RepoDigests 0}}' "$TARGET_IMAGE_NAME_WITH_TAG" | cut -d '@' -f2
}

function getTagsByDigest () {
    TARGET_IMAGE_NAME="$1"
    TARGET_DIGEST="$2"

    # prepend the image name with "library/" if it doesn't contain a slash
    if [[ $TARGET_IMAGE_NAME != *"/"* ]]; then
        TARGET_IMAGE_NAME="library/$TARGET_IMAGE_NAME"
    fi

    # get authorization token for the given image name
    TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$TARGET_IMAGE_NAME:pull" | jq -r .token)

    # find all tags for the given image name
    ALL_TAGS=$(curl -s -H "Authorization: Bearer $TOKEN" https://index.docker.io/v2/$TARGET_IMAGE_NAME/tags/list | jq -r .tags[])

    # itate over all these tags
    for TAG in ${ALL_TAGS[@]}; do
        # get image digest
        DIGEST=$(curl -s -D - -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" https://index.docker.io/v2/$TARGET_IMAGE_NAME/manifests/$TAG | grep Docker-Content-Digest | cut -d ' ' -f 2)
        # if the tag matches the given digest
        if [[ $TARGET_DIGEST = $DIGEST ]]; then
            # "return" the tag
            echo "$TAG"
        fi
    done
}

function getContainerImageNames () {
    docker inspect $(docker ps  | awk '{print $2}' | grep -v ID) | jq .[].RepoTags | grep -v "\[" | grep -v "\]" | grep " " | cut -d '"' -f2 | cut -d '/' -f2-
}


# get all image names of all local containers
IMGS_WITH_TAG=$(getContainerImageNames)
# iterate of those image names
for IMAGE_NAME_WITH_TAG in ${IMGS_WITH_TAG[@]}; do
    # get the digest of the current iteration's IMAGE_NAME_WITH_TAG
    DIGEST=$(getDigestByImageNameWithTag $IMAGE_NAME_WITH_TAG)
    echo "TARGET_DIGEST: $DIGEST" 
    # get the raw image name without the tag
    IMAGE_NAME=$(echo "$IMAGE_NAME_WITH_TAG" | cut -d ':' -f1)
    # find all tags for this image that have the same digest
    MATCHING_TAGS=$(getTagsByDigest $IMAGE_NAME $DIGEST)
    echo "Image: $IMAGE_NAME_WITH_TAG"
    echo "Image digest: $IMAGE_NAME"
    echo "Image tags with same digest: "
    echo "$MATCHING_TAGS"
    echo "-----------------------------"
done

很不幸,似乎需要花费很长时间才能完成。我不确定是否做错了什么,但这是我能想到的最好方法。

有没有什么办法可以使它正常工作?


docker ps | awk '{print $2}' | grep -v ID 等价于 docker ps --format '{{.ID}}'。你能分享一种测试脚本并为某些标签填充一些 Docker 映像的方法吗? - KamilCuk
你只需要安装Docker和jq,然后至少启动一个容器。例如:docker run -d -t --name=test node:11 bash。在这种情况下,具有相同摘要的标签将是11.15.0-stretch、11.15-stretch、11-stretch、11.15.0、11.15、11。所以脚本应该返回这些内容。 - Forivin
因为你比较了不必要的标签,只有比较本地系统上正在使用的标签才能节省时间,你可以查看我的更新答案。 - Adiii
错误的数组使用。添加一个shebang,然后将您的脚本粘贴在这里:http://www.shellcheck.net/ - Cyrus
@Cyrus,Shellcheck 没有显示任何错误。它只是警告可能存在不必要的拆分和通配符扩展,但我认为这个脚本没有问题。 - Forivin
4个回答

8

我认为这是一种更好的方法,而不需要检查容器,因为docker ps已经打印出创建容器的docker镜像标签。

docker inspect $(docker ps  | awk '{print $2}' | grep -v ID) | jq .[].RepoTags

首先,这个命令获取正在运行的容器列表,然后检查每个正在运行容器所使用的镜像,并使用 jq 获取该镜像所有存储库标签。
以下是输出结果。

docker images tags and name

更新: 你可以使用 skopeo 实现相同的功能,虽然也可以使用 API ,但需要付出更大的努力。不过既然有了 skopeo,你无需安装它,只需运行容器并获取其结果,或者安装它,脚本都支持。
running_container=$(docker ps  | awk '{print $2}' | grep -v ID) 
echo "running container: $running_container"
for image in $running_container
do
local_tag=$(echo "$image" | awk -F":" '{print $2}')
if [ -z $local_tag ]; then
# if tag is empty then tag is latest
local_tag="latest"
image="$image":"$local_tag"
fi
local_digest=$(docker inspect $image | jq '.[].RepoDigests[]' | awk -F"@" '{print $2}' | tr -d '"')
echo "Local digest is:" $local_digest
remote_digest=$(docker run --rm --env image=$image alexeiled/skopeo:latest ash -c "skopeo inspect docker://docker.io/$image" | jq '.Digest' | tr -d '"' )
echo $remote_digest 

# option2 install the skopeo on your local system
# remote_digest=$(skopeo inspect docker://docker.io/$image | jq '.Digest' | tr -d '"')
echo "Remote digest is : "$remote_digest

if [ "${local_digest}" == "${remote_digest}" ]; then
echo "local image is up to date with remote"
else
echo "Remote image is updated; please run docker pull $image"
fi
done

enter image description here


我喜欢这个!但是这样做,你有没有任何保证,例如alpine:latest当前运行的镜像与Docker Hub上当前可用的alpine:latest镜像相同? - Guillaume Barré
嗨@Nirekin,我刚刚检查了一下,请看这里。 - Adiii
@Forivin,你试过我的答案了吗? - Adiii

4
如果您从注册表中拉取了该镜像,则图像检查中的RepoDigest字段将具有sha256引用:
docker ps --format '{{.Image}}' | xargs \
  docker image inspect --format '{{if .RepoDigests}}{{index .RepoDigests 0}}{{end}}'

对于像node:latest这样的单个镜像在您的主机上,看起来像这样:

docker image inspect --format '{{index .RepoDigests 0}}' node:latest

相同标签名称的内容推送到仓库中,其摘要无法更改。当您从仓库拉取更新后的标签时,将看到此摘要更新。


1
好的,那么我会有一个摘要,但我想要像node:10.7.3这样的东西,而不是node@sha256:0aec4c38431c386877888c612a9340b1132bbeb4dc4af5b5ff54b9310d48e5a1,因为对于后者,当我的版本中发现漏洞时,我将不知道是否需要更新。 - Forivin
@Forivin,你本地没有这些信息。你只拉取了最新的指向静态sha256的内容。所有其他标签都在注册服务器上,如果要查询每个标签的远程仓库摘要,需要进行一些工作,特别是考虑到多架构镜像和可能会更新的标签。 - BMitch
@Forivin 如果一个仓库使用语义化版本控制,那么最好的做法是为你的应用程序指定最适合的依赖项,例如 node:10 而不是 node:10.7.3,然后定期拉取新的镜像并重新部署你的容器。 - BMitch
但是使用 Docker Hub API 检索信息应该是可能的,对吗?我知道我可以使用 https://registry.hub.docker.com/v2/repositories/library/node/tags/?page=1 等来获取有关所有可能的标记的一些信息。只是缺少摘要信息。 - Forivin
1
@Forivin 可能性:是。琐碎程度:不是。 - BMitch
好的,我现在尝试做这个,但似乎不起作用或者需要很长时间。你介意看一下我添加到问题中的代码吗? - Forivin

0
Docker 镜像和容器都有一个ID,对于正在运行的容器,您可以获取其镜像的ID,然后拉取与给定ID相对应的镜像。
首先,您需要在所有正在运行的容器上使用“docker inspect”命令,以获取基于哪个镜像的容器的 sha256 ID。
“docker inspect”会返回“Image”下的镜像ID:
{
  "Id": "6de053a2afa4499471c5e5c2afe0b0d83c9c7e50fc7e687fb63a7ebfd2bff320",
  ...
  },
  "Image": "sha256:26eb6780e26887a6838684a549562c0404fd85c55f71e0af6c79a4da5505d2a7",
  ....
}

然后,您只需通过摘要(不可变标识符)提取这些图像即可。

$ docker pull node@sha256:the-image-digest-here

或者

$ docker pull node@sha256:26eb6780e26887a6838684a549562c0404fd85c55f71e0af6c79a4da5505d2a7

如果你幸运的话,与这些摘要相对应的图像仍然可用于Docker Hub。

在那之后,如果你仍然面对着“latest”图像,我会建议你将这些图像重新命名为适当的名称和标记,并将它们拉到你自己的Docker Hub存储库中,以便能够直接使用它们...


0

这只会获取图像ID。我还需要一种方法来获取提取的图像ID所对应的标签。 - Forivin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接