suggested improvements to docker image ls
and docker image ls --tree
for multi-platform #5560
Description
Description
The tree output currently uses the same sort order as the existing
non-tree output, and orders the images by "created" time in descending
order;
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 8262a6d8c38a 7 minutes ago 13.6MB
docker-cli-dev latest f5f0547476ee 12 minutes ago 762MB
nginx alpine 2140dad235c1 2 weeks ago 76.7MB
alpine latest beefdbd8a1da 6 weeks ago 24.2MB
Sorting by the created date can be useful, e.g.;
- The last built image appears at the top (so could be found through cutting
the first line) - It's consistent with
docker container ls
/docker ps
, which uses the
same ordering, but also provides flags to get the last containers (-l
,
--latest
, and-n
,--last
)
However, the CREATED
has become less useful in various situations. For
example, "reproducible builds" tend to either leave the "created" date
unset, or use a fixed date (often resulting in images created "many years
ago").
The date also has some ambiguity; for multi-platform images, each image
can have its own "created" date (and built at a different time); which
date to show in the list?
The CREATED
date also has been confusing at times. For example, an image
that was just pulled may have been built weeks ago, and now not showing at
the top of the list. But also situations where docker build
was able to
use the build-cache, in which case the image wasn't updated, and as a result
the CREATED
date of the image being in the past.
Finally, sorting by CREATED
is confusing when using the new --tree
output
of images. This output does not currently have a CREATED
column, which
makes the output order seem "random". With the tree view being more verbose,
it may also be harder to find back images in the list when they're not sorted
in an easy to discover way (some platforms were omitted in the example below
to keep the example short).
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
└─ linux/arm64 18ca7881145d 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
├─ linux/arm64/v8 d1f949a77b81 76.7MB 21.5MB
├─ linux/amd64 ae136e431e76 0B 0B
└─ linux/s390x 8c310bf29cfa 0B 0B
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
└─ linux/s390x 2b5b26e09ca2 0B 0B
Suggestions
I think there's a couple of options we have, short-term and longer term.
Short term
We should be careful changing the default order, at least for the current
presentation, as people may depend on this. However, the work on the --tree
output is part of future work to make the CLI more human-friendly, and to
provide a more modern look and feel. Such changes are a good opportunity
to make changes; those changes may be "breaking" changes, so we may need
some opt-in/opt-out options to help people transition.
Longer term
We may need more granular information about dates and usage of images (and
other content), such as:
- Whether an image was built locally or pulled (relevant w.r.t. garbage
collection) - Dates related to the image: created date, last pulled, last pushed,
last used, etc. (also see Add ability to see when an image has last been used by a container moby/moby#4237). - More?
1. Add a collapsed version of the --tree
view
We should make a collapsed version of the --tree
view. This layout can
become the default in future, but initially (and while we're still designing
these bits), we can make this an "opt-in" through the features
option
in the CLI config (e.g. {"features": {"multiplatform-output": true}}
.
The collapsed view will have the same columns as the expanded --tree
view;
$ docker image ls
IMAGE ID DISK USAGE CONTENT SIZE USED
<untagged> 20ad73eca911 13.6MB 4.09MB ✔
<untagged> b3e87f642f5c 13.6MB 4.09MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
alpine:latest beefdbd8a1da 24.2MB 7.46MB
This means that when using the --tree
view, the layout stays the same,
but with more details shown;
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
<untagged> 20ad73eca911 13.6MB 4.09MB ✔
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB ✔
<untagged> b3e87f642f5c 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
└─ linux/arm64 18ca7881145d 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
├─ linux/arm64/v8 d1f949a77b81 76.7MB 21.5MB
├─ linux/amd64 ae136e431e76 0B 0B
├─ linux/arm/v6 ae1ee4b63c14 0B 0B
├─ linux/arm/v7 20ad73eca911 0B 0B
├─ linux/386 1e69bfb21757 0B 0B
├─ linux/ppc64le 7fef8bcf8b6c 0B 0B
└─ linux/s390x 8c310bf29cfa 0B 0B
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
├─ linux/amd64 33735bd63cf8 0B 0B
├─ linux/arm/v6 50f635c8b04d 0B 0B
├─ linux/arm/v7 f2f82d424957 0B 0B
├─ linux/386 b3e87f642f5c 0B 0B
├─ linux/ppc64le c7a6800e3dc5 0B 0B
└─ linux/s390x 2b5b26e09ca2 0B 0B
2. Sort alphabetically by default
For the new layout, we can change the sort-order. I suggest that sorting
alphabetically (using natural-sort) would make sense. I think we should
also consider sorting <untagged>
images last, as they may be less relevant
to the user:
$ docker image ls
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB ✔
<untagged> b3e87f642f5c 13.6MB 4.09MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB
This means that when using the --tree
view, the layout stays the same,
but with more details shown;
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
├─ linux/amd64 33735bd63cf8 0B 0B
├─ linux/arm/v6 50f635c8b04d 0B 0B
├─ linux/arm/v7 f2f82d424957 0B 0B
├─ linux/386 b3e87f642f5c 0B 0B
├─ linux/ppc64le c7a6800e3dc5 0B 0B
└─ linux/s390x 2b5b26e09ca2 0B 0B
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
└─ linux/arm64 18ca7881145d 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
├─ linux/arm64/v8 d1f949a77b81 76.7MB 21.5MB
├─ linux/amd64 ae136e431e76 0B 0B
├─ linux/arm/v6 ae1ee4b63c14 0B 0B
├─ linux/arm/v7 20ad73eca911 0B 0B
├─ linux/386 1e69bfb21757 0B 0B
├─ linux/ppc64le 7fef8bcf8b6c 0B 0B
└─ linux/s390x 8c310bf29cfa 0B 0B
<untagged> 20ad73eca911 13.6MB 4.09MB ✔
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB ✔
<untagged> b3e87f642f5c 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
3. Use stable sort order for manifests
The --tree
option on currently sorts manifests to put those that are present
first, and those that are not present (not pulled) after. The intent was to
present "available" images at the top of each tree, followed by images that
were not pulled.
However, there's some limitations to this. First of all, the current approach
makes the output non-deterministic as the order in which variants are pulled
determines the order in which they're presented, i.e., the last pulled variant
is returned first;
$ docker pull --platform=linux/riscv64 alpine:latest
$ docker pull --platform=linux/arm64 alpine:latest
$ docker image ls -a --tree alpine
IMAGE ID DISK USAGE CONTENT SIZE IN USE
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
├─ linux/amd64 33735bd63cf8 0B 0B
├─ linux/arm/v6 50f635c8b04d 0B 0B
├─ linux/arm/v7 f2f82d424957 0B 0B
├─ linux/386 b3e87f642f5c 0B 0B
├─ linux/ppc64le c7a6800e3dc5 0B 0B
└─ linux/s390x 2b5b26e09ca2 0B 0B
This makes the output non-deterministic, and lacking a LAST PULLED
(or
something similar) field, can make it somewhat confusing.
The order in which variants appear in the manifest can be relevant, as
in some cases this order affects what image is pulled as "best match"
if no exact match is available for the host's native architecture, and
if multiple platforms variants are candidates.
I think we should default to present variants in the order they are included
in the manifest index. More details also in this PR:
$ docker pull --platform=linux/riscv64 alpine:latest
$ docker pull --platform=linux/arm64 alpine:latest
$ docker image ls -a --tree alpine
IMAGE ID DISK USAGE CONTENT SIZE IN USE
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/amd64 33735bd63cf8 0B 0B
├─ linux/arm/v6 50f635c8b04d 0B 0B
├─ linux/arm/v7 f2f82d424957 0B 0B
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
├─ linux/386 b3e87f642f5c 0B 0B
├─ linux/ppc64le c7a6800e3dc5 0B 0B
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
└─ linux/s390x 2b5b26e09ca2 0B 0B
4. Hide non-pulled images by default (TBD)
One option worth considering is to hide non-pulled platform variants
by default. Doing so would partially achieve the goal that sorting
the "available variants first" mentioned above, and it would make the
output shorter in most situations. In many cases, users may only have
the native variant of an image pulled.
We need to design a UX for this though; would --all
be used to show
"all the things", or do we need a more granular option? ("all variants"
vs "all images, including dangling ones")
$ docker image ls --tree alpine
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
└─ linux/riscv64 80cde017a105 10.6MB 3.37MB
5. Hide untagged images by default
We should consider re-defining the meaning of --all
, as well as "dangling"
and "intermediate" images. Some of the "dangling" images definition originates
from the classic/legacy builder, which used the image store for build-cache;
each step in the Dockerfile resulted in an untagged image, but the image had
to be preserved to be used for its cache. Cleaning up those images was a manual
step, so the --all
option was added to make them visible.
With BuildKit being the default builder now, cleaning up the build-cache is
handled separate from cleaning up images ( docker builder prune
/ docker buildx prune
),
and BuildKit also provides automatic garbage collection of its buildcache.
While we don't (yet!) have automatic garbage collection for images, I think
we should have that at some point (at least opt-in), and hiding untagged
content would (IMO) be a good place to start, to show that's content eligible
for removal (if it's important to you, you should've put a ring on it, and
tagged or pushed it).
$ docker image ls
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
Same, but --tree
view:
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
└─ linux/riscv64 80cde017a105 10.6MB 3.37MB
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
└─ linux/arm64 18ca7881145d 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
└─ linux/arm64/v8 d1f949a77b81 76.7MB 21.5MB
And -a
/ --all
option to show all images, including un-tagged and those
not pulled;
$ docker image ls --tree
IMAGE ID DISK USAGE CONTENT SIZE USED
alpine:latest beefdbd8a1da 24.2MB 7.46MB
├─ linux/amd64 33735bd63cf8 0B 0B
├─ linux/arm/v6 50f635c8b04d 0B 0B
├─ linux/arm/v7 f2f82d424957 0B 0B
├─ linux/arm64/v8 9cee2b382fe2 13.6MB 4.09MB
├─ linux/386 b3e87f642f5c 0B 0B
├─ linux/ppc64le c7a6800e3dc5 0B 0B
├─ linux/riscv64 80cde017a105 10.6MB 3.37MB
└─ linux/s390x 2b5b26e09ca2 0B 0B
docker-cli-dev:latest f5f0547476ee 762MB 179MB ✔
└─ linux/arm64 18ca7881145d 762MB 179MB ✔
nginx:alpine 2140dad235c1 76.7MB 21.5MB
├─ linux/amd64 ae136e431e76 0B 0B
├─ linux/arm/v6 ae1ee4b63c14 0B 0B
├─ linux/arm/v7 20ad73eca911 0B 0B
├─ linux/arm64/v8 d1f949a77b81 76.7MB 21.5MB
├─ linux/386 1e69bfb21757 0B 0B
├─ linux/ppc64le 7fef8bcf8b6c 0B 0B
└─ linux/s390x 8c310bf29cfa 0B 0B
<untagged> 20ad73eca911 13.6MB 4.09MB ✔
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB ✔
<untagged> b3e87f642f5c 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
<untagged> 8262a6d8c38a 13.6MB 4.09MB
└─ linux/arm64 1ab6fc68586e 13.6MB 4.09MB
Some parts may have to be looked into in more depth; there's still some
odd behavior that, while "by design", is surprising;
Related to that, we need to decide whether to hide untagged images if
they are still in use by a container, or consider those case (as they
(I think) require docker image rm --force
) are still eligible for
garbage collecting (once the container is removed0, and thus should also
be hidden.
Activity