Reputation: 3880
If I do docker inspect {img}
, I will get a list of many sha256s. What does each of these sha mean? Here's a list of all the shas from docker inspect ubuntu:16.04
:
"Id": "sha256:0ef2e08ed3fabfc44002ccb846c4f2416a2135affc3ce39538834059606f32dd"
"RepoDigests": ["ubuntu@sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535"]
"ContainerConfig"."Image":"sha256:518b94cfb647aca74cc36f08ddacd5cb61abee3c8cf5cd66b1fadff40c7240eb"
"Config"."Image":"sha256:518b94cfb647aca74cc36f08ddacd5cb61abee3c8cf5cd66b1fadff40c7240eb"
"RootFS"."Layers":["sha256:745f5be9952c1a22dd4225ed6c8d7b760fe0d3583efd52f91992463b53f7aea3",
"sha256:85782553e37a2998422ecb14fb34ac3fda94dbc90c6630d721a3bcc770939946",
"sha256:29660d0e5bb2bae1d415f5638fa6011ab4063d1c0895e889d51ad365186d1995",
"sha256:440e02c3dcde277c7426c07c6e240a40b1e53da4a8a0cc22a8cecd4e6f419a98",
"sha256:56827159aa8b327a1b15c2102040ee87f3ca0bf8285aab00a1286e8af79a4beb"]
This leads to my second question, I have seen people use sha as the source when they build docker images: FROM ubuntu@sha256:...
. Which sha from the above that it pulls that from?
Upvotes: 8
Views: 17131
Reputation: 20822
To answer your question about which id to use FROM ubuntu@sha256:...
, I'll use a micromamba image that I just incorporated into my dockerfile.
According to the micromamba docs,
To reproducibly build images derived from these micromamba images, the best practice is for the Dockerfile FROM command to reference the image’s sha256 digest and not use tags.
So, I was using this image on dockerhub. Notice that the SHA256 id is in the URL. It also follows DIGEST
on this page.
Now, armed with all this info, I changed
FROM mambaorg/micromamba:1.4.9
to
FROM mambaorg/micromamba@sha256:972c762dcc17673e316f0f10b91582b96f1b968440074103d855d083cf648981
and it worked.
Upvotes: 1
Reputation: 263469
An image consist of a manifest, a config, and an array of filesystem layers. You may also have a multi-platform image manifest that points to multiple image manifests. Everything on the registry is a Content Addressable Storage (CAS) meaning it can be referred to by the hash of it's contents. So lets take an example.
Start with the manifest list:
$ regctl manifest get --list localhost:5000/library/busybox --format '{{ jsonPretty . }}'
{
"manifests": [
{
"digest": "sha256:6066ca124f8c2686b7ae71aa1d6583b28c6dc3df3bdc386f2c89b92162c597d9",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 527
},
{
"digest": "sha256:399e1e4a0d587717dc9e3a85150cec8498cb6dc73dcb7eddb94959fedb331104",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 527
},
{
"digest": "sha256:4ecc3dc2e06a24df931cb719c3784611d15721c3cb64ab069141071b73f6598b",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
},
"size": 527
},
{
"digest": "sha256:53c212bcc0501f011c232df0fb6c837651d0b2f3257b6478a50c0e006b0dabc5",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 527
},
{
"digest": "sha256:ce53e9b0310447d0e851ff0d2c9b90f358dbffe719a723147e84b93a4799396c",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 527
},
{
"digest": "sha256:0bec409945b4e48517645d4f1bc44a965dc09a0c9647be4494f82ce0f1306c27",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 527
},
{
"digest": "sha256:77df281071dd7e01972ec2c4a33c1a6c00d13b24238375fd6622fce97f622fa2",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "mips64le",
"os": "linux"
},
"size": 527
},
{
"digest": "sha256:b70f0f45692830c2990b42f770aa29488c20ac41f1c3dcaa242920b73cb1399b",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:06b206c1f1a38094697c7e8bf868f9d326e56a256bc516dbb8ff0ee9c1178999",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "riscv64",
"os": "linux"
},
"size": 527
},
{
"digest": "sha256:86824a27910bd2a8c6a8478fe99206e6cf4bcada7cb8435c0060cbe885559e53",
"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 --list localhost:5000/library/busybox --format raw-body \
| sha256sum
139abcf41943b8bcd4bc5c42ee71ddc9402c7ad69ad9e177b0a9bc4541f14924 -
$ docker pull localhost:5000/library/busybox
Using default tag: latest
latest: Pulling from library/busybox
01c2cdc13739: Pull complete
Digest: sha256:139abcf41943b8bcd4bc5c42ee71ddc9402c7ad69ad9e177b0a9bc4541f14924
Status: Downloaded newer image for localhost:5000/library/busybox:latest
localhost:5000/library/busybox:latest
$ docker inspect localhost:5000/library/busybox
[
{
"Id": "sha256:cabb9f684f8ba3edb303d578bfd7d709d853539ea1b420a3f6c81a08e85bb3d7",
"RepoTags": [
"localhost:5000/library/busybox:latest"
],
"RepoDigests": [
"localhost:5000/library/busybox@sha256:139abcf41943b8bcd4bc5c42ee71ddc9402c7ad69ad9e177b0a9bc4541f14924"
],
...
In this case, we could pull by that multi-platform image digest (localhost:5000/library/busybox@sha256:139abcf41943b8bcd4bc5c42ee71ddc9402c7ad69ad9e177b0a9bc4541f14924
), which is what would work in the Dockerfile too.
What are the rest of those digests then? Lets check the manifest for the linux/amd64 in the image above:
$ regctl manifest get localhost:5000/library/busybox@sha256:6066ca124f8c2686b7ae71aa1d6583b28c6dc3df3bdc386f2c89b92162c597d9
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1456,
"digest": "sha256:cabb9f684f8ba3edb303d578bfd7d709d853539ea1b420a3f6c81a08e85bb3d7"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 772798,
"digest": "sha256:01c2cdc137396a9b78be86c47e5773388e6eed953dec79e257777518134b45f1"
}
]
}
$ regctl manifest get localhost:5000/library/busybox@sha256:6066ca124f8c2686b7ae71aa1d6583b28c6dc3df3bdc386f2c89b92162c597d9 --format raw-body \
| sha256sum
6066ca124f8c2686b7ae71aa1d6583b28c6dc3df3bdc386f2c89b92162c597d9 -
There we see the config and layer array (in this case one layer). The config blob is json which looks like:
$ regctl blob get localhost:5000/library/busybox sha256:cabb9f684f8ba3edb303d578bfd7d709d853539ea1b420a3f6c81a08e85bb3d7 | 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": [
"sh"
],
"Image": "sha256:279d201053a8347d8add195c3e9bcbd46c22432c6340c21e1726ec1e402d90b5",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "021b3de9f9d3b33dc28abc07abbe8ccbd3e715fd2b4ed82e2837f7d1dcfd90ec",
"container_config": {
"Hostname": "021b3de9f9d3",
"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 [\"sh\"]"
],
"Image": "sha256:279d201053a8347d8add195c3e9bcbd46c22432c6340c21e1726ec1e402d90b5",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-10-27T17:19:45.337061231Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-10-27T17:19:45.128684405Z",
"created_by": "/bin/sh -c #(nop) ADD file:88b76d15f53403ca217f156c3fa01df961ddf224a193cf39ebe51678b77cf9cf in / "
},
{
"created": "2021-10-27T17:19:45.337061231Z",
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:a9ca537752fdf30df0dfe9fcd4ba936db4acbe0e991c9e4704c284bffed54eb5"
]
}
}
$ regctl blob get localhost:5000/library/busybox sha256:cabb9f684f8ba3edb303d578bfd7d709d853539ea1b420a3f6c81a08e85bb3d7 \
| sha256sum
cabb9f684f8ba3edb303d578bfd7d709d853539ea1b420a3f6c81a08e85bb3d7 -
You may see the digest of the config blob in the image id in docker (I'm not sure that's a guarantee, it may depend on how the config was generated that creates the image id).
The layer is a tar+gzip file (this one looks a little different than others since it's busybox where everything is linked to a single binary):
$ regctl blob get localhost:5000/library/busybox sha256:01c2cdc137396a9b78be86c47e5773388e6eed953dec79e257777518134b45f1 | tar -tvzf - | head -20
drwxr-xr-x 0/0 0 2021-10-26 14:46 bin/
-rwxr-xr-x 0/0 1153368 2021-10-26 14:46 bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/[[ link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/acpid link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/add-shell link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/addgroup link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/adduser link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/adjtimex link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/ar link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/arch link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/arp link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/arping link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/ascii link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/ash link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/awk link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/base32 link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/base64 link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/basename link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/bc link to bin/[
hrwxr-xr-x 0/0 0 2021-10-26 14:46 bin/beep link to bin/[
$ regctl blob get localhost:5000/library/busybox sha256:01c2cdc137396a9b78be86c47e5773388e6eed953dec79e257777518134b45f1 \
| sha256sum
01c2cdc137396a9b78be86c47e5773388e6eed953dec79e257777518134b45f1 -
In each of these examples, I've included a sha256sum
of the raw content to show it's the same as the digest on that content you just pulled. And because the manifest list contains digests for the manifest which contains digests for the config and layers, we have a merkle tree structure where referring to the digest of the parent manifest list ensures that none of the content it points to can be altered (as long as we verify the digest of the content we pull).
Disclaimer, the regctl
command here comes from my regclient project, but there are others including google's crane and redhat's skopeo.
Upvotes: 1
Reputation: 3112
Those are digests of image layers. The same image might be tagged with different names. But the SHA256 digest is a unique and immutable identifier you can reference.
If you pull an image specifying the digest, you have a guarantee that the image you’re using is always the same.
The more details can be found in docs, here: Pull an image by digest (immutable identifier) and here: Understand images, containers, and storage drivers
Upvotes: 3