Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX: improve / design UX for multi-arch images #44582

Open
thaJeztah opened this issue Dec 4, 2022 · 13 comments
Open

UX: improve / design UX for multi-arch images #44582

thaJeztah opened this issue Dec 4, 2022 · 13 comments
Labels
area/images area/ux containerd-integration Issues and PRs related to containerd integration kind/epic Epics to track work on related tickets kind/feature Functionality or other elements that the project doesn't currently have. Features are new and shiny status/1-design-review

Comments

@thaJeztah
Copy link
Member

thaJeztah commented Dec 4, 2022

Description

relates to:

With the containerd-integration in progress, we need to decide on UX for managing multi-arch images. Where previously an image would always be a single architecture, images may now be multi-arch, and we store multiple architectures / variants of an image. We discussed this topic some weeks agon in the containerd sync call, and the proposal was to create a ticket for discussion. Since that call, PR rumpl#113 made some changes (not yet upstreamed) to show individual architectures as individual images, which helps with visualising that the local image store has multiple variants stored for an image (and somewhat matches nerdctl), however, as we currently don't present the image's architecture in overview, this presentation is not ideal. It may also be a bit disjoint from the concept of multi-arch images, as image-variants now become "separate", somewhat defeating the concept of "multi".

This ticket describes some options; none of these are decided on (or final for that matter), but hopefully this can act as a starting point to come to a concensus on UX.

Assumptions

While writing these options, I made the following assumptions:

  • where possible, we want the UX to stay close to the existing (non-multi-arch) UX
  • for many (most) users, "multi-arch" remains to be "single-arch" (as before), but in some cases they may be using multiple architectures
    • the concept of multi-arch is for the image-index (multi-arch manifest) to be treated as a single entity
    • for most cases, the platform would be "current platform" (and we can keep the UX the same as non-multiarch)
    • for most cases, users wouldn't be micro-managing individual architectures; deleting / managing individual variants (when I run docker image rm alpine:3.16, I just want to remove alpine:3.16)
  • in short: in most situations, the local image would be a "shallow" pull (one, or two architectures out of possible many)
  • and; in most situations, there's no requirement to deal with all architectures. possible outliers here would be transferring an image between registries (pull from registry A, push to registry B)

Listing images

We need to decide where (and when) to expose architectures. We had some discussion about this during one of the maintainers calls, when discussing #42464. At the time, the question was raised "should this be visible by default?". There may not be a single answer to this (different scenarios require different information), and we could decide to include the platform information in API responses, but don't print the information by default.

If we decide to not include the platform, the output of docker images would remain the same as before multi-arch. Each image represents an image-manifest (either single- or multi-arch);

REPOSITORY       TAG          IMAGE ID       CREATED        SIZE
alpine           3.16         b95359c25051   25 hours ago   2.81MB
alpine           latest       8914eb54f968   5 days ago     3.26MB
busybox          latest       fcd85228d7a2   2 days ago     832kB

As we are showing manifest index (for multi-arch), we can add a PLATFORMS templating placeholder. Users can either use their own template, or we can add a flag to show that column. The column would show the variants that are present in the image cache (store) to keep the list short (and of we would truncate the output by default, unless --no-trunc is set). The SIZE column showing the size of all variants currently stored;

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS                                      SIZE
alpine           3.16         b95359c25051   25 hours ago   linux/amd64, linux/arm64/v8, linux/s390x, ...  6.19MB
alpine           latest       8914eb54f968   5 days ago     linux/arm64/v8                                 3.26MB
busybox          latest       fcd85228d7a2   2 days ago     linux/arm64/v8                                 832kB

Shallow / non-shallow pulls

There's some ambiguity, because alpine:3.16 is a multi-arch image, and provides many architectures. In the example above, alpine:3.16 is effectively a "shallow" pull; not all of the variants have been pulled (which is the most likely scenario). For reference, this is the list from Docker Hub; https://hub.docker.com/_/alpine/tags?page=1&name=3.16.3

DIGEST          OS/ARCH         COMPRESSED SIZE
b2774aff8c30    linux/386       2.68 MB
3d426b0bfc36    linux/amd64     2.68 MB
269d2ad7050b    linux/arm/v6    2.49 MB
92cd2f468f33    linux/arm/v7    2.31 MB
559254f7ee68    linux/arm64/v8  2.58 MB
a7ed77a6bc01    linux/ppc64le   2.67 MB
0c447070f97d    linux/s390x     2.47 MB

