检查Docker Hub上是否已存在image:tag组合

104

作为bash脚本的一部分,我想检查特定docker镜像:标签组合是否存在于docker hub上。此外,它将是一个私有仓库。

即伪代码如下:

tag = something
if image:tag already exists on docker hub:
    Do nothing
else
    Build and push docker image with that tag

2
我相信我们有同样的问题。不确定这是否与您相关,但我们正在运行自己的Docker Registry,并公开了一个API供您使用。https://docs.docker.com/registry/spec/api/#listing-image-tags 我可能会在我的CI构建中添加一步查询API,以便它不会覆盖现有标记。 - Christoffer
10
我很惊讶这不是Docker仓库API的一部分... - kgx
可以使用dip来检查图像是否存在于Docker-registry中。我今天发布了1.0.0版本。 - 030
这里有没有人知道在哪里可以请求Docker的功能?我想向Docker团队发送一个功能请求(或者了解为什么这不适合成为Docker内部的好候选)。 - akki
哇喔,100个赞,我做到了)) - VMAtm
14个回答

139

更新:请参考下文中的不使用Docker的解决方案。

使用Docker

这是我在gitlab中使用docker:stable镜像的解决方案。

登录

docker login -u $USER -p $PASSWORD $REGISTRY

检查是否存在:
docker manifest inspect $IMGNAME:$IMGTAG > /dev/null ; echo $?

Docker执行成功将返回0,失败则返回1。

如果您收到警告:请更新Docker或启用实验性客户端功能

将环境变量DOCKER_CLI_EXPERIMENTAL设置为 enabled (参见以下Matěj的回答)

或者按照原始回答进行调整配置:

echo '{"experimental": "enabled"}' > ~/.docker/config.json

这也将覆盖您的配置。如果这不是一个选项,您需要手动执行或使用 jqsed 或其他可用工具。

无需 Docker 进行测试

如果您没有访问 docker-daemon 的权限,您可以尝试使用 Skopeocrane


32
可惜这仍然是实验性的。 - Mark Tickner
5
实际上,你的第一个命令会覆盖整个配置文件,因此值得通过编辑文件来添加此字符串。 - vladkras
5
懒人必备命令:jq '. + {"experimental": "enabled"}' < ~/.docker/config.json | sponge ~/.docker/config.json。该命令用于在Docker配置文件中添加实验性功能,并使用sponge命令将更改写回到原始配置文件中,避免了常见的重定向问题。 - mgalgs
9
如下面不同的回答中所提到的,您可以通过使用 DOCKER_CLI_EXPERIMENTAL=enabled 来避免更改您的 Docker 配置文件。 - Chris Deacy
5
为什么两年后这仍然是实验性的? - jones-chris
显示剩余8条评论

60

请尝试这个

function docker_tag_exists() {
    curl --silent -f -lSL https://index.docker.io/v1/repositories/$1/tags/$2 > /dev/null
}

if docker_tag_exists library/nginx 1.7.5; then
    echo exist
else 
    echo not exists
fi

更新:

如果使用基于此链接的Docker Registry v2:

# set username and password
UNAME="user"
UPASS="password"

function docker_tag_exists() {
    TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
    curl --silent -f --head -lL https://hub.docker.com/v2/repositories/$1/tags/$2/ > /dev/null
}

if docker_tag_exists library/nginx 1.7.5; then
    echo exist
else 
    echo not exists
fi

12
非docker.io仓库怎么样? - Ilia Sidorenko
3
@WayNo 你用的是什么?请分享文档链接。 - Evgeny Oskin
3
这是一个AWS ECR的脚本: https://gist.github.com/1ec2102c8c0d48592750b1f0b5306cc9 - Olivier Lalonde
10
这个可以用于 ECR:aws ecr describe-images --repository-name <repo> --image-ids imageTag=latest。这将避免编写任何 curl/授权的需求。 - Nikhil Owalekar
2
我已经在几个我目前正在使用的存储库上进行了测试,据我所知,页面大小参数在大约1400之后就无法正常工作了。你为什么会问这些存储库有那么多标签呢?好吧....那是一个好问题。 - Kyle Jones
显示剩余7条评论

48

morty答案基础上,注意到Docker支持使用环境变量设置实验性标志:

