¿Cómo eliminar imágenes de un registro privado de acopladores?

162

Ejecuto un registro privado de acopladores y quiero eliminar todas las imágenes, excepto las latestde un repositorio. No quiero eliminar todo el repositorio, solo algunas de las imágenes que contiene. Los documentos de la API no mencionan una forma de hacer esto, pero seguramente es posible.

León
fuente
1
La respuesta aceptada ya no es correcta (aunque definitivamente es muy buena). Puede eliminar imágenes usando DELETE / v2 / <name> / manifests / <reference>: docs.docker.com/registry/spec/api/#deleting-an-image
Michael Zelensky

Respuestas:

116

Actualmente no puede usar la API de registro para esa tarea. Solo le permite eliminar un repositorio o una etiqueta específica.

En general, eliminar un repositorio significa que todas las etiquetas asociadas a este repositorio se eliminan.

Eliminar una etiqueta significa que se elimina la asociación entre una imagen y una etiqueta.

Ninguna de las anteriores eliminará una sola imagen. Se dejan en su disco.


Solución alterna

Para esta solución, debe tener sus imágenes acopladas almacenadas localmente.

Una solución alternativa para su solución sería eliminar todas las etiquetas menos las últimas y, por lo tanto, eliminar potencialmente la referencia a las imágenes asociadas. Luego puede ejecutar este script para eliminar todas las imágenes, a las que no hace referencia ninguna etiqueta o la ascendencia de cualquier imagen utilizada.

Terminología (imágenes y etiquetas)

Considere un gráfico de imagen como esta, donde las letras mayúsculas ( A, B, ...) representan identificadores de imagen cortos y <-medios que una imagen se basa en otra imagen:

 A <- B <- C <- D

Ahora agregamos etiquetas a la imagen:

 A <- B <- C <- D
           |    |
           |    <version2>
           <version1>

Aquí, la etiqueta hace <version1>referencia a la imagen Cy la etiqueta hace <version2>referencia a la imagen D.

Refinando tu pregunta

En tu pregunta dijiste que querías eliminar

todas las imágenes menos la latest

. Ahora, esta terminología no es del todo correcta. Has mezclado imágenes y etiquetas. Mirando el gráfico, creo que estaría de acuerdo en que la etiqueta <version2>representa la última versión. De hecho, de acuerdo con esta pregunta , puede tener una etiqueta que represente la última versión:

 A <- B <- C <- D
           |    |
           |    <version2>
           |    <latest>
           <version1>

Como la <latest>etiqueta hace referencia a la imagen, Dle pregunto: ¿realmente desea eliminar todo menos la imagen D? ¡Probablemente no!

¿Qué sucede si eliminas una etiqueta?

Si elimina la etiqueta <version1>con la API REST de Docker, obtendrá esto:

 A <- B <- C <- D
                |
                <version2>
                <latest>

Recuerde: ¡ Docker nunca eliminará una imagen! Incluso si lo hizo, en este caso no puede eliminar una imagen, ya que la imagen Ces parte de la ascendencia de la imagen Dque está etiquetada.

Incluso si usa este script , no se eliminará ninguna imagen.

Cuando una imagen puede ser borrada

Bajo la condición de que pueda controlar cuándo alguien puede jalar o empujar a su registro (por ejemplo, deshabilitando la interfaz REST). Puede eliminar una imagen de un gráfico de imagen si ninguna otra imagen se basa en ella y ninguna etiqueta hace referencia a ella.

Tenga en cuenta que en el siguiente gráfico, la imagen Dse no basada en Csino en B. Por lo tanto, Dno depende de C. Si elimina la etiqueta <version1>en este gráfico, la imagen Cno será utilizada por ninguna imagen y este script puede eliminarla.

 A <- B <--------- D
      \            |
       \           <version2>
        \          <latest>
         \ <- C
              |
              <version1>

Después de la limpieza, su gráfico de imagen se ve así:

 A <- B <- D
           |
           <version2>
           <latest>

¿Es esto lo que quieres?

Konrad Kleine
fuente
Tienes razón: la eliminación de las etiquetas en realidad no eliminó las imágenes asociadas. ¿Hay alguna forma de eliminar las imágenes que tienen acceso ssh al cuadro, entonces?
Leo
1
Edité mi respuesta con una solución alternativa que hace el trabajo por mí. Espero que ayude.
Konrad Kleine
Eso es exactamente: entiendo que las imágenes se construyen de forma incremental, pero necesito una forma de podar esas ramas muertas. ¡Gracias!
Leo
@ KonradKleine, ¿cómo se hace el gráfico de imágenes de la lista de imágenes que obtiene del registro?
Rafael Barros
9
La recolección de basura finalmente llegó al Registro v2.4.0 docs.docker.com/registry/garbage-collection
Markus Lindberg el
68

