Docker, dangling container? - docker

I cant start my docker-compose environment, because the port is allocated by something.
ERROR: for ***** Cannot start service *****: driver failed programming external connectivity on endpoint ***** (4314ec13837d41ca8ef1b7e1d8446ab8cfa96136539a75ac763ba1cf538ffdc1): Bind for 0.0.0.0:8881 failed: port is already allocated
Encountered errors while bringing up the project.
However, there is not such a container
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
But when I inspect the docker-compose network, I can see a container in the network
$ docker network inspect *****
[
{
"Name": "*****",
"Id": "56d09f51e8fe5a9ad11dd6cdaff7e89983f519fd1ecff08116c2e48a3b34ff32",
"Created": "2018-08-01T08:17:30.704352821Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"08a5defa5ceae7a5a5ac7ec85f5ecd0924ecb4e5eacca4f5752049e32a0f190c": {
"Name": "08a5defa5cea_*****",
"EndpointID": "5d081a19b5ffe4e893965a4306668059900812532cc4995e168ce017f2765e12",
"MacAddress": "02:42:ac:12:00:06",
"IPv4Address": "172.18.0.6/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "*****",
"com.docker.compose.project": "docker"
}
}
]
Yet it is impossible to remove this container
docker rm -f *****
Error: No such container: *****
How to proceed?

I run this script to shut down all stuck containers which sometimes linger
#!/bin/bash
# ... bring down all containers
force_it="" # normal
# force_it=" -f " # when you see "device or resource busy"
for curr_container_id in $( docker ps -q ); do
echo curr_container_id $curr_container_id
echo
echo "docker stop $curr_container_id "
docker stop $curr_container_id
echo
echo "docker rm $force_it $curr_container_id "
docker rm $force_it $curr_container_id
done
for curr_container_id in $( docker ps -q -a ); do
echo curr_container_id $curr_container_id
echo
echo "docker stop $curr_container_id "
docker stop $curr_container_id
echo
echo "docker rm $force_it $curr_container_id "
docker rm $force_it $curr_container_id
done
it works 99% of the time ... if not near the top you can toggle the force parm and re-run it ... this allows you to avoid having to bounce any docker server

Related

Is there a way to convert a x86-only docker image to a multi-platform one?

The newest docker registry/engine have supported "manifest list" feature to allow user referencing images with different CPU architectures, OSes and other characteristics, by solo entry in registry.
So saying I have a legacy x86-only image in my repository ,and unfortunately it'd been altered during previous running as container (successive docker commits) which means no Dockerfile available. Is there a way I can convert this x86-only image to support manifest list without rebuilding it?
Docker can export the contents of a container started from the image. Then a new image can be created FROM scratch that ADDs the contents back.
Steps
docker export will create a tar file of the complete contents of a container created from the image.
$ CID=$(docker create myimage)
$ docker export -o myimage.tar $CID
$ docker rm $CID
Build a new Dockerfile FROM scratch that ADDs the exported contents tar file back.
FROM scratch
ADD myimage.tar /
Any extended meta data for Dockerfile, like ENTRYPOINT, CMD or VOLUMES, can be queried via inspect or history:
$ docker image inspect myimage -f '{{json .Config}}' | jq
{
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"27017/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.10",
"JSYAML_VERSION=3.10.0",
"GPG_KEYS=2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5",
"MONGO_PACKAGE=mongodb-org",
"MONGO_REPO=repo.mongodb.org",
"MONGO_MAJOR=3.6",
"MONGO_VERSION=3.6.3"
],
"Cmd": [
"mongod"
],
"ArgsEscaped": true,
"Image": "sha256:bac19e2cfd49108534b108c101a68a2046090d25da581ae04dc020aac93b4e31",
"Volumes": {
"/data/configdb": {},
"/data/db": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": [],
"Labels": null
}
or
docker image history myimage --no-trunc

Permissions issues in Docker image while gradle is trying to write to an out directory