For cases where the user needs to interact with individual variants of the image, we can;

  • add a --verbose or --show-platforms flag
  • add a --platform flag on commands such as docker image rm / docker rmi, and docker image inspect to provide more granular control.

We can use something similar to docker service ps;

ID             NAME        IMAGE          NODE             DESIRED STATE   CURRENT STATE           ERROR                              PORTS
zef3c8wgn6x2   foo.1       nginx:alpine   docker-desktop   Running         Running 4 hours ago
vab7t41r0pg6    \_ foo.1   nginx:alpine   docker-desktop   Shutdown        Rejected 4 hours ago    "rpc error: code = Canceled de…"
c300yxopsccu    \_ foo.1   nginx:alpine   docker-desktop   Shutdown        Shutdown 4 hours ago
0qqppurnkc8x    \_ foo.1   nginx:alpine   docker-desktop   Shutdown        Shutdown 20 hours ago
scbfjmdcwm05    \_ foo.1   nginx:alpine   docker-desktop   Shutdown        Shutdown 27 hours ago
a1u6mkrs21gq   foo.2       nginx:alpine   docker-desktop   Running         Running 8 minutes ago

Which could look something like this:

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS                         SIZE
alpine           3.16         b95359c25051   25 hours ago   linux/amd64, linux/arm64/v8, ...  6.19MB
 \_ alpine       3.16         3d426b0bfc36   25 hours ago   linux/amd64                       2.68 MB
 \_ alpine       3.16         559254f7ee68   25 hours ago   linux/arm64/v8                    2.58 MB
 \_ alpine       3.16         0c447070f97d   25 hours ago   linux/s390x                       2.47 MB
alpine           latest       8914eb54f968   5 days ago     linux/arm64/v8                    3.26MB
 \_ alpine       latest       af06af3514c4   5 days ago     linux/arm64/v8                    3.26MB
busybox          latest       fcd85228d7a2   2 days ago     linux/arm64/v8                    832kB
 \_ busybox      latest       e68659cdc5b2   2 days ago     linux/arm64/v8                    832kB

It's worth noting that manifest (lists) are not necessarily unique; multiple tags can resolve to the same manifest(list). For example, if both alpine:latest and alpine:3.16 would have resolved to the same digest, the detailed list would be like below:

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS                         SIZE
alpine           3.16         b95359c25051   25 hours ago   linux/amd64, linux/arm64/v8, ...  6.19MB
 \_ alpine       3.16         3d426b0bfc36   25 hours ago   linux/amd64                       2.68 MB
 \_ alpine       3.16         559254f7ee68   25 hours ago   linux/arm64/v8                    2.58 MB
 \_ alpine       3.16         0c447070f97d   25 hours ago   linux/s390x                       2.47 MB
alpine           latest       b95359c25051   25 hours ago   linux/arm64/v8                    6.19MB
 \_ alpine       latest       3d426b0bfc36   25 hours ago   linux/amd64                       2.68 MB
 \_ alpine       latest       559254f7ee68   25 hours ago   linux/arm64/v8                    2.58 MB
 \_ alpine       latest       0c447070f97d   25 hours ago   linux/s390x                       2.47 MB

Note that in this case showing the REPOSITORY and TAG for each variant therefore is not "strictly correct", as those variants are not directly associated with a REPO or TAG. It's also not possible to "untag" such references (as this would mean "remove a reference from the manifest list", which would mean "create a new image manifest"). Because of this, we may want to consider to not present the TAG (and perhaps even REPOSITORY) for the variants;

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS                         SIZE
alpine           3.16         b95359c25051   25 hours ago   linux/amd64, linux/arm64/v8, ...  6.19 MB
 \_ alpine                    3d426b0bfc36   25 hours ago   linux/amd64                       2.68 MB
 \_ alpine                    559254f7ee68   25 hours ago   linux/arm64/v8                    2.58 MB
 \_ alpine                    0c447070f97d   25 hours ago   linux/s390x                       2.47 MB
