Is that possible to get image ID from Docker Registry V2?
Asked Answered
V

4

7

When an image has been pushed to registry V2, does the image ID will be pushed to registry as well? Is that possible to get image ID for a certain repository from V2 registry?

Voltage answered 10/6, 2016 at 23:4 Comment(0)
M
16

If the image was pushed with Docker Client 1.10 or above, you can obtain the image ID from the registry with

GET /v2/<image>/manifests/<tag> 

Your request must include the header

Accept: application/vnd.docker.distribution.manifest.v2+json

In the response, the image ID will be in the Content-Docker-Digest Response header.

Mweru answered 17/8, 2016 at 12:11 Comment(4)
Thanks, that's what I'm doing now.Voltage
Hi there, can you include a formatted curl request as an example? I am having issues with the v2 api.Prolocutor
the answer by @fightingdu below has the a good curl request in it. You just have to sub in your values for $server, $repo, and $tagMweru
Content-Docker-Digest does not contain the image id. The image id is the digest of the image configuration, see github.com/docker/distribution/issues/… ... you can get it from config.digest in newer registry protocol versions.Mistress
G
7

[ Updated answer in 2020-11 ]

There are lots of sha's to deal with when using docker images. Each filesystem layer has a unique sha, and that gets updated when the layer is compressed and stored on the registry. There's a sha computed on the json object representing the image configuration, and this is what you see when you lookup the image id locally with:

$ docker inspect busybox:latest --format '{{ .ID }}'
sha256:f0b02e9d092d905d0d87a8455a1ae3e9bb47b4aa3dc125125ca5cd10d6441c9f

There's a sha for a single platform's manifest on the registry. This manifest includes a pointer to the image configuration and the various layers. And there's also a sha for the multi-platform manifest that points to each of those individual platforms. Lets dig into those. First here's a script I'll use to query the Docker Hub registry with anonymous requests:

$ cat manifest-v2.sh
#!/bin/sh

ref="${1:-library/ubuntu:latest}"
sha="${ref#*@}"
if [ "$sha" = "$ref" ]; then
  sha=""
fi
wosha="${ref%%@*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
  tag="latest"
fi
api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
        | jq -r '.token')
curl -H "Accept: ${api}" -H "Accept: ${apil}" \
     -H "Authorization: Bearer $token" \
     -s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}" | jq .

Next, lets pull the manifest for the latest tag:

$ ./manifest-v2.sh library/busybox:latest
{
  "manifests": [
    {
      "digest": "sha256:c9249fdf56138f0d929e2080ae98ee9cb2946f71498fc1484288e6a935b5e5bc",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      },
      "size": 527
    },
    {
      "digest": "sha256:a7c572c26ca470b3148d6c1e48ad3db90708a2769fdf836aa44d74b83190496d",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "platform": {
        "architecture": "arm",
        "os": "linux",
        "variant": "v5"
      },
      "size": 527
    },
...

The result is a manifest list, lets pull the sha for just the amd64 platform:

$ ./manifest-v2.sh library/busybox@sha256:c9249fdf56138f0d929e2080ae98ee9cb2946f71498fc1484288e6a935b5e5bc
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1493,
    "digest": "sha256:f0b02e9d092d905d0d87a8455a1ae3e9bb47b4aa3dc125125ca5cd10d6441c9f"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 764619,
      "digest": "sha256:9758c28807f21c13d05c704821fdd56c0b9574912f9b916c65e1df3e6b8bc572"
    }
  ]
}

In there we have a single layer and the config object, and the sha on that config with the application/vnd.docker.container.image.v1+json media type matches our image id sha256:f0b02e9d092d9.... This is easier to pick out with the v2 output from the registry which is why the manifest script inserts the Accept headers.

I'd caution that I don't believe it is guaranteed to match, YMMV, IANAL, etc. I could foresee docker adding/removing fields that would get ignored by the different versions of the docker engine. So I'd avoid any hard dependency of this behavior.

Note that this sha is not the same as the sha you would use to uniquely identify an image on the registry for pulling images. For that, you want the manifest sha, and if there's a manifest list you should use that. You can see this sha with just a HEAD request when resolving the tag (using the -I parameter to curl):

$ cat manifest-v2-head.sh 
#!/bin/sh

ref="${1:-library/ubuntu:latest}"
sha="${ref#*@}"
if [ "$sha" = "$ref" ]; then
  sha=""
fi
wosha="${ref%%@*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
  tag="latest"
fi
# echo "Looking up repo $repo, ${sha:-$tag}"
# api="application/vnd.oci.image.index.v1+json"
# api="application/vnd.oci.image.manifest.v1+json"
api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
        | jq -r '.token')
curl -H "Accept: ${api}" -H "Accept: ${apil}" \
     -H "Authorization: Bearer $token" \
     -I -s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}" 

$ ./manifest-v2-head.sh library/busybox:latest
HTTP/1.1 200 OK
Content-Length: 2080
Content-Type: application/vnd.docker.distribution.manifest.list.v2+json
Docker-Content-Digest: sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d"
Date: Wed, 18 Nov 2020 14:38:59 GMT
Strict-Transport-Security: max-age=31536000
RateLimit-Limit: 250;w=21600
RateLimit-Remaining: 250;w=21600

From the above, you could docker pull busybox@sha:a9286d....

All of the commands here are Docker Hub specific. For working with other registries, I have a regctl command available in my regclient project that handles auth and API calls to other registries:

$ regctl image manifest busybox
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1493,
    "digest": "sha256:f0b02e9d092d905d0d87a8455a1ae3e9bb47b4aa3dc125125ca5cd10d6441c9f"
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "size": 764619,
      "digest": "sha256:9758c28807f21c13d05c704821fdd56c0b9574912f9b916c65e1df3e6b8bc572"
    }
  ]
}