Me he enfrentado al mismo problema con mi registro y luego probé la solución que figura a continuación desde una página de blog. Funciona.

Paso 1: listado de catálogos

Puede enumerar sus catálogos llamando a esta url:

http://YourPrivateRegistyIP:5000/v2/_catalog

La respuesta tendrá el siguiente formato:

{
  "repositories": [
    <name>,
    ...
  ]
}

Paso 2: listado de etiquetas para el catálogo relacionado

Puede enumerar las etiquetas de su catálogo llamando a esta url:

http://YourPrivateRegistyIP:5000/v2/<name>/tags/list

La respuesta tendrá el siguiente formato:

{
"name": <name>,
"tags": [
    <tag>,
    ...
]

}

Paso 3: enumere el valor de manifiesto para la etiqueta relacionada

Puede ejecutar este comando en el contenedor de registro docker:

curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://localhost:5000/v2/<name>/manifests/<tag> 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}'

La respuesta tendrá el siguiente formato:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Ejecute el comando que se muestra a continuación con el valor de manifiesto:

curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/<name>/manifests/sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Paso 4: eliminar los manifiestos marcados

Ejecute este comando en su contenedor de registro de docker:

bin/registry garbage-collect  /etc/docker/registry/config.yml  

Aquí está mi config.yml

root@c695814325f4:/etc# cat /etc/docker/registry/config.yml
version: 0.1
log:
  fields:
  service: registry
storage:
    cache:
        blobdescriptor: inmemory
    filesystem:
        rootdirectory: /var/lib/registry
    delete:
        enabled: true
http:
    addr: :5000
    headers:
        X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
Yavuz Sert
fuente
Los comandos tuvieron éxito, incluidos los mensajes sobre " Deleteing blob: /docker/..." pero no cambiaron el espacio en disco utilizado. Usando bin / Registry github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979
3
Para eliminar una imagen, debe ejecutar el contenedor de registro con REGISTRY_STORAGE_DELETE_ENABLED = parámetro verdadero.
Adil
3
Establecí el valor "eliminar: habilitado" en verdadero en el archivo /etc/docker/registry/config.yml. Para esta configuración no es necesario establecer la variable REGISTRY_STORAGE_DELETE_ENABLED @AdilIlhan
Yavuz Sert el
re: paso 3, ¿el "Docker-Content-Digest" debe estar en el encabezado? (no lo veo en la salida del rizo). ¿Debería poder ver los resultados de marcar un manifiesto antes de eliminar los manifiestos marcados para saber que lo marqué correctamente? Parece darme una respuesta 202 sin importar lo que le envíe.
SteveW
la Docker-Content-Digestparte debe estar en minúsculas (probada en el motor Docker v18.09.2) es decircurl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://localhost:5000/v2/<name>/manifests/<tag> 2>&1 | grep docker-content-digest | awk '{print ($3)}'
Muhammad Abdurrahman,
63

El v2registro actual ahora admite la eliminación a través deDELETE /v2/<name>/manifests/<reference>

Ver: https://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image

Uso de trabajo: https://github.com/byrnedo/docker-reg-tool

Editar: el manifiesto <reference>anterior se puede recuperar de solicitar a

GET /v2/<name>/manifests/<tag>

y comprobando el Docker-Content-Digestencabezado en la respuesta.

Edición 2: puede que tenga que ejecutar su registro con el siguiente conjunto de entorno:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: puede que tenga que ejecutar la recolección de basura para liberar este espacio en disco: https://docs.docker.com/registry/garbage-collection/

byrnedo
fuente
3
Algunas otras respuestas aquí muestran cómo extraer el hash de referencia manifiesto. Esto es necesario para traducir una etiqueta en algo para pasar DELETE.
JamesThomasMoon1979
Obteniendo error no soportado. He dado el valor de resumen adecuado como referencia.
Winster
1
@ JamesThomasMoon1979 actualizado con instrucciones sobre cómo obtener el hash de referencia.
byrnedo
11
en V2 también y obteniendo error{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem
1
Consulte la edición @Gadelkareem
byrnedo
17

Problema 1

Usted mencionó que era su registro de acoplador privado, por lo que probablemente deba verificar la API de registro en lugar del documento de la API de registro de Hub , que es el enlace que proporcionó.

Problema 2

La API de registro de Docker es un protocolo cliente / servidor, depende de la implementación del servidor si se eliminan las imágenes en el back-end. (Supongo)

DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)

Explicación detallada

A continuación, demuestro cómo funciona ahora desde su descripción como entiendo sus preguntas.