alpine           latest       8914eb54f968   25 hours ago   linux/arm64/v8                    6.19 MB
 \_ alpine                    3d426b0bfc36   25 hours ago   linux/amd64                       2.68 MB
 \_ alpine                    559254f7ee68   25 hours ago   linux/arm64/v8                    2.58 MB
 \_ alpine                    0c447070f97d   25 hours ago   linux/s390x                       2.47 MB

Removing the REPOSITORY is a bit awkward, as it's the first column; we could consider changing the order of columns, putting the ID (digest) first;

IMAGE ID           REPOSITORY   TAG       PLATFORMS                         CREATED        SIZE
b95359c25051       alpine       3.16      linux/amd64, linux/arm64/v8, ...  25 hours ago   6.19 MB
 \_ 3d426b0bfc36                          linux/amd64                       25 hours ago   2.68 MB
 \_ 559254f7ee68                          linux/arm64/v8                    25 hours ago   2.58 MB
 \_ 0c447070f97d                          linux/s390x                       25 hours ago   2.47 MB
b95359c25051       alpine       latest    linux/arm64/v8                    25 hours ago   6.19 MB
 \_ 3d426b0bfc36                          linux/amd64                       25 hours ago   2.68 MB
 \_ 559254f7ee68                          linux/arm64/v8                    25 hours ago   2.58 MB
 \_ 0c447070f97d                          linux/s390x                       25 hours ago   2.47 MB

Given that in this presentation the columns don't align either way, we could consider omitting PLATFORMS for the top-level, re-purposing the space below REPOSITORY and TAG;

IMAGE ID           REPOSITORY   TAG       CREATED        SIZE
b95359c25051       alpine       3.16      25 hours ago   6.19 MB
 \_ 3d426b0bfc36    \_ linux/amd64        25 hours ago   2.68 MB
 \_ 559254f7ee68    \_ linux/arm64/v8     25 hours ago   2.58 MB
 \_ 0c447070f97d    \_ linux/s390x        25 hours ago   2.47 MB
b95359c25051       alpine       latest    25 hours ago   6.19 MB
 \_ 3d426b0bfc36    \_ linux/amd64        25 hours ago   2.68 MB
 \_ 559254f7ee68    \_ linux/arm64/v8     25 hours ago   2.58 MB
 \_ 0c447070f97d    \_ linux/s390x        25 hours ago   2.47 MB

Inspecting images

With the individual variants broken up, users can remove (or inspect) individual variants of an image, for example, to inspect the linux/s390x variant of the alpine image:

docker image inspect 0c447070f97d

For convenience, we should consider adding --platform to filter / show a specific variant:

docker image inspect --platform=linux/s390x alpine:3.16
  • ⚠️ Currently, docker image inspect defaults to showing the image for the default platform.
  • ❓ Do we want the command to default to show all architectures that are present?

Doing so would improve visibility for multi-arch images. The output of docker image inspect is already an array (which is used when inspecting multiple images);

docker image inspect busybox:latest hello-world:latest
[
  {
      "Id": "sha256:fcd85228d7a25feb59f101ac3a955d27c80df4ad824d65f5757a954831450185",
      "RepoTags": [
        "busybox:latest"
      ],
      "RepoDigests": null,
      "...": "...",
  },
  {
      "Id": "sha256:1ec996c686eb87d8f091080ec29dd1862b39b5822ddfd8f9a1e2c9288fad89fe",
      "RepoTags": [
        "hello-world:latest"
      ],
      "RepoDigests": [
        "hello-world@sha256:e18f0a777aefabe047a671ab3ec3eed05414477c951ab1a6f352a06974245fe7"
      ],
      "...": "...",
  }
]

Deleting images

Similarly to docker image inspect, the digest can be used to delete individual variants;

docker image rm 0c447070f97d

Note that this will remove the variant, so it should disappear from both alpine:3.16 and alpine:latest (or any image referencing it):

IMAGE ID           REPOSITORY   TAG       CREATED        SIZE
b95359c25051       alpine       3.16      25 hours ago   5.26 MB
 \_ 3d426b0bfc36    \_ linux/amd64        25 hours ago   2.68 MB
 \_ 559254f7ee68    \_ linux/arm64/v8     25 hours ago   2.58 MB
b95359c25051       alpine       latest    25 hours ago   5.26 MB
 \_ 3d426b0bfc36    \_ linux/amd64        25 hours ago   2.68 MB
 \_ 559254f7ee68    \_ linux/arm64/v8     25 hours ago   2.58 MB