$ regctl image digest busybox --list
sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d

$ regctl image digest busybox
sha256:c9249fdf56138f0d929e2080ae98ee9cb2946f71498fc1484288e6a935b5e5bc

Original answer:

The image id itself is not stored in any registry API accessible location, here's an example using a local registry:

bash$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
...

bash$ docker tag busybox localhost:5000/busybox

bash$ docker push localhost:5000/busybox
The push refers to a repository [localhost:5000/busybox]
5f70bf18a086: Pushed 
...

bash$ curl http://localhost:5000/v2/busybox/tags/list
{"name":"busybox","tags":["latest"]}

bash$ curl http://localhost:5000/v2/busybox/manifests/latest
{
   "schemaVersion": 1,
   "name": "busybox",
   "tag": "latest",
   "architecture": "amd64",
   "fsLayers": [
      {
         "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
      },
      {
         "blobSum": "sha256:385e281300cc6d88bdd155e0931fbdfbb1801c2b0265340a40481ee2b733ae66"
      }
   ],
   "history": [
      {
         "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"156e10b83429\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":[\"sh\"],\"Image\":\"56ed16bd6310cca65920c653a9bb22de6b235990dcaa1742ff839867aed730e5\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"container\":\"5f8098ec29947b5bea80483cd3275008911ce87438fed628e34ec0c522665510\",\"container_config\":{\"Hostname\":\"156e10b83429\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [\\\"sh\\\"]\"],\"Image\":\"56ed16bd6310cca65920c653a9bb22de6b235990dcaa1742ff839867aed730e5\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2016-03-18T18:22:48.810791943Z\",\"docker_version\":\"1.9.1\",\"id\":\"437595becdebaaaf3a4fc3db02c59a980f955dee825c153308c670610bb694e1\",\"os\":\"linux\",\"parent\":\"920777304d1d5e337bc59877253e946f224df5aae64c72538672eb74637b3c9e\"}"
      },
      {
         "v1Compatibility": "{\"id\":\"920777304d1d5e337bc59877253e946f224df5aae64c72538672eb74637b3c9e\",\"created\":\"2016-03-18T18:22:48.262403239Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:47ca6e777c36a4cfffe3f918b64a445c8f32300deeb9dfa5cc47261bd7b75d21 in /\"]}}"
      }
   ],
   "signatures": [
      {
         "header": {
            "jwk": {
               "crv": "P-256",
               "kid": "FIFX:SJRD:AQHW:MCFX:M6WC:LXI2:3VO2:4LFW:UHDZ:QUN7:OLX4:6WGD",
               "kty": "EC",
               "x": "Xm8wJTzw3nb--rGoD3dxjKffikj7Snb9dHW-qGbqSAM",
               "y": "GnATS--7lVcA_-jQGuDKTtjhmnGgvBrx8rLdlPOJV3U"
            },
            "alg": "ES256"
         },
         "signature": "f8NVzOF6ujm_0COedniGCGL_q3KsTfKFM9T8ZZDf2MSIMJ3TYoR_s795NqdEy8yWaoLuT2LoI0BCEsuOTZUhCw",
         "protected": "eyJmb3JtYXRMZW5ndGgiOjE5MTQsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNi0wNi0xMVQwMToxMzoyMVoifQ"
      }
   ]

bash$ curl -I http://localhost:5000/v2/busybox/manifests/latest
HTTP/1.1 200 OK
Content-Length: 2561
Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
Docker-Content-Digest: sha256:e45f25b1760f616e65f106b424f4ef29185fbd80822255d79dabc73b8eb715ad
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:e45f25b1760f616e65f106b424f4ef29185fbd80822255d79dabc73b8eb715ad"
X-Content-Type-Options: nosniff
Date: Sat, 11 Jun 2016 01:21:26 GMT

No where in any of those API calls could I find the desired 47bcc53... image id that I see locally.

bash$ docker inspect busybox:latest
[
    {
        "Id": "sha256:47bcc53f74dc94b1920f0b34f6036096526296767650f223433fe65c35f149eb",
        "RepoTags": [
            "busybox:latest",
            "localhost:5000/busybox:latest"
        ],
...

From this image spec the image id is a reproducible hash, and I do see the same value for the image id on different systems.

ImageID Each image's ID is given by the SHA256 hash of its configuration JSON. It is represented as a hexadecimal encoding of 256 bits, e.g., sha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9. Since the configuration JSON that gets hashed references hashes of each layer in the image, this formulation of the ImageID makes images content-addresable.

So if you can reproduce the configuration JSON from the API calls, then you may be able to generate the image id yourself.

Gorget answered 11/6, 2016 at 1:43 Comment(1)
Thanks! This answer is what I expected base on my research...Seems the only way to get image ID of a repository image is try to get the configuration JSON, and reproduce image ID via the same hash algorithm of Docker Daemon.Voltage
S
5

From my research, you can obtain the image ID from the registry(2.3.0+) with

curl -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' http://$server/v2/$repo/manifests/$tag

and the config.digest field in the response represents imageID,that what you want!

for example

refers: https://duyanghao.github.io/docker-registry-pull-manifest-v2/

Scrip answered 28/10, 2016 at 12:37 Comment(0)
R
0

You can get the image ID using the docker manifest command:

docker manifest inspect <registry>/<name>:<tag>

The output will be JSON and the image ID is present at the path .config.digest.

If you have jq installed, you can run the following to print only the image ID:

docker manifest inspect <registry>/<name>:<tag> | jq -r '.config.digest | split(":")[1]'
Ride answered 21/3 at 11:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.