Search code examples
docker

Docker: How to remove all the containers according with a removed image's id?


About Docker for experimental purposes was executed the following command:

sudo docker rmi -f ubuntu:20.04

which prints:

Untagged: ubuntu:20.04
Untagged: ubuntu@sha256:0b897358ff6624825fb50d20ffb605ab0eaea77ced0adb8c6a4b756513dec6fc
Deleted: sha256:5f5250218d28ad6612bf653eced407165dd6475a4daf9210b299fed991e172e9

Now if is executed the sudo docker ps -a command is displayed

CONTAINER ID   IMAGE          COMMAND                  CREATED        STATUS                    PORTS     NAMES
...
ed096064373f   5f5250218d28   "cat /etc/os-release"    19 hours ago   Exited (0) 19 hours ago             ubuntu-20.18-d-015
3bc4c52f0299   5f5250218d28   "cat /etc/os-release"    19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-015
8cd0502ff777   5f5250218d28   "ls -als ../etc"         19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-014
d3ce9a3fbd47   5f5250218d28   "ls -als /"              19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-013
c8889c44f3a9   5f5250218d28   "ls -als ."              19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-012
1c6ff666c7ff   5f5250218d28   "ls -als /home/manue…"   19 hours ago   Exited (2) 19 hours ago             ubuntu-20.04-d-011
b5da7429b8b8   5f5250218d28   "ls -als /etc"           19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-010
f169712b9718   5f5250218d28   "ls -als /etc"           19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-009
5ea2001f47e2   5f5250218d28   "ls -als /home"          19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-008
cde6c940efb4   5f5250218d28   "ls -als /home/manue…"   19 hours ago   Exited (2) 19 hours ago             ubuntu-20.04-d-007
f75c3f7932cd   5f5250218d28   "ls -als ."              19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-006
7d7154a21668   5f5250218d28   "cat /etc/os-release"    19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-005
a75b22a43af7   5f5250218d28   "echo hello"             19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-004
2fcfb4dea168   5f5250218d28   "cat /home/manueljor…"   19 hours ago   Exited (1) 19 hours ago             ubuntu-20.04-d-003
8c9def0115c8   5f5250218d28   "date"                   19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-002
f2a7ad7904a8   5f5250218d28   "ls /etc"                19 hours ago   Exited (0) 19 hours ago             ubuntu-20.04-d-001
...

Observe the IMAGE header value. It shows the 5f5250218d28 value where it is the first 12 characters of sha256:5f5250218d28ad6612bf653eced407165dd6475a4daf9210b299fed991e172e9

Now, I want to remove all these containers based on the Images's id shown in the IMAGE header.

Thus I did do a research and I found:

Where is indicated the following command:

  • docker rm $(docker ps -a -q --filter "ancestor=ubuntu")

Now, just playing with the command substitution part and by applying the following variations as follows:

docker ps -aq --filter "ancestor=5f5250218d28"
docker ps -aq --filter "ancestor=sha256:5f5250218d28"
docker ps -aq --filter "ancestor=5f5250218d28ad6612bf653eced407165dd6475a4daf9210b299fed991e172e9"
docker ps -aq --filter "ancestor=sha256:5f5250218d28ad6612bf653eced407165dd6475a4daf9210b299fed991e172e9"

All of them returns nothing

Just in case if is executed

sudo docker ps -aq --filter "name=ubuntu-20.04-d-"

It prints the expected set of container's id but not always the containers would have the same pattern name

Question

  • How to remove all the containers according with the image's id?

So I need resolve first the command substitution part about the ps -aq --filter command

I've read the official documentation about:

And it seems is not possible. But I am assuming would exists a trick/smart approach


Solution

  • Updated: I removed the recommendation of the -q flag as of course the go template already returned the IDs only.


    A Docker image is metadata + filesystem layers. What docker rmi or docker image rm does is removing the user-friendly tag from the images. When it has multiple tags, and you remove one, the command's output shows only "untagged". When there is no tag anymore, there is an additional line saying "deleted", which means the whole image, including the metadata and layers not part of other images.

    But if something still refers to the image like a running container, the image cannot be deleted completely. since that would make the executable and libraries disappear used by the process in the container.

    You can force it if for some reason you can't stop the container but you want to make sure nobody starts a container from that image anymore, although, indeed I don't have an example from real life, because I never even thought of using it. But when you do, the image remains as "dangling" and the docker rmi command will show only "untagged" in the output and docker image ls will reveal these by showing <none> as repository and tag. You could actually filter to dangling images by running

    docker image ls --filter=dangling
    

    but docker image prune will not let you delete it even though it is supposed to delete dangling images as a container still refers to it.

    Since your containers were already stopped, Docker could delete the image metadata, however, the filesystem layers are still there as the containers exist and you could start them again, but the so-called "ancestor", which is the image is not actually there anymore.

    I'm not sure if this was an intentional implementation or something the developers would fix, but I could confirm, that the ancestor filter works only if the ancestor exists. Instead of using that filter, you can use the following command to list the container started from the image with the a specific image ID which is "5f5250218d28" in your case and I used it in the example:

    docker ps -a --format '{{ if eq .Image "5f5250218d28" }}{{ .ID }}{{ end }}' | grep "\S"
    

    You don't need to add -q to get only the container IDs as the go template solved that already so you can use it in the subshell instead of the one with the filter parameter. This relies only on the container's medata and uses Go template to show the container ID only when the Image id is what you want. The grep command will just remove empty lines since you still get as many lines as many container's you have without a filter.

    If you don't know what objects you can work with, you can run

    docker ps -a --format json
    

    Or to get a shorter output, just get the first json line:

    docker ps -a --format json | head -n1
    

    And optionally format it with jq if you installed it:

    docker ps -a --format json | head -n1 | jq
    

    Example:

    {
      "Command": "\"httpd-foreground\"",
      "CreatedAt": "2024-08-14 20:00:21 +0200 CEST",
      "ID": "0ed21408c8b2",
      "Image": "3c2285410b59",
      "Labels": "",
      "LocalVolumes": "0",
      "Mounts": "",
      "Names": "test2",
      "Networks": "bridge",
      "Ports": "",
      "RunningFor": "23 hours ago",
      "Size": "0B",
      "State": "exited",
      "Status": "Exited (0) 23 hours ago"
    }
    

    And if you are more familiar with jq then go templates, you can just use jq to get the container IDs:

    docker ps -a --format json \
      | jq -r '. | select(.Image == "3c2285410b59") | .ID'
    

    -q is still not needed as jq took care of the formatting.

    My original, much shorter answer is on the Docker forum, but I thought I would add some more details and alternatives.