Как загрузить образ докера с помощью HTTP API, используя учетные данные концентратора докеров

Итак, у меня есть частный репозиторий в Docker Hub, и я пытаюсь загрузить изображение (blobs) вручную с помощью HTTP API.

Теперь есть некоторые проблемы

Но в Docker HUB API нет API для получения списка больших двоичных объектов из тега, а затем для его загрузки.

Есть апи реестра докеров, но там мой логин-пароль не работает. Что делать?

2 ответа

Для изображения вы сначала извлекаете манифест и анализируете его для списка больших двоичных объектов, которые вам нужно извлечь. Всем этим API нужны одни и те же заголовки авторизации, используемые для перечисления тегов. Я собираюсь использовать regctl из моего проекта regclient для запросов к локальному реестру, но вы также можете использовать curl для Hub, как показано ниже.

      $ 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преобразует изображение в смолу манифестов и больших двоичных объектов. Это выводится в формате OCI Layout, и если вы извлечете только один манифест изображения (а не мультиплатформенный манифест), он также будет включать необходимые файлы для docker loadимпортировать смолу.

Есть апи реестра докеров, но там мой логин-пароль не работает. Что делать?

Вероятно, это зависит от того, как вы их снабжаете. Я не пробовал это с частным репозиторием в 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/^(([^",]|"([^"]|\")*")*),/\1\n/; t1'
        }
        header_www_authenticate=`echo "$header_www_authenticate" | split_into_lines`
        extract_value() {
            sed -E 's/^[^=]+="(([^"]|\")*)"$/\1/; s/\\(.)/\1/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

Или, если быть более точным, он извлекает конфигурацию изображения и слои. Дело в том, что ты собираешься с ними делать? Вы можете попробовать создать tar-архив, похожий на созданный . Который является то, чтов основном делает. Но в идеале для этого надо знать, что именно и делать.

Чтобы дать вам несколько ссылок на исходный код,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.goиvendor/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/изображение/tarexport/save.go#L187

Большая часть кода находится вimage/tarexport/save.go.

Но видимо разработчик этим не заморачивался, и это (docker-drag) вроде работает.

В случае частных репозиториев Docker Hub вам, вероятно, потребуется добавить-u user:passwordк$realm?service=$service&scope=$scopeзапрос.

Пара замечаний по использованию API:

  • Реестр представляет собой набор репозиториев. Подумайте о Гитхабе. Обычно имена репозиториев имеют формуuser/name(напримерnginxproxy/nginx-proxy). А вот с официальными репозиториями (которые все на самом деле начинаются сlibrary/) первый сегмент может быть опущен (library/ruby->ruby). Также может быть только один сегмент (например, в частных реестрах), и иногда только первая часть называется репозиторием (пример ). Или второй.
  • Репозиторий — это набор изображений. Некоторые из них помечены. Изображения без тегов обычно появляются, когда вы отправляете новую версию изображения с тем же тегом.
  • Старая, но, может быть, несколько актуальная актуальная статья (по крайней мере, начало).
  • URL-адрес реестра Docker Hub см. в этом ответе .
  • Не ожидайте, что все в спецификации будет работать. Например, Docker Hub не реализует/v2/_catalogмаршрут.
Другие вопросы по тегам