【问题标题】:How to download docker image using HTTP API using docker hub credentials如何使用 docker hub 凭证使用 HTTP API 下载 docker 映像
【发布时间】:2022-10-08 13:13:16
【问题描述】:

所以我在 docker hub 有一个私有存储库 我正在尝试使用 HTTP API 手动下载图像(blob)。

现在有一些问题

但是 docker HUB api 中没有 API 可以从标签中获取 blob 列表然后下载它。

有一个 docker registry api,但我的用户名密码不起作用。 该怎么办?

【问题讨论】:

  • 如果您研究工具的实现,How do I download Docker images without using the pull command? 可能会有所帮助。
  • 我找不到一个需要用户名密码的
  • 你认为 docker 注册表真的支持 http 下载吗?如果 api 不支持,那么你不能。我建议您从使用 http 的自托管私有注册表开始。然后你可以使用一些工具如wireshark来捕获流量。

标签: docker docker-registry dockerhub


【解决方案1】:

对于图像,您首先提取清单并将其解析为您需要提取的 blob 列表。所有这些 API 都需要用于列出标签的相同授权标头。我将使用 regclient 项目中的 regctl 来查询本地注册表,但您也可以对 Hub 使用 curl,如下所示。

$ regctl tag ls localhost:5000/library/alpine
3
3-bkup-20210904
3.10
3.11
3.12
3.13
3.14
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
latest

$ regctl manifest get localhost:5000/library/alpine:latest --format body | jq .
{
  "manifests": [
    {
      "digest": "sha256:14b55f5bb845c7b810283290ce057f175de87838be56f49060e941580032c60c",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      },
      "size": 528
    },
    {
      "digest": "sha256:40f396779ba29da16f29f780963bd4ad5b7719e3eb5dec04516d583713256aa8",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v6"
      },
      "size": 528
    },
    {
      "digest": "sha256:392d9d85dff31e34d756be33579f05ef493cb1b0edccc36a11b3295365553bfd",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v7"
      },
      "size": 528
    },
    {
      "digest": "sha256:4fb53f12d2ec18199f16d7c305a12c54cda68cc622484bfc3b7346a44d5024ac",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm64",
        "os": "linux",
        "variant": "v8"
      },
      "size": 528
    },
    {
      "digest": "sha256:e8d9cf28250078f08e890a3466efbefda68a8feac03cc4076d3ada3397370d6e",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "386",
        "os": "linux"
      },
      "size": 528
    },
    {
      "digest": "sha256:d860569a59af627dafee0b0f2b8069e31b07fbdaebe552904dbaec28047ccf64",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      },
      "size": 528
    },
    {
      "digest": "sha256:6640b198347e5bf1e9a9dc5fc864e927154275dc31f3d26193b74350a5c94c9f",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "s390x",
        "os": "linux"
      },
      "size": 528
    }
  ],
  "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
  "schemaVersion": 2
}

$ regctl manifest get localhost:5000/library/alpine@sha256:14b55f5bb845c7b810283290ce057f175de87838be56f49060e941580032c60c --format body | jq .
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1472,
    "digest": "sha256:e9adb5357e84d853cc3eb08cd4d3f9bd6cebdb8a67f0415cc884be7b0202416d"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 2812636,
      "digest": "sha256:3d243047344378e9b7136d552d48feb7ea8b6fe14ce0990e0cc011d5e369626a"
    }
  ]
}