As an alternative, we can add a --platform flag to docker image rm as well; the command below would achieve the same as above;

docker image rm --platform=linux/s390x alpine:3.16

❓ what do we want the behavior to be if (as in the example) the variant is referenced by multiple architectures? The docker image rm --platform command is ambiguous, as the user request the variant to be removed from the alpine:3.16 image. It may be surprising that it's also removed from the alpine:latest image (I guess the "most correct" presentation would be to show one line per digest, and multiple tags after it, but this is a HUGE change, and may not be very user-friendly).

  • Print a warning? (Require some "force" option)?
  • Or just "go ahead and remove"?

Shallow, shallower, shallowest

As images in the local store may be a "shallow" pull of a multi-arch image, the question is: do we want to provide insight into that? If so, how?

  • Do we want an (optional) column to show that the image is "shallow"? (only some variants present)
  • Do we want an indicator what the total size and number of variants would be if it's fully pulled?
  • Do we want an option to pull (all) the "missing" variants?
  • What do we want the behavior to be when doing a docker image pull without specifying a --platform?

For the "how much is missing comparing to (e.g.) docker service ls;

ID             NAME      MODE         REPLICAS   IMAGE          PORTS
i47e9yrzjef8   foo       replicated   2/2        nginx:alpine

We could add (optional) columns for "counts" to see "how" shallow the image is;

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS     SIZE
alpine           3.16         b95359c25051   25 hours ago   4/7           6.19MB / 17.88MB
alpine           latest       8914eb54f968   5 days ago     1/7           3.26MB / 17.88MB
busybox          latest       fcd85228d7a2   2 days ago     1/10          832kB / 10.76MB

For the "verbose" output, we could consider having an option to show what's missing; not sure (yet) how to best present that it's "missing", perhaps the SIZE column to show how much is there, or a MISSING somewhere?

IMAGE ID           REPOSITORY   TAG       CREATED        SIZE
b95359c25051       alpine       3.16      25 hours ago   6.19 / 17.88MB
 \_ b2774aff8c30    \_ linux/368          -                 0 / 2.68 MB
 \_ 3d426b0bfc36    \_ linux/amd64        25 hours ago   2.68 / 2.68 MB
 \_ 269d2ad7050b    \_ linux/arm/v6       -                 0 / 2.49 MB
 \_ 92cd2f468f33    \_ linux/arm/v7       -                 0 / 2.31 MB
 \_ 559254f7ee68    \_ linux/arm64/v8     25 hours ago   2.58 / 2.58 MB
 \_ a7ed77a6bc01    \_ linux/ppc64le      -                 0 / 2.67 MB
 \_ 0c447070f97d    \_ linux/s390x        25 hours ago   2.47 / 2.47 MB
b95359c25051       alpine       latest    25 hours ago   6.19 / 17.88MB
 \_ ...            ...                    ...             ... / ...

Filtering

Add a --platform option to docker image ls (and consider a --filter platform=xxx). Using the filter would show any image that currently has the given os/arch present;

docker image ls --platform=linux/s390x

REPOSITORY       TAG          IMAGE ID       CREATED        PLATFORMS     SIZE
alpine           3.16         b95359c25051   25 hours ago   4/7           6.19MB / 17.88MB

In "verbose" view, this would hide the other architectures;

IMAGE ID           REPOSITORY   TAG       CREATED        SIZE
b95359c25051       alpine       3.16      25 hours ago   6.19 / 17.88MB
 \_ 0c447070f97d    \_ linux/s390x        25 hours ago   2.47 / 2.47 MB
b95359c25051       alpine       latest    25 hours ago   6.19 / 17.88MB
 \_ ...            ...                    ...             ... / ...

Pruning