DOCKER_CLI_EXPERIMENTAL启用cli的实验性功能(例如enableddisabled

因此代码段变成了:

tag=something
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect $image:$tag >/dev/null; then
    Do nothing
else
    Build and push docker image with that tag
fi

4
我正在使用 Docker CLI Docker version 20.10.7,看起来您不再需要启用 DOCKER_CLI_EXPERIMENTAL - dsaydon

19

最简单的:

docker pull alpine:invalid > /dev/null && echo "success" || echo "failed"

如果图像存在,输出 success;如果不存在,输出 failed

如果在bash脚本中使用,甚至可以将其导出为变量:

enter image description here

请注意,这将获取图像(如果存在)。在使用此解决方案之前,请注意开销成本。


14
请注意,如果容器存在,则会拉取该容器。仅仅为了获取那些信息,甚至拉取小的alpine也是一种负担。更不用说在CI环境中通常有数GB的镜像了。 - Morty
2
没错。我完全同意。我在这个答案中添加了这些信息。感谢您的输入。 - mayankcpdixit
2
这非常适合我的AWS CodeBuild层缓存。我只想尝试拉取它,无论它是否存在都无所谓。它将被构建和缓存。 - nitsujri
如果期望的是图像不存在,那就没问题。此外,在不太可能的情况下,如果图像确实存在,例如当您意外地使用旧标记构建发布时,它将自动将您的新图像覆盖回到以前的状态,这非常有用。 - rustyx

7

我在本地局域网中使用registry:2、私有CA和基本认证搭建了一个Docker私有仓库。

我刚刚查看了官方的Docker API文档(https://docs.docker.com/registry/spec/api/),并想出了这个解决方案,它看起来相当简洁、易于调试、自定义并且适合CICD/脚本编写。

curl --silent -i -u "demoadmin":"demopassword" https://mydockerrepo.local:5000/v2/rancher/pause/manifests/3.1 | grep "200 OK"

--silient可以去除一些额外的文本
-i是让返回代码“200 OK”显示的关键

如果存在则返回代码为0,如果不存在则返回代码为1 你可以使用以下命令验证:
Bash# echo $?


2
我想知道如何通过rest检查私有repo中是否存在标签,而不必执行/v2/imagename/tags/list操作,这是完美的答案!由于某种原因,在stackoverflow上很难找到这个答案。 - Patrick Riordan

3

这里有一个有用的Bash函数:

docker_image_exists() {
  local image_full_name="$1"; shift
  local wait_time="${1:-5}"
  local search_term='Pulling|is up to date|not found'
  local result="$((timeout --preserve-status "$wait_time" docker 2>&1 pull "$image_full_name" &) | grep -v 'Pulling repository' | egrep -o "$search_term")"
  test "$result" || { echo "Timed out too soon. Try using a wait_time greater than $wait_time..."; return 1 ;}
  echo $result | grep -vq 'not found'
}

使用示例:

docker_image_exists elifarley/docker-dev-env:alpine-sshd && \
  echo EXISTS || \
  echo "Image does not exist"

4
这段代码似乎是通过尝试拉取图像来检查其是否存在,这有些过度 - 如果成功,则会更改本地主机上的图像。 - Vince Bowdren
2
它确实尝试拉取它,但如果拉取图像所需的时间超过所选的超时值(默认为5秒),则脚本会中止操作。 - Elifarley
2
好的,说得对。也许在回答中加入一些解释会更有用,说明它做了什么和没有做什么? - Vince Bowdren
2
有人能否编写一个使用API的Bash脚本呢? - Nick D
4
运行此命令后,似乎对一个镜像使用 docker pull 命令会破坏该镜像与正在运行的所有容器之间的关联。请注意,此函数将对正在运行的容器产生副作用,需谨慎使用! - Ilia Sidorenko

3

这是对Evgeny Oskin的解决方案的小改进。当涉及到一个尚未创建的用户repo时,jq会提示"Cannot iterate over null"。为了克服这个问题,可以使用?跳过不存在的块。 以下是针对公共repo的上述解决方案的修改:

最初的回答:

#!/usr/bin/env bash

function docker_image_tag_exists() {
    EXISTS=$(curl -s https://hub.docker.com/v2/repositories/$1/tags/?page_size=10000 | jq -r "[.results? | .[]? | .name == \"$2\"] | any")
    test ${EXISTS} = true
}

if docker_image_tag_exists $1 $2; then
    echo "true"
else
    echo "false"
fi

0

我曾经为了私有的Docker Hub仓库而苦苦挣扎,最终决定编写一个Ruby脚本来解决问题。现在这个脚本已经可以使用了。请随意使用!

#!/usr/bin/env ruby
require 'base64'
require 'net/http'
require 'uri'

def docker_tag_exists? repo, tag
  auth_string = Base64.strict_encode64 "#{ENV['DOCKER_USER']}:#{ENV['DOCKER_PASSWORD']}"
  uri = URI.parse("https://registry.hub.docker.com/v1/repositories/#{repo}/tags/#{tag}")
  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = "Basic #{auth_string}"
  request['Accept'] = 'application/json'
  request['Content-Type'] = 'application/json'
  response = Net::HTTP.start(request.uri.hostname, request.uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  (response.body == 'Tag not found') ? 0 : 1
end

exit docker_tag_exists? ARGV[0], ARGV[1]

注意:在调用此命令时,您需要指定 DOCKER_USER 和 DOCKER_PASSWORD...

DOCKER_USER=XXX DOCKER_PASSWORD=XXX config/docker/docker_hub.rb "NAMESPACE/REPO" "TAG" && echo 'latest'

如果身份验证成功且指定的标签不存在,则此行将打印出“latest”!我在尝试基于当前 git 分支获取标签时在我的 Vagrantfile 中使用了这个命令:

git rev-parse --symbolic-full-name --abbrev-ref HEAD


0

对于本地的Docker注册表,您可以尝试以下方法:

function docker_tag_exists() {
    curl --silent -f -lSL http://localhost:5000/v2/$1/manifests/$2 > /dev/null
}
if docker_tag_exists image_on_local latest; then
    echo exists
else
    echo not exists
fi

0
我已经为此努力了一段时间,我找到了这个解决方案:
docker manifest inspect $registry/$imageName:$tag > /dev/null 2>&1 && echo yes || echo no

如果图像已经存在,则输出“是”,如果不存在,则输出“否”。
这不会拉取图像。我也尝试过“docker image inspect”,但是开始出现错误,所以改用了这种方法。
希望能对你有所帮助!

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