¿Cómo obtener la lista de imágenes secundarias dependientes en Docker?

123

Estoy tratando de eliminar una imagen y obtengo:

# docker rmi f50f9524513f  
Failed to remove image (f50f9524513f): Error response from daemon: conflict: unable to delete f50f9524513f (cannot be forced) - image has dependent child images

Esta es la versión de Docker:

# docker version
Client:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.10.3
 API version:  1.22
 Go version:   go1.5.3
 Git commit:   20f81dd
 Built:        Thu Mar 10 21:49:11 2016
 OS/Arch:      linux/amd64

pero no hay información adicional:

# docker images --format="raw" | grep f50f9524513f -C3

repository: debian
tag: 8
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

repository: debian
tag: jessie
image_id: f50f9524513f
created_at: 2016-03-01 18:51:14 +0000 UTC
virtual_size: 125.1 MB

¿Cómo puedo obtener las imágenes de los hijos dependientes que afirma tener?

no hay contenedores en ejecución ni detenidos con esa identificación de imagen.

nicocesar
fuente
9
Me pregunto por qué el equipo de Docker no puede proporcionar una forma nativa de hacer esto.
Amalgovinus

Respuestas:

54

Respuesta corta: aquí hay una secuencia de comandos de python3 que enumera las imágenes de Docker dependientes.

Respuesta larga: puede ver la identificación de la imagen y la identificación principal de todas las imágenes creadas después de la imagen en cuestión con lo siguiente:

docker inspect --format='{{.Id}} {{.Parent}}' \
    $(docker images --filter since=f50f9524513f --quiet)

Debería poder buscar imágenes con una identificación principal que comience con f50f9524513f, luego buscar imágenes secundarias de esas , etc. Pero .Parent no es lo que piensa. , por lo que en la mayoría de los casos necesitará especificar docker images --allarriba para que funcione, luego obtendrá los identificadores de imagen para todas las capas intermedias también.

Aquí hay una secuencia de comandos de python3 más limitada para analizar la salida de la ventana acoplable y hacer la búsqueda para generar la lista de imágenes:

#!/usr/bin/python3
import sys

def desc(image_ids, links):
    if links:
        link, *tail = links
        if len(link) > 1:
            image_id, parent_id = link
            checkid = lambda i: parent_id.startswith(i)
            if any(map(checkid, image_ids)):
                return desc(image_ids | {image_id}, tail)
        return desc(image_ids, tail)
    return image_ids


def gen_links(lines):
    parseid = lambda s: s.replace('sha256:', '')
    for line in reversed(list(lines)):
        yield list(map(parseid, line.split()))


if __name__ == '__main__':
    image_ids = {sys.argv[1]}
    links = gen_links(sys.stdin.readlines())
    trunc = lambda s: s[:12]
    print('\n'.join(map(trunc, desc(image_ids, links))))

Si guarda esto, desc.pypodría invocarlo de la siguiente manera:

docker images \
    | fgrep -f <(docker inspect --format='{{.Id}} {{.Parent}}' \
        $(docker images --all --quiet) \
        | python3 desc.py f50f9524513f )

O simplemente use la esencia de arriba , que hace lo mismo.

Aryeh Leib Taurog
fuente
2
¿Qué pasa si ninguno de ellos comienza con los personajes esperados? ¿Eso indica un posible error? Estoy en Docker para Mac beta, FWIW, por lo que no me sorprendería.
neverfox
O es un error o significa que la imagen en cuestión no tiene hijos.
Aryeh Leib Taurog
2
Esto realmente no responde a la pregunta original. Esto muestra lo que se creó después de la imagen en cuestión, que puede o no depender de la imagen que el póster intentaba eliminar. La respuesta de Simon Brady funciona, al menos para tamaños de muestra pequeños de imágenes.
codificador de pingüino
2
@penguincoder para eso es el script de Python.
Aryeh Leib Taurog
Como @neverfox, esta respuesta no funcionó para mí. Sin embargo, la respuesta de Simon Brady a continuación funcionó.
Mark
91

Si no tiene una gran cantidad de imágenes, siempre existe el enfoque de fuerza bruta:

for i in $(docker images -q)
do
    docker history $i | grep -q f50f9524513f && echo $i
done | sort -u
Simon Brady
fuente
1
Creo que esta es la "mejor" solución. Puede expandir esto un poco y dejar un poco más claro qué es qué cambiando echo $1a lo más feo (pero aún por fuerza bruta) docker images | grep $i(más portátil en las versiones de Docker que usando --filterbanderas para el ID de imagen)
Jon V
15

Instale dockviz y siga las ramas desde la identificación de la imagen en la vista de árbol:

go get github.com/justone/dockviz
$(go env GOPATH)/bin/dockviz images --tree -l
mmoya
fuente
mejor hacerlo sudo apt-get update ; sudo apt-get install golang-go; export GOPATH=$HOME/.goprimero.
loretoparisi
3
Está disponible en macOS a través debrew install dockviz
rcoup
Cuidado, al usar Ubuntu. Por lo general, se usan repositorios obsoletos para ir
Qohelet
7

Creé una esencia con un script de shell para imprimir el árbol descendiente de una imagen de Docker, en caso de que alguien esté interesado en la solución de bash:

#!/bin/bash
parent_short_id=$1
parent_id=`docker inspect --format '{{.Id}}' $1`

get_kids() {
    local parent_id=$1
    docker inspect --format='ID {{.Id}} PAR {{.Parent}}' $(docker images -a -q) | grep "PAR ${parent_id}" | sed -E "s/ID ([^ ]*) PAR ([^ ]*)/\1/g"
}

print_kids() {
    local parent_id=$1
    local prefix=$2
    local tags=`docker inspect --format='{{.RepoTags}}' ${parent_id}`
    echo "${prefix}${parent_id} ${tags}"

    local children=`get_kids "${parent_id}"`

    for c in $children;
    do
        print_kids "$c" "$prefix  "
    done
}

print_kids "$parent_id" ""
Michal Linhard
fuente
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada. - De la crítica
Juan Cruz Soler
Solo tuve problemas para formatear el código, así que me di por vencido, gracias por hacer esto Jan
Michal Linhard
2

Esto es lo que hice para preservar mi "imagen" final (capa, en realidad, que es lo que me desconcertó, ya que estoy entrando en las compilaciones de Docker).

Estaba recibiendo el mensaje completo "... no se puede forzar ...". Me di cuenta de que no podía eliminar las imágenes que no necesitaba porque no son imágenes realmente independientes creadas por 'docker commit'. Mi problema fue que tenía varias imágenes (o capas) entre la imagen base y mi imagen final, y solo tratando de limpiar es donde encontré el error / advertencia sobre el niño y el padre.

  1. Exporté la imagen final (o capa, si se quiere) a un tarball.
  2. Luego eliminé todas las imágenes que quería, incluida la final; la tengo guardada en un tarball, así que, aunque no estaba seguro de poder usarla, solo estaba experimentando.
  3. Entonces corrí docker image load -i FinalImage.tar.gz. La salida fue algo como:

7d9b54235881: Loading layer [==================================================>]  167.1MB/167.1MB
c044b7095786: Loading layer [==================================================>]  20.89MB/20.89MB
fe94dbd0255e: Loading layer [==================================================>]  42.05MB/42.05MB
19abaa1dc0d4: Loading layer [==================================================>]  37.96MB/37.96MB
4865d7b6fdb2: Loading layer [==================================================>]  169.6MB/169.6MB
a0c115c7b87c: Loading layer [==================================================>]    132MB/132MB

ID de imagen cargada: sha256: 82d4f8ef9ea1eab72d989455728762ed3c0fe35fd85acf9edc47b41dacfd6382

Ahora, cuando hago una lista con 'docker image ls', solo tengo la imagen base original y la imagen final que guardé previamente en un tarball.

[root@docker1 ~]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
httpd               import              82d4f8ef9ea1        3 days ago          747MB
centos              httpd               36540f359ca3        5 weeks ago         193MB

Mi sistema está "limpio" ahora. Solo tengo las imágenes que quiero. Incluso borré la imagen base sin problemas.

[root@docker1 ~]# docker rmi 36540f359ca3
Untagged: centos:httpd
Untagged:     centos@sha256:c1010e2fe2b635822d99a096b1f4184becf5d1c98707cbccae00be663a9b9131
Deleted: sha256:36540f359ca3b021d4b6a37815e9177b6c2bb3817598979ea55aee7ecc5c2c1f
Jerome Jordan
fuente
Buena respuesta, pero se supone que la carga solo debe usarse en una imagen creada a partir de "docker save". La restauración adecuada para una "exportación de docker" es "importación de docker"
Amalgovinus
2

Basado en las respuestas de Slushy y Michael Hoffman , si no tiene un montón de imágenes, puede usar esta función de shell:

docker_image_desc() {
  for image in $(docker images --quiet --filter "since=${1}"); do
    if [ $(docker history --quiet ${image} | grep ${1}) ]; then
      docker_image_desc "${image}"
    fi
  done
  echo "${1}"
}

y luego llámalo usando

docker_image_desc <image-id> | awk '!x[$0]++'
Maxim Suslov
fuente
2

Aquí hay una solución basada en la API de Python ( pip install docker) que enumera de forma recursiva los descendientes junto con sus etiquetas (si las hay), aumentando la sangría según la profundidad de la relación (hijos, nietos, etc.):

import argparse
import docker

def find_img(img_idx, id):
    try:
        return img_idx[id]
    except KeyError:
        for k, v in img_idx.items():
            if k.rsplit(":", 1)[-1].startswith(id):
                return v
    raise RuntimeError("No image with ID: %s" % id)

def get_children(img_idx):
    rval = {}
    for img in img_idx.values():
        p_id = img.attrs["Parent"]
        rval.setdefault(p_id, set()).add(img.id)
    return rval

def print_descendants(img_idx, children_map, img_id, indent=0):
    children_ids = children_map.get(img_id, [])
    for id in children_ids:
        child = img_idx[id]
        print(" " * indent, id, child.tags)
        print_descendants(img_idx, children_map, id, indent=indent+2)

def main(args):
    client = docker.from_env()
    img_idx = {_.id: _ for _ in client.images.list(all=True)}
    img = find_img(img_idx, args.id)
    children_map = get_children(img_idx)
    print_descendants(img_idx, children_map, img.id)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("id", metavar="IMAGE_ID")
    main(parser.parse_args())

Ejemplo:

$ python find_dep_img.py 549afbf12931
 sha256:913d0981fdc7d2d673f2c8135b7afd32ba5037755e89b00432d3460422ba99b9 []
   sha256:0748dbc043b96ef9f88265c422e0267807f542e364b7a7fadf371ba5ee082b5d []
     sha256:6669414d2a0cc31b241a1fbb00c0ca00fa4dc4fa65dffb532bac90d3943d6a0a []
       sha256:a6441e7d9e92511608aad631f9abe8627033de41305c2edf7e03ee36f94f0817 ['foo/bar:latest']

Lo he puesto a disposición como resumen en https://gist.github.com/simleo/10ad923f9d8a2fa410f7ec2d7e96ad57

simleo
fuente
1

A continuación, se muestra una forma sencilla de obtener una lista de imágenes secundarias que dependen de una imagen principal:

image_id=123456789012

docker images -a -q --filter since=$image_id |
xargs docker inspect --format='{{.Id}} {{.Parent}}' |
grep $image_id

Eso generará una lista de ID de imágenes de padres / hijos, por ejemplo (truncado por brevedad):

sha256:abcdefghijkl sha256:123456789012

El lado izquierdo es el ID de la imagen secundaria, el lado derecho es el ID de la imagen principal que estamos tratando de eliminar, pero dice "la imagen tiene imágenes secundarias dependientes". Esto nos dice que abcdefghijkles el niño del que depende 123456789012. Así que primero tenemos que hacerlo docker rmi abcdefghijkl, luego tú puedes docker rmi 123456789012.

Ahora, puede haber una cadena de imágenes de niños dependientes, por lo que es posible que deba seguir repitiendo para encontrar al último niño.

Wisbucky
fuente
0

Puede eliminar imágenes de Docker independientemente de la relación entre padres e hijos a través del siguiente directorio de Docker

/var/lib/docker/image/devicemapper/imagedb/content/sha256

En este directorio puedes encontrar imágenes de Docker, para que puedas borrar lo que quieras.

Jvn
fuente
Estoy tratando de hacer comandos portátiles usando la API de la ventana acoplable, asumiendo que el mapeador de dispositivos y el motor de la ventana acoplable (a diferencia del enjambre de la ventana acoplable, por ejemplo) hacen que esta sea una solución no portátil. También es arriesgado eliminar archivos en el sistema de archivos mientras otro proceso (incluido el demonio de la ventana acoplable) podría estar usándolo.
nicocesar
¿Dónde está en macos?
Anthony Kong
1
* ADVERTENCIA "esto puede causar múltiples errores al eliminar y extraer contenedores en el futuro. Nunca elimine directorios /var/lib/dockerdirectamente a mano
vladkras
0

Qué tal si:

ID=$(docker inspect --format="{{.Id}}" "$1")
IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{end}}" $(docker images --filter since="$ID" -q))
echo $(printf "%s\n" "${IMAGES[@]}" | sort -u)

Imprimirá las identificaciones de la imagen del niño, con el sha256:prefijo.
También tuve lo siguiente, que agrega los nombres:

IMAGES=$(docker inspect --format="{{if eq \"$ID\" .Config.Image}}{{.Id}}{{.RepoTags}}{{end}}" $(docker images --filter since="$ID" -q))

  • ID= Obtiene la identificación completa de la imagen
  • IMAGES= Obtiene todas las imágenes secundarias que tienen esta imagen en la lista Image
  • echo... Elimina duplicados y hace eco de los resultados
Justin
fuente
Me gustan los condicionales del formato Go, pero tal vez sean mejores como filtro para este caso de uso.
ingyhere
0

Hice esto para encontrar niños de forma recursiva, sus etiquetas de repositorio e imprimir lo que están haciendo:

docker_img_tree() {
    for i in $(docker image ls -qa) ; do
        [ -f "/tmp/dii-$i"  ] || ( docker image inspect $i > /tmp/dii-$i)
        if grep -qiE 'Parent.*'$1 /tmp/dii-$i ; then
            echo "$2============= $i (par=$1)"
            awk '/(Cmd|Repo).*\[/,/\]/' /tmp/dii-$i | sed "s/^/$2/"
            docker_img_tree $i $2===
        fi
    done
}

No olvide eliminar / tmp / dii- * si su host no es seguro.

android.weasel
fuente
-6

También me enfrentaba al mismo problema. Pasos seguidos a continuación para resolver el problema.

Detener todos los contenedores en ejecución

docker stop $ (docker ps -aq) Eliminar todos los contenedores

docker rm $ (docker ps -aq) Eliminar todas las imágenes

docker rmi $ (imágenes de docker -q)

Ram Pasupula
fuente
esta respuesta no cumple con el requisito del interrogador.
Ankur Loriya
Deje de comentar cosas que las personas podrían copiar y pegar y hacer cosas dañinas.
nicocesar