To be discussed; do we want pruning to default to

  1. "remove unused architectures" from an image
  2. or: only remove images as a whole (if any of the architectures are in use, don't remove anything)?
  3. combination of 1. and 2.; if --all is set, do 1., otherwise do 2.
  4. like 3. but with a dedicated flag?

Pulling images

If an image is not present in the local store, the current behavior is to pull with the default platform (platform of the host).

We need to define the expected behavior when pulling an image that is already present.

what should happen when

  • doing a docker image pull without specifying a --platform ?
  • doing a docker image pull with an explicit --platform=<DEFAULT PLATFORM>
  • doing a docker image pull with --platform=<other platform>
  • doing a docker run --platform and the given platform is not in the current image

Currently, the behavior is confusing (and there's some bugs / undefined behavior);

The part to define if image should be updated (re-resolving the digest) or not. There's advantages to either, but equally "confusing" behavior.

It depends on what we envision pull to mean, and whether a --platform was specified (explicitly?). The changes are subtle, but may be important. Some options;

  1. When pulling an image (using name:tag), irregardless of --platform to be specified (explicitly), we resolve the digest, and pull the image.
  2. (A) When pulling an image WITH a --platform specified
    • don't re-resolve the manifest-index
    • pull the new platform, using the digest that's found in manifest index that's currently present
    • This option allows "back-filling" missing plaforms for an already present image.
  3. (B) When pulling an image WITH a --platform specified
    • resolve the manifest-index and pull it
    • pull the NEW platform with the digest found in the new manifest-index
    • store the NEW platform under the NEW manifest-index
    • Two images will show (currently); one for the old (with the existing platforms) and one for the new (with the newly pulled platform)
  4. (C) Like combination of 2. and 3.: use 2. as default, but offer a --resolve, --update or --pull=<some option> option to force updating.
  5. (A) When pulling an image WITHOUT a --platform specified, then
    • resolve the manifest-index and pull it
    • pull all existing platforms, using the digests found in the new manifest-index
    • pull the default platform (if not present) (?), using the digest found in the new manifest-index
  6. (B) When pulling an image WITHOUT a --platform specified, then
    • resolve the manifest-index and pull it
    • pull only the default platform, using the digest found in the new manifest-index
    • store the default platform under the NEW manifest-index
    • Two images will show (currently); one for the old (with the existing platforms) and one for the new (with the newly pulled default platform)

My preference goes out to

  • 2. or 4. when a --platform is set, as it doesn't implicitly update the image for other architectures.
  • 5. for when no --platform is set; this is the closes match to the "pre-multi-arch" behavior: when pulling an image, it's updated to the latest version. But with multi-arch, this means "all arches that I already have".
@thaJeztah thaJeztah added status/1-design-review kind/feature Functionality or other elements that the project doesn't currently have. Features are new and shiny area/images containerd-integration Issues and PRs related to containerd integration labels Dec 4, 2022
@AkihiroSuda
Copy link
Member

PLATFORMS
4/7

While swarm replicas are expected to be eventually completely fulfilled, platforms are not, so this may cause confusion.

@thaJeztah
Copy link
Member Author

Yes, I definitely wasn't sold on that one myself, but thought I'd include it, to see if we think there's a need to show that an image is "partial" / "shallow" in some way. Not sure what the best, non-confusing presentation is for it (I ran a bit out of steam, so will give it further thinking as well, but ideas always welcome!)

@rumpl
Copy link
Member

rumpl commented Dec 13, 2022

For listing images I quite like what @AkihiroSuda did i.e. one line per platform and we show the index digest and the image platform

$ nerdctl images
REPOSITORY    TAG     IMAGE ID        CREATED           PLATFORM          SIZE       BLOB SIZE
alpine        3.16    b95359c25051    15 seconds ago    linux/amd64       5.9 MiB    2.7 MiB
alpine        3.16    b95359c25051    15 seconds ago    linux/arm64/v8    0.0 B      2.6 MiB

I'm really not a fan of PLATFORMS, it only complicates everything, makes us add new flags (--no-trunc??) so that the user can see the info they need...

@tianon
Copy link
Member

tianon commented Feb 13, 2023

Sorry, I meant to come back to this much sooner 😩

I think we do need to do something with the image index here, because that's the actual top-level object we pull and reference by tag, and the images inside it are only indirectly tagged, so I think it's going to be even more confusing for users how those objects are structured in a registry if we hide it in the CLI experience.

In other words, I think we'd provide the most value to our users if we show the tag attached to the top-level index object (which is the thing that's actually tagged in the containerd image store and the registry), and use something like one of the tree views that @thaJeztah suggested in order to show the per-architecture images we actually have locally (although I don't think we have to hide it behind a "verbose" flag, since the added detail about which ones I have locally is useful and important data that's critical to my understanding of what I have locally, which is the primary reason to run docker images).

@jalonsogo
Copy link

Multi-arch images Exploration-Proposal

Description:

We are exploring a new default visualization that provides more advantages for the user like:

  • Allow copy-paste image reference for direct usage. Combining image and tag in one column.
  • Offer the same type of information on both Docker Desktop and Docker CLI (“in use” column)
  • Better layout for representing the concept of multi-arch images.
  • Show the user hints about the most common next logical step.

Actual status

Right now, this is the output for docker images / docker images ls

CleanShot 2023-03-06 at 13-1 05 1

New default for Docker image ls

The new default view for docker images / docker images ls will be this:

$ docker images --layout=tree --platform --hint

This view lets you copy and paste the image reference quickly and compare sizing better with the size aligned with the dot.

Multi-arch images visibility

We have introduced the flag —platform to display the platforms included in one image. In compact mode, the layout is this.

docker images  --platforms

But also, to improve readability and usability, we have introduced two new flags that allow switching between the compact layout (the default one) and one tree view using the format flag —format=tree

docker images --format tree --platforms --hint

This layout provides a better understanding of the concept of one image containing multiple images.

In the mockup, we can also highlight two features:

  • The blue elements are links to the registry. (This is specific to the tree layout). Providing an easy interaction with the images.
  • Show context-sensitive hints using —hint, providing the most common commands used in this context to help the users.

If needed, the user can identify one specific image inside another image displaying all the IDs contained in one image using —platforms=all, this works both in Compact and tree mode.

docker images --firnat tree --platforms=all --hint

In summary

Changes:

  • New default output for docker images ls
  • Three new flags:
    • layout [Compact, Tree] → Allows switching between something more readable and something more scrappable.
    • platforms [(empty)|all] → Display all the platforms available or all the platforms supported.
    • hints → Shows context-sensitive hints

@neersighted
Copy link
Member

I quite like this proposal. The only think I'd like to push back on is a --hint flag; I think that if it's opt-in it's not likely to be very useful. Turning it on by default, introducing a top-level docker --no-hint <subcommand> option and a DOCKER_HINT=0 environment-based opt out is probably a better approach.

In any case, the hints are interesting and could benefit a lot of other areas in the CLI UX, but I think we should split discussion of those out of this initial proposal as they're really quite generic/not directly part of the scope of this issue.

@thaJeztah
Copy link
Member Author

thaJeztah commented Mar 6, 2023 via email

@thaJeztah thaJeztah added the kind/epic Epics to track work on related tickets label Mar 7, 2023
@tianon
Copy link
Member

tianon commented Mar 10, 2023

I don't like mixing image name and platform (like in alpine:3.16/amd64, for example), as I think it's confusing to the user especially since there's not any other system that allows specifying platform in that way as an input, so it's strange to see them combined like that for an output, but other than the cosmetic concern (which could be resolved with instead of /, for example, alpine:3.16 amd64, although I think the tree views by default is much more compelling personally), these look very interesting to me. 😄

@jalonsogo
Copy link

Agree, also with that approach, and we avoid the collision of two / for example, alpine:3.16/arm/v8. Anyway, that approach is only for the "compact" visualization.

Should we explore other ways? For example:

  • alpine:3.16>arm/v8
  • alpine:3.16-arm/v8
  • alpine:3.16=arm/v8

If we use one space, we cannot use the behavior of double-clicking one string and copying it.

@thaJeztah
Copy link
Member Author

Thanks @tianon! Heh, yes, I was hoping to get some comments on that format.

Let me put it in context; Javier was not yet familiar with all the concepts of OCI, Multi-Arch (manifest lists) etc. So, while I had to give a bit of a "bootcamp", I also tried to take advantage of his "not yet poisoned by domain knowledge" fresh eyes 👀

While we were discussing initial concepts, we also went through "multiple namespaces" (while not really seen much, and not supported by Docker Hub) are allowed (registry.com/foo/bar/baz/image:latest), so the format could be somewhat confusing.

However, while discussing; Javier asked some really good questions;

  • what do you use this overview for?
  • how do you use the information?

Besides "getting an overview of what's there", my answer after that was "... and then to select an image to interact with" (which could be "to inspect the image", "to delete the image", or "to run the image".

Then the question became "how do you do that?". You need something unique to select that image.

The current output of docker image ls is bad for this;

  • image (repository) and tag are in separate columns. To select a specific image by repository:tag, I'd have to make 2 selections, and type a colon (:) in between
  • ^ this makes "grep-ing" also difficult (yes, --filter is possible, but even more awkward)
  • (REPOSITORY is confusing; while correct, it's not what many users think of as term)
  • ID is an option, but is not unique if multiple tags point to the same image.
  • The position of the ID column is also awkward; depending on your list of images (length of tags), there may be a large gap between the image name and ID (see first screenshot), which makes it very easy to select the wrong line; and ID being a "random set of characters" makes it difficult to miss you did so.
  • This is all not taking into account "and now.. what if you need a specific platform variant from the list?"

In short;

  • what info is needed to uniquely identify an image from the list?
  • can we make that a single selectable column?
  • that ideally is also "human readable"

So, while the example may not be perfect, we decided "let's include it", and take it as a starting point to discuss if we could come up with such a format.

@thaJeztah
Copy link
Member Author

Oh! I see @jalonsogo also replied while I was fighting the tiny keys on my phone to write down my reply 😂❤️❤️

@tianon
Copy link
Member

tianon commented Mar 10, 2023

Yeah, I 100% love the repo:tag change -- I actually use a local custom default imagesFormat in my .docker/config.json to get similar output today. 😂

(I had to wait until I was reasonably confident that I'd ported the majority of my really old scripts from docker images | awk -F ' +' 'NR > 1 { ... }' over to use docker images --format first 🙈 😂)

$ jq -r .imagesFormat .docker/config.json
table {{ $haveRepo := ne .Repository "<none>" }}{{ $haveTag := ne .Tag "<none>" }}{{ $haveDigest := and (ne .Digest "<none>") (ne .Digest "") }}{{ if and $haveRepo (or $haveTag $haveDigest) }}{{ .Repository }}{{ if $haveTag }}:{{ .Tag }}{{ else }}@{{.Digest}}{{ end }}{{ else }}{{ if $haveRepo }}{{ .Repository }} @ ... <use --digests>{{ else }}...{{ end }}{{ end }}\t{{ .ID }}\t{{ .CreatedSince }}\t{{ .Size }}

$ docker images debian
REPOSITORY:TAG         IMAGE ID       CREATED      SIZE
debian:bullseye-slim   224855429754   9 days ago   80.5MB

Like you, I use the default output primarily for seeing what I have locally (size, age, specific tags, dangling images) and for copy-pasting into other tools. In the containerd integration, an additional very related thing I expect to be able to see from this is which specific platforms I've pulled locally for each image, so IMO we're on the right track.

Using any character that directly connects the image name and the architecture makes me nervous because it looks like syntax, and I'm more likely to expect it to be something I can pass into tools somehow/somewhere (if Docker presents the information in this format, it must be the format Docker wants to accept as well).

I really like the idea of very explicitly separating the "visual" use case from the "scraping" use case, and think it would be totally fair to have a much more dense display (like a dedicated "platform" column) specifically for scraping. Honestly I don't ever use --filter because I can't remember the caveats/quirks/syntax, so I tend to either do just docker images basic-repo/tag-filter or fall back to --format so I'm extracting exactly the information I'm looking for in a way I can easily feed to whatever other tool (sometimes even as wild as {{ json . }} so I can feed to jq because it's got a more natural language than text/template's 😬).

@tianon
Copy link
Member

tianon commented Jun 8, 2023

One thing I realized today while talking to @yosifkit about this is that with the c8d content store, "image ID" is a lot less relevant in the general case than it used to be -- for images with a tag, that's usually what we want, but for images without a tag (either pull-by-digest or dangling), the image ID is the digest, so would already be implied/included.

@thaJeztah thaJeztah changed the title Discuss: improve / design UX for multi-arch images UX: improve / design UX for multi-arch images Jul 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/images area/ux containerd-integration Issues and PRs related to containerd integration kind/epic Epics to track work on related tickets kind/feature Functionality or other elements that the project doesn't currently have. Features are new and shiny status/1-design-review
Projects
None yet
Development

No branches or pull requests

6 participants