I am building a docker image which will contain gradle, and other tools used for testing the application.
So, I have created a new Dockerfile based on a corporate docker image with one of the tools I need, then strapped gradle after that, courtesy of
keeganwitt.
My run command looks like this:
docker run -it --mount source=~/app-repo/appName,target=/home/gradle/,type=bind app_unittests:gradle_sahi /bin/bash -c "whoami;pwd;chown --recursive gradle:gradle /home/gradle; gradle test --tests test.automation.tools.TestSahi --debug --stacktrace"
So this works, it starts running, and eventually I get:
Failed to create parent directory '/home/gradle/out' when creating directory '/home/gradle/out/classes/java/main
Also note that I added the chown command to my docker run command (lifted from the gradle dockerfile) - but it also runs into permission issues on everything...
I also checked docker inspect <CONTAINER_ID>:
"Mounts": [
{
"Type": "bind",
"Source": "/root/app-repo/appName",
"Destination": "/home/gradle",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "volume",
"Name": "66dcb821f5687c55821ec908ea7ad041065477fa9328cd5cd47e21cf7d7a0028",
"Source": "/var/lib/docker/volumes/66dcb821f5687c55821ec908ea7ad041065477fa9328cd5cd47e21cf7d7a0028/_data",
"Destination": "/home/gradle/.gradle",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
As well as Mounts under HostConfig:
"Mounts": [
{
"Type": "bind",
"Source": "/root/app-repo/appName",
"Target": "/home/gradle/"
}
]
You can see, it IS mounted RW:true.
Please help..
You should run ls -n /root/app-repo/appName (on the source). If the ids are not 1000, you will need to chown the directory (sudo chown -R 1000:1000 /root/app-repo/appName) or run the container as root (docker run --user root or user: root in your Docker compose file).

Docker container communication on the same net not work

I have 3 different docker container (on windows 10) on the same network (core_net), but when use curl on backend
-curl localhost:7000
the response is:
"curl: (7) Failed to connect to localhost port 7000: Connection refused"
Why?
Docker commands:
Frontend:
docker run -itd --name dam --net=core_net -p 3000:3000 DAM
Backend:
docker run -itd --name core --net=core_net -p 6000:6000 -p 7000:7000 -p 8000:8000 -p 9000:9000 SG
Database:
docker run --name mongodb -p 27017:27017 -d mongo:3
These is the dockerfile:
Frontend:
FROM node:4.5.0
# Create app directory
RUN mkdir -p /DAM
WORKDIR /DAM
# Install app dependencies
COPY package.json /DAM
RUN npm install
RUN npm install gulp -g
RUN echo '{ "allow_root": true }' > /root/.bowerrc
RUN npm install bower -g
# Bundle app source
COPY . /DAM
ENV PORT 3000 3001
EXPOSE $PORT
CMD ["gulp", "serve"]
and
Backend:
FROM node:4.5.0
RUN npm install nodemon -g
# Create app directory
RUN mkdir -p /SG
WORKDIR /SG
# Install app dependencies
COPY package.json /SG
RUN npm install
# Bundle app source
COPY . /SG
ENV PORT 6000 7000 8000 9000
EXPOSE $PORT
CMD ["npm", "start"]
Inside container the ping works and the inspect is this:
$ docker network inspect core_net
{
"Name": "core_net",
"Id": "1f9e5426abe397d520360c05c95fee46fe08c98fe5c474c8b52764e491ea23e7",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Containers": {
"3d3b8780fba2090b1c2feaddf2e035624529cf5474ad4e6332fe7071c0acbd25": {
"Name": "core",
"EndpointID": "f0a6882e690cf5a7deedfe57ac9b941d239867e3cd58cbdf0ca8a8ee216d53a9",
"MacAddress": "02:42:ac:12:00:04",
"IPv4Address": "172.18.0.4/16",
"IPv6Address": ""
},
"bb6a6642b3a7ab778969f2e00759d3709bdca643cc03f5321beb9b547b574466": {
"Name": "dam",
"EndpointID": "b42b802e219441f833d24971f1e1ea74e093f56e28126a3472a44750c847daa4",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"cf8dd2018f58987443ff93b1e84fc54b06443b17c7636c7f3b4685948961ba3f": {
"Name": "mongodb",
"EndpointID": "be02d784cbd46261b7a53d642102887cafa0f880c8fe08086b9cc026971ea1be",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
Commnication between mongodb and core work but between dam and core not work.
what's the problem?
To connect to another container with in an network you can not use localhost, but cou can use the name of the conatiner you want to reach. e.g. curl core:7000
To use localhost the conatiners have to share their network stack. You can do that with --network container:core
And if you dont have to reach the backend from outside of docker, it is enought to only expose the ports, and not to publish them

Get list of docker tags available to pull from command line?

I want to get some basic information about the published versions/tags of a docker image, to know what image:tag's I can pull. I would also like to see the time that each tag was most recently published.
Is there a way to do this on the command line?
Docker version 1.10.2, build c3959b1
Basically looking for the equivalent of npm info {pkg} for a docker image.
Not from the command line. You have docker search but it only returns a subset of the data you want, and only for the image with the :latest tag:
> docker search sixeyed/hadoop-dotnet
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
sixeyed/hadoop-dotnet Hadoop with .NET Core installed 1 [OK]
If you want more detail, you'll need to use the registry API, but it only has a catalog endpoint for listing repositories, the issue for search is still open.
Assuming you know the repository name, you can navigate the API - first you need an auth token:
> curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:sixeyed/hadoop-dotnet:pull"
{"token":"eyJhbG...
Then you pass the token to subsequent requests, e.g. to list the tags:
> curl --header "Authorization: Bearer eyJh..." https://index.docker.io/v2/sixeyed/hadoop-dotnet/tags/list
{"name":"sixeyed/hadoop-dotnet","tags":["2.7.2","latest"]}
And then get all the information about one image by its repository name and tag:
> curl --header "Authorization: Bearer eyJh..." https://index.docker.io/v2/sixeyed/hadoop-dotnet/manifests/latest
I want to get some basic information about the published versions/tags of a docker image, to know what image:tag's I can pull.
This is from the tags/list API. Here's a small script that does it:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
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 .
Running that looks like:
$ ./tags-v2.sh library/ubuntu:latest
{
"name": "library/ubuntu",
"tags": [
"10.04",
"12.04",
"12.04.5",
"12.10",
"13.04",
"13.10",
"14.04",
"14.04.1",
...
I would also like to see the time that each tag was most recently published.
You likely want to pull the image config for this. First you need to pull the manifest, parse the config descriptor, then pull that blob for the config json. Here's a script that will do that for docker images:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
digest=$(curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${tag}" \
| jq -r .config.digest)
curl -H "Accept: application/vnd.docker.container.image.v1+json" \
-H "Authorization: Bearer $token" \
-s -L "https://registry-1.docker.io/v2/${repo}/blobs/${digest}" | jq .
And an example of running that script:
$ ./get-config-v2.sh library/ubuntu:latest
{
"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": [
"bash"
],
"Image": "sha256:6c18a628d47eacf574eb93da2324293a0e6c845084cca2ea13efaa3cee4d0799",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "249e88be79ad9986a479c71c15a056946ae26b0c54c1f634f115be6d5f9ba1c8",
"container_config": {
"Hostname": "249e88be79ad",
"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 [\"bash\"]"
],
"Image": "sha256:6c18a628d47eacf574eb93da2324293a0e6c845084cca2ea13efaa3cee4d0799",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-10-16T00:37:47.578710012Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-10-16T00:37:47.226745473Z",
"created_by": "/bin/sh -c #(nop) ADD file:5d68d27cc15a80653c93d3a0b262a28112d47a46326ff5fc2dfbf7fa3b9a0ce8 in / "
},
{
"created": "2021-10-16T00:37:47.578710012Z",
"created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:9f54eef412758095c8079ac465d494a2872e02e90bf1fb5f12a1641c0d1bb78b"
]
}
}
Note that this will only give you details from the build tooling, the registry itself does not track when an image was uploaded to that registry from any OCI APIs (some registries may add their own metadata, but this would be registry specific).
To handle more authentication types, different kinds of images (OCI vs Docker), and similar, I've packaged these commands and more into regctl in the regclient project. Similar projects exist from Google's container registry crane command, and RedHat's skopeo, each giving access to registries from the command line:
$ regctl tag ls ubuntu
10.04
12.04
12.04.5
12.10
13.04
13.10
14.04
14.04.1
14.04.2
14.04.3
14.04.4
14.04.5
...
$ regctl image config ubuntu
{
"created": "2021-10-16T00:37:47.578710012Z",
"architecture": "amd64",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:9f54eef412758095c8079ac465d494a2872e02e90bf1fb5f12a1641c0d1bb78b"
]
},
"history": [
{
"created": "2021-10-16T00:37:47.226745473Z",
"created_by": "/bin/sh -c #(nop) ADD file:5d68d27cc15a80653c93d3a0b262a28112d47a46326ff5fc2dfbf7fa3b9a0ce8 in / "
},
{
"created": "2021-10-16T00:37:47.578710012Z",
"created_by": "/bin/sh -c #(nop) CMD [\"bash\"]",
"empty_layer": true
}
]
}
If you don't want to use a token:
curl -L -s 'https://registry.hub.docker.com/v2/repositories/<$repo_name>/tags?page=<$page_nb>&page_size=<$page_size>' | jq '."results"[]["name"]'
Where you can get <$repo_name> with:
docker search <$expression>
For example:
$ docker search piwigo
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
linuxserver/piwigo A Piwigo container, brought to you by LinuxS… 152
mathieuruellan/piwigo Easy deployment of piwigo for my personal us… 19 [OK]
lsioarmhf/piwigo 3
hg8496/piwigo 2 [OK]
[…]
$ curl -L -s 'https://registry.hub.docker.com/v2/repositories/linuxserver/piwigo/tags?page=1&page_size=10' | jq '."results"[]["name"]'
"latest"
"12.0.0"
"version-12.0.0"
"12.0.0-ls137"
"arm64v8-12.0.0"
"arm32v7-12.0.0"
"amd64-12.0.0"
"arm64v8-version-12.0.0"
"arm32v7-version-12.0.0"
"amd64-version-12.0.0"
Source: Démarrer avec les containers Docker

How to view Docker image layers on Docker Hub?

I know that I can use this command $ docker images --tree docker history to view the layers of a Docker image, but how do I do that for images on Docker Hub without pulling it? This is so that I know what is on an image before I download it.
E.g., for the Tomcat repo, https://registry.hub.docker.com/_/tomcat/, the webpage doesn't seem to show what is on the image. I have to look at the Dockerfile on Github to find out.
Update
I see this repo https://registry.hub.docker.com/u/tutum/tomcat/ has more tabs. The "Dockerfile" tab shows how it is created, but only seems to show the latest version. Is there no choice to view the file for other tags?
Docker Hub is quite limited at the moment and does not offer the feature you asked for.
When an image is configured to build from source at Docker Hub (an Automated Build) you can see what went into it, but when it is uploaded pre-built you have no information.
This is now possible by clicking into the image tag on Docker Hub.
Use microbadger, which comes with a few badges as well, eg:
So your tomcat image on https://microbadger.com/images/tomcat would look like:
You can get this for any registry by using the registry API. Within the image manifest is a pointer to the image config, and that config is a json blob that includes the history steps. Note that this history is not authoritative, and depending on the build tooling, could report different commands than what were actually used to build the image layers.
A shell script version of this using the auth for Docker Hub looks like:
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
repo="${ref%:*}"
tag="${ref##*:}"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" \
| jq -r '.token')
digest=$(curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $token" \
-s "https://registry-1.docker.io/v2/${repo}/manifests/${tag}" \
| jq -r .config.digest)
curl -H "Accept: application/vnd.docker.container.image.v1+json" \
-H "Authorization: Bearer $token" \
-s -L "https://registry-1.docker.io/v2/${repo}/blobs/${digest}" | jq .history
Behind the scenes, what's happening is the registry first resolves any manifest list to a single manifest for the default architecture, but you could pull any architecture manually:
$ regctl image manifest --list localhost:5000/library/alpine:latest --format '{{jsonPretty .}}'
{
"manifests": [
{
"digest": "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 528
},
{
"digest": "sha256:18c29393a090ba5cde8a5f00926e9e419f47cfcfd206cc3f7f590e91b19adfe9",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v6"
},
"size": 528
},
...
Then that platform specific manifest includes the config and layers:
$ regctl image manifest localhost:5000/library/alpine#sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a --format '{{jsonPretty .}}'
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1471,
"digest": "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2814446,
"digest": "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e"
}
]
}
And pulling the config blob shows all of the contents you may recognize from the history and inspect commands:
$ regctl blob get localhost:5000/library/alpine sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab | 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:d3e0b6258ec2f725c19668f11ae5323c3b0245e197ec478424ec6a87935690eb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"container": "330289c649db86f5fb1ae5bfef18501012b550adb0638b9193d4a3a4b65a2f9b",
"container_config": {
"Hostname": "330289c649db",
"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:d3e0b6258ec2f725c19668f11ae5323c3b0245e197ec478424ec6a87935690eb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"created": "2021-08-27T17:19:45.758611523Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-08-27T17:19:45.553092363Z",
"created_by": "/bin/sh -c #(nop) ADD file:aad4290d27580cc1a094ffaf98c3ca2fc5d699fe695dfb8e6e9fac20f1129450 in / "
},
{
"created": "2021-08-27T17:19:45.758611523Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68"
]
}
}
The regctl commands above come from my regclient project, but I believe you can do similar commands with skopeo and crane. The advantage of these tools over curl commands is handling the authentication that varies between registries (the bearer token from Hub won't work for other registries).
Lastly, the one-line answer using regctl is a formatted image config command, which performs all the above steps without needing jq:
$ regctl image config localhost:5000/library/alpine:latest --format '{{jsonPretty .History}}'
[
{
"created": "2021-08-27T17:19:45.553092363Z",
"created_by": "/bin/sh -c #(nop) ADD file:aad4290d27580cc1a094ffaf98c3ca2fc5d699fe695dfb8e6e9fac20f1129450 in / "
},
{
"created": "2021-08-27T17:19:45.758611523Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
}
]

Resources