$ regctl blob get localhost:5000/library/alpine sha256:e9adb5357e84d853cc3eb08cd4d3f9bd6cebdb8a67f0415cc884be7b0202416d | jq .
{
  "architecture": "amd64",
  "config": {
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
      "/bin/sh"
    ],
    "Image": "sha256:e211ac20c5c7aaa4ed30d5553654d4679082ec48efcb4d164bac6d50d62653fd",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": null
  },
  "container": "b6ba94212561a8075e1d324fb050db160e25035ffcfbbe5b410e411e2b7000e2",
  "container_config": {
    "Hostname": "b6ba94212561",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
      "/bin/sh",
      "-c",
      "#(nop) ",
      "CMD ["/bin/sh"]"
    ],
    "Image": "sha256:e211ac20c5c7aaa4ed30d5553654d4679082ec48efcb4d164bac6d50d62653fd",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": {}
  },
  "created": "2022-03-17T04:01:59.188838147Z",
  "docker_version": "20.10.12",
  "history": [
    {
      "created": "2022-03-17T04:01:58.883733237Z",
      "created_by": "/bin/sh -c #(nop) ADD file:cf4b631a115c2bbfbd81cad2d3041bceb64a8136aac92ba8a63b6c51d60af764 in / "
    },
    {
      "created": "2022-03-17T04:01:59.188838147Z",
      "created_by": "/bin/sh -c #(nop)  CMD ["/bin/sh"]",
      "empty_layer": true
    }
  ],
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:5e03d8cae8773cb694fff1d55da34a40d23c2349087ed15ce68476395d33753c"
    ]
  }
}

$ regctl blob get localhost:5000/library/alpine sha256:3d243047344378e9b7136d552d48feb7ea8b6fe14ce0990e0cc011d5e369626a | tar -tvzf - | head
drwxr-xr-x 0/0               0 2022-03-16 16:15 bin/
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/arch -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/ash -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/base64 -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/bbconfig -> /bin/busybox
-rwxr-xr-x 0/0          824984 2022-02-02 13:21 bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/cat -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/chgrp -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/chmod -> /bin/busybox
lrwxrwxrwx 0/0               0 2022-03-16 16:15 bin/chown -> /bin/busybox
...

如果您尝试使用 curl 执行此操作,请举几个例子:

获取令牌(特定于 Docker Hub,每个注册表可能有不同的身份验证方法和服务器):

token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" 
        | jq -r '.token')

标签:

curl -H "Authorization: Bearer $token" 
     -s "https://registry-1.docker.io/v2/${repo}/tags/list" | jq .

清单:

api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
curl -H "Accept: ${api}" -H "Accept: ${apil}" 
     -H "Authorization: Bearer $token" 
     -s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}" | jq .

和斑点:

curl -H "Authorization: Bearer $token" 
     -s -L -o - "https://registry-1.docker.io/v2/${repo}/blobs/${digest}"

如果您想导出所有内容以便以后导入,regctl image export 会将图像转换为清单和 blob 的 tar。这是 OCI 布局格式的输出,如果您只拉取单个图像清单(而不是多平台清单),它还将包含 docker load 导入 tar 所需的文件。

【讨论】:

    【解决方案2】:

    有一个 docker registry api,但我的用户名密码不起作用。该怎么办?

    这可能取决于您如何供应它们。我没有尝试使用 Docker Hub 上的私有存储库,但这里有一个脚本,它从 Docker Hub 下载公共图像,以及从私有注册表中下载图像:

    #!/usr/bin/env bash
    set -eu
    image=$1
    creds=${2-}
    
    # https://github.com/moby/moby/blob/v20.10.18/vendor/github.com/docker/distribution/reference/normalize.go#L29-L57
    # https://github.com/moby/moby/blob/v20.10.18/vendor/github.com/docker/distribution/reference/normalize.go#L88-L105
    registry=${image%%/*}
    if [ "$registry" = "$image" ] 
    || { [ "`expr index "$registry" .:`" = 0 ] && [ "$registry" != localhost ]; }; then
        registry=docker.io
    else
        image=${image#*/}
    fi
    if [ "$registry" = docker.io ] && [ "`expr index "$image" /`" = 0 ]; then
        image=library/$image
    fi
    if [ "`expr index "$image" :`" = 0 ]; then
        tag=latest
    else
        tag=${image#*:}
        image=${image%:*}
    fi
    if [ "$registry" = docker.io ]; then
        registry=https://registry-1.docker.io
    elif ! [[ "$registry" =~ ^localhost(:[0-9]+)$ ]]; then
        registry=https://$registry
    fi
    
    r=`curl -sS "$registry/v2/" 
        -o /dev/null 
        -w '%{http_code}:%header{www-authenticate}'`
    http_code=`echo "$r" | cut -d: -f1`
    curl_args=(-sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json')
    if [ "$http_code" = 401 ]; then
        if [ "$registry" = https://registry-1.docker.io ]; then
            header_www_authenticate=`echo "$r" | cut -d: -f2-`
            header_www_authenticate=`echo "$header_www_authenticate" | sed -E 's/^Bearer +//'`
            split_into_lines() {
                sed -Ee :1 -e 's/^(([^",]|"([^"]|")*")*),/
    /; t1'
            }
            header_www_authenticate=`echo "$header_www_authenticate" | split_into_lines`
            extract_value() {
                sed -E 's/^[^=]+="(([^"]|")*)"$//; s/\(.)//g'
            }
            realm=$(echo "$header_www_authenticate" | grep '^realm=' | extract_value)
            service=$(echo "$header_www_authenticate" | grep '^service=' | extract_value)
            scope=repository:$image:pull
            token=`curl -sS "$realm?service=$service&scope=$scope" | jq -r .token`
            curl_args+=(-H "Authorization: Bearer $token")
        else
            curl_args+=(-u "$creds")
        fi
    fi
    manifest=`curl "${curl_args[@]}" "$registry/v2/$image/manifests/$tag"`
    config_digest=`echo "$manifest" | jq -r .config.digest`
    config=`curl "${curl_args[@]}" -L "$registry/v2/$image/blobs/$config_digest"`
    layers=`echo "$manifest" | jq -r '.layers[] | .digest'`
    echo "$layers" | 
        while IFS= read -r digest; do
            curl "${curl_args[@]}" -L "$registry/v2/$image/blobs/$digest" | wc -c
        done
    

    用法:

    $ ./download-image.sh hello-world
    $ ./download-image.sh library/hello-world
    $ ./download-image.sh docker.io/library/hello-world
    $ ./download-image.sh myregistry.com/hello-world testuser:testpassword
    $ ./download-image.sh localhost:5000/hello-world
    

    或者更准确地说,它检索图像配置和图层。问题是,你打算用它们做什么?您可以尝试创建一个类似于docker save 生成的tar 存档。 docker-drag 基本上就是这样做的。但理想情况下,您应该知道 docker pulldocker save 究竟做了什么。

    为了给你一些源代码的链接,docker pull(服务器部分)或多或少从这里开始:

    https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image.go#L37
    https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image_routes.go#L78
    https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_pull.go#L54
    https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_pull.go#L130
    https://github.com/moby/moby/blob/v20.10.18/distribution/pull.go#L52

    大部分相关代码都在distribution/pull_v2.go(高级部分)中。

    执行 http 请求的代码在 vendor/github.com/docker/distribution/registry/client/auth/session.govendor/github.com/docker/distribution/registry/client/repository.go 中。

    docker save:

    https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image.go#L31
    https://github.com/moby/moby/blob/v20.10.18/api/server/router/image/image_routes.go#L160
    https://github.com/moby/moby/blob/v20.10.18/daemon/images/image_exporter.go#L16
    https://github.com/moby/moby/blob/v20.10.18/image/tarexport/save.go#L187

    大部分代码在image/tarexport/save.go

    但是好吧,显然docker-drag 的开发人员并不关心它,它(docker-drag)似乎工作。

    如果是私有 Docker Hub 存储库,您可能需要将 -u user:password 添加到 $realm?service=$service&scope=$scope 请求中。

    关于使用 API 的几点说明:

    • 注册表是存储库的集合。想想 GitHub。通常存储库名称的格式为user/name(例如nginxproxy/nginx-proxy)。但是对于官方存储库(实际上都以library/ 开头),第一段可能会被省略(library/ruby -> ruby)。也可能只有一个片段(例如在私有注册表中),并且有时仅第一部分就称为存储库(example)。
    • 存储库是图像的集合。其中一些被标记。当您使用相同的标签推送新版本的图像时,通常会出现未标记的图像。
    • 一个旧的,但可能有点最新的relevant article(至少是开始)。
    • 有关 Docker Hub 的注册表 URL,请参见 this answer
    • 不要期望spec 中的所有内容都能正常工作。例如。 Docker Hub 没有实现/v2/_catalog 路由。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-24
      • 1970-01-01
      • 2019-03-08
      • 2019-12-10
      • 2019-11-12
      • 2020-06-10
      • 1970-01-01
      相关资源
      最近更新 更多