Ejecutas, ejecutas el registro privado de Docker, yo uso el predeterminado y escucho en el 5000puerto

docker run -d -p 5000:5000 registry

Luego etiqueto la imagen local y la presiono.

$ docker tag ubuntu localhost:5000/ubuntu
$ docker push localhost:5000/ubuntu
The push refers to a repository [localhost:5000/ubuntu] (len: 1)
Sending image list
Pushing repository localhost:5000/ubuntu (1 tags)
511136ea3c5a: Image successfully pushed
d7ac5e4f1812: Image successfully pushed
2f4b4d6a4a06: Image successfully pushed
83ff768040a0: Image successfully pushed
6c37f792ddac: Image successfully pushed
e54ca5efa2e9: Image successfully pushed
Pushing tag for rev [e54ca5efa2e9] on {http://localhost:5000/v1/repositories/ubuntu/tags/latest}

Después de eso, puede usar la API de registro para verificar que exista en su registro privado de acopladores

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags
{"latest": "e54ca5efa2e962582a223ca9810f7f1b62ea9b5c3975d14a5da79d3bf6020f37"}

¡Ahora puedo eliminar la etiqueta usando esa API!

$ curl -X DELETE localhost:5000/v1/repositories/ubuntu/tags/latest
true

Verifique nuevamente, la etiqueta no existe en su servidor de registro privado

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
Larry Cai
fuente
2
Por favor, vea mi respuesta a continuación para obtener una explicación de por qué este comando no ayuda.
Konrad Kleine
2
Elimina etiquetas pero no los datos de la imagen. Esto último lo hace el script al que hago referencia después de haber eliminado una etiqueta. También uso la API de registro para eliminar una etiqueta.
Konrad Kleine
3
depende de la implementación de cada servidor API de registro, desde el punto de vista del protocolo, no existe.
Larry Cai
Tienes razón en que depende de la implementación, pero estaba / estoy buscando una manera de eliminar los datos de la registryimagen de la imagen canónica . SSHing y ejecutar un script funciona, incluso si no es ideal. Como nota al margen, sigo pensando que es una API incompleta: puede eliminar etiquetas, pero si lo obtiene /images, todavía verá todos los datos de imagen restantes.
Leo
Gracias por mencionar Registry API, estaba buscando una forma de ELIMINAR una imagen en el registro privado. Este método es lo suficientemente bueno para mí, incluso si solo se trata de "quitar etiquetas" sin liberar espacio en disco.
Howard Lee
16

Esto es realmente feo pero funciona, el texto se prueba en el registro: 2.5.1. No logré que la eliminación funcione sin problemas incluso después de actualizar la configuración para habilitar la eliminación. La identificación fue realmente difícil de recuperar, tuve que iniciar sesión para obtenerla, tal vez algún malentendido. De todos modos, lo siguiente funciona:

  1. Inicie sesión en el contenedor

    docker exec -it registry sh
    
  2. Defina variables que coincidan con su contenedor y la versión del contenedor:

    export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. Mover al directorio de registro:

    cd /var/lib/registry/docker/registry/v2
    
  4. Eliminar archivos relacionados con su hash:

    find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. Eliminar manifiestos:

    rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. Cerrar sesión

    exit
    
  7. Ejecute el GC:

    docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. Si todo se hizo correctamente, se muestra información sobre los blobs eliminados.

hirro
fuente
Seguí los mismos pasos mencionados anteriormente, pero cuando verifiqué el uso del disco de Docker Registry {container} se mostraba igual que antes de seguir los pasos ... ¿Alguna idea de por qué?
Savio Mathew
no funciona Fácil de verificar. Cuando haces los siguientes pasos y presionas el repositorio nuevamente, dice "064794e955a6: Layer ya existe"
Oduvan
8

Hay algunos clientes (en Python, Ruby, etc.) que hacen exactamente eso. Para mi gusto, no es sostenible instalar un tiempo de ejecución (por ejemplo, Python) en mi servidor de registro, ¡solo para mantener mi registro!


Entonces deckschrubberes mi solución:

go get github.com/fraunhoferfokus/deckschrubber
$GOPATH/bin/deckschrubber

Las imágenes anteriores a una edad determinada se eliminan automáticamente. La edad puede ser especificado usando -year, -month, -day, o una combinación de ellos:

$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000

ACTUALIZACIÓN : aquí hay una breve introducción sobre deckschrubber.

Yan Foto
fuente
1
deckschrubberes bastante bueno, muy fácil de instalar (binario único) y le permite eliminar imágenes por nombre (con coincidencia de expresiones regulares) y por antigüedad.
RichVel
ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon
@scythargon es imposible decir si su especificación es correcta.
jitter
Advertencia: literalmente no hice nada en mi repositorio.
Richard Warburton el
Desafortunadamente, no eliminará imágenes que no tengan etiquetas. Entonces, si continúa presionando imágenes en una sola etiqueta, solo verificará la etiquetada, no las otras.
Krystian
1

Brevemente;

1) Debe escribir el siguiente comando para RepoDigests de un docker repo;

## docker inspect <registry-host>:<registry-port>/<image-name>:<tag>
> docker inspect 174.24.100.50:8448/example-image:latest


[
    {
        "Id": "sha256:16c5af74ed970b1671fe095e063e255e0160900a0e12e1f8a93d75afe2fb860c",
        "RepoTags": [
            "174.24.100.50:8448/example-image:latest",
            "example-image:latest"
        ],
        "RepoDigests": [
            "174.24.100.50:8448/example-image@sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6"
        ],
...
...

$ {digest} = sha256: 5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6

2) Usar la API REST del registro

  ##curl -u username:password -vk -X DELETE registry-host>:<registry-port>/v2/<image-name>/manifests/${digest}


  >curl -u example-user:example-password -vk -X DELETE http://174.24.100.50:8448/v2/example-image/manifests/sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6

Debería obtener un 202 Aceptado para una invocación exitosa.

3-) Ejecutar recolector de basura

docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml

registro - nombre del contenedor de registro.

Para una explicación más detallada, ingrese la descripción del enlace aquí

fgul
fuente
0

Debajo de Bash Script Elimina todas las etiquetas ubicadas en el registro, excepto la última.

for D in /registry-data/docker/registry/v2/repositories/*; do
if [ -d "${D}" ]; then
    if [ -z "$(ls -A ${D}/_manifests/tags/)" ]; then
        echo ''
    else
        for R in $(ls -t ${D}/_manifests/tags/ | tail -n +2); do
            digest=$(curl -k -I -s -H -X GET http://xx.xx.xx.xx:5000/v2/$(basename  ${D})/manifests/${R} -H 'accept: application/vnd.docker.distribution.manifest.v2+json'  | grep Docker-Content-Digest | awk '{print $2}' )
            url="http://xx.xx.xx.xx:5000/v2/$(basename  ${D})/manifests/$digest"
            url=${url%$'\r'}
            curl -X DELETE -k -I -s   $url -H 'accept: application/vnd.docker.distribution.manifest.v2+json' 
        done
    fi
fi
done

Después de esta carrera

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Shrijan Tiwari
fuente
¿Podría elaborar un poco más sobre cómo usar este excelente script?
Khalil Gharbaoui
0

Script simple de ruby ​​basado en esta respuesta: Registry_cleaner .

Puede ejecutarlo en una máquina local:

./registry_cleaner.rb --host=https://registry.exmpl.com --repository=name --tags_count=4

Y luego, en la máquina de registro, elimine los blobs con /bin/registry garbage-collect /etc/docker/registry/config.yml.

Ilya Krigouzov
fuente
0

Aquí hay un guión basado en la respuesta de Yavuz Sert. Elimina todas las etiquetas que no son la última versión, y su etiqueta es superior a 950.

#!/usr/bin/env bash


CheckTag(){
    Name=$1
    Tag=$2

    Skip=0
    if [[ "${Tag}" == "latest" ]]; then
        Skip=1
    fi
    if [[ "${Tag}" -ge "950" ]]; then
        Skip=1
    fi
    if [[ "${Skip}" == "1" ]]; then
        echo "skip ${Name} ${Tag}"
    else
        echo "delete ${Name} ${Tag}"
        Sha=$(curl -v -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://127.0.0.1:5000/v2/${Name}/manifests/${Tag} 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}')
        Sha="${Sha/$'\r'/}"
        curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE "http://127.0.0.1:5000/v2/${Name}/manifests/${Sha}"
    fi
}

ScanRepository(){
    Name=$1
    echo "Repository ${Name}"
    curl -s http://127.0.0.1:5000/v2/${Name}/tags/list | jq '.tags[]' |
    while IFS=$"\n" read -r line; do
        line="${line%\"}"
        line="${line#\"}"
        CheckTag $Name $line
    done
}


JqPath=$(which jq)
if [[ "x${JqPath}" == "x" ]]; then
  echo "Couldn't find jq executable."
  exit 2
fi

curl -s http://127.0.0.1:5000/v2/_catalog | jq '.repositories[]' |
while IFS=$"\n" read -r line; do
    line="${line%\"}"
    line="${line#\"}"
    ScanRepository $line
done
alonana
fuente