Como excluir imagens de um registro do docker privado?

162

Eu executo um registro de janela de encaixe privado e desejo excluir todas as imagens, exceto as latestde um repositório. Não quero excluir o repositório inteiro, apenas algumas das imagens dentro dele. Os documentos da API não mencionam uma maneira de fazer isso, mas certamente é possível?

Leo
fonte
1
A resposta aceita não está mais correta (embora seja definitivamente muito boa). Você pode remover imagens usando DELETE / v2 / <name> / manifestests / <reference>: docs.docker.com/registry/spec/api/#deleting-an-image
Michael Zelensky

Respostas:

116

Atualmente, você não pode usar a API do Registro para essa tarefa. Permite apenas excluir um repositório ou uma tag específica.

Em geral, excluir um repositório significa que todas as tags associadas a este repositório são excluídas.

Excluir uma tag significa que a associação entre uma imagem e uma tag é excluída.

Nenhuma das opções acima excluirá uma única imagem. Eles são deixados no seu disco.


Gambiarra

Para esta solução alternativa, você precisa ter as imagens do docker armazenadas localmente.

Uma solução alternativa para sua solução seria excluir todas as tags, exceto as mais recentes, e, assim, remover potencialmente a referência às imagens associadas. Em seguida, você pode executar esse script para remover todas as imagens que não são referenciadas por nenhuma tag ou ancestralidade de qualquer imagem usada.

Terminologia (imagens e tags)

Considere um gráfico de imagem como este, onde as letras maiúsculas ( A, B...) representam IDs curtos da imagem e <-significa que uma imagem é baseada em outra imagem:

 A <- B <- C <- D

Agora, adicionamos tags à imagem:

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

Aqui, a tag faz <version1>referência à imagem Ce a tag faz <version2>referência à imagem D.

Refinando sua pergunta

Na sua pergunta, você disse que queria remover

todas as imagens, exceto a latest

. Agora, essa terminologia não está totalmente correta. Você misturou imagens e tags. Olhando para o gráfico, acho que você concorda que a tag <version2>representa a versão mais recente. De fato, de acordo com esta pergunta, você pode ter uma tag que representa a versão mais recente:

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

Como a <latest>tag faz referência à imagem D, pergunto: você realmente deseja excluir tudo menos a imagem D? Provavelmente não!

O que acontece se você excluir uma tag?

Se você excluir a tag <version1>usando a API REST do Docker, obterá o seguinte:

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

Lembre-se: o Docker nunca excluirá uma imagem! Mesmo assim, neste caso, não pode excluir uma imagem, pois ela Cfaz parte da ancestralidade da imagem Dque está marcada.

Mesmo se você usar esse script , nenhuma imagem será excluída.

Quando uma imagem pode ser excluída

Sob a condição de que você pode controlar quando alguém pode puxar ou enviar por push ao seu registro (por exemplo, desativando a interface REST). Você pode excluir uma imagem de um gráfico de imagem se nenhuma outra imagem for baseada nela e nenhuma tag se referir a ela.

Note que no gráfico a seguir, a imagem Dé não baseado em Cmas B. Portanto, Dnão depende C. Se você excluir a tag <version1>neste gráfico, a imagem Cnão será usada por nenhuma imagem e esse script poderá removê-la.

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

Após a limpeza, seu gráfico de imagem fica assim:

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

É isso que voce quer?

Konrad Kleine
fonte
Você está certo - a exclusão das tags não excluiu as imagens associadas. Existe alguma maneira de excluir as imagens com acesso ssh à caixa?
Leo
1
Eu editei minha resposta com uma solução alternativa que faz o trabalho para mim. Espero que ajude.
Konrad Kleine
É exatamente isso - entendo que as imagens são construídas de forma incremental, mas preciso de uma maneira de podar esses galhos mortos. Obrigado!
Leo
@KonradKleine como você cria o gráfico de imagens da lista de imagens que você obtém do registro?
Rafael Barros
9
Coleta de lixo chegou finalmente ao Registro v2.4.0 docs.docker.com/registry/garbage-collection
Markus Lindberg
68

Eu enfrentei o mesmo problema com meu registro e tentei a solução listada abaixo em uma página de blog. Funciona.

Etapa 1: Listando Catálogos

Você pode listar seus catálogos chamando este URL:

http://YourPrivateRegistyIP:5000/v2/_catalog

A resposta estará no seguinte formato:

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

Etapa 2: listando tags para o catálogo relacionado

Você pode listar as tags do seu catálogo chamando este URL:

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

A resposta estará no seguinte formato:

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

}

Etapa 3: listar o valor do manifesto para a tag relacionada

Você pode executar este comando no contêiner de registro do 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)}'

A resposta estará no seguinte formato:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Execute o comando fornecido abaixo com o valor do manifesto:

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

Etapa 4: excluir manifestos marcados

Execute este comando no seu contêiner do docker registry:

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

Aqui está o meu 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
fonte
Os comandos foram bem-sucedidos, incluindo mensagens sobre " Deleteing blob: /docker/..." mas não alteraram o espaço em disco usado. Usando bin / registry github.com/docker/distribution v2.4.1 .
JamesThomasMoon1979
3
Para excluir uma imagem, você deve executar o contêiner do registro com o parâmetro REGISTRY_STORAGE_DELETE_ENABLED = true.
Adil
3
Defino o valor "delete: enabled" como true no arquivo /etc/docker/registry/config.yml. Para esta configuração, não é necessário definir a variável REGISTRY_STORAGE_DELETE_ENABLED @AdilIlhan
Yavuz Sert
re: etapa 3, o "Docker-Content-Digest" deve estar no cabeçalho? (não o vê na saída de ondulação). Devo poder ver os resultados da marcação de um manifesto antes de excluir o (s) manifesto (s) marcado (s) para saber se o marquei com sucesso? Parece me dar uma resposta 202, não importa o que eu a envie.
21418 Steve
a Docker-Content-Digestpeça deve estar em minúscula (testada no mecanismo docker v18.09.2) iecurl -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

O v2registro atual agora suporta a exclusão viaDELETE /v2/<name>/manifests/<reference>

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

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

Editar: o manifesto <reference>acima pode ser recuperado da solicitação para

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

e verificando o Docker-Content-Digestcabeçalho na resposta.

Editar 2: pode ser necessário executar seu registro com o seguinte conjunto de env:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: Talvez você precise executar a coleta de lixo para liberar esse espaço em disco: https://docs.docker.com/registry/garbage-collection/

byrnedo
fonte
3
Algumas outras respostas aqui mostram como extrair o hash de referência manifesto. Isso é necessário para converter uma tag em algo para passar DELETE.
JamesThomasMoon1979
Obtendo erro não suportado. Eu dei o valor de digestão adequado como referência.
Winster
1
@ JamesThomasMoon1979 atualizado com instruções sobre como obter o hash de referência.
Byrnedo
11
na V2 também e recebendo erro{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem 31/10
1
Confira a edição @Gadelkareem
byrnedo 16/07
17

Problema 1

Você mencionou que era seu registro da janela de encaixe privada, portanto, provavelmente você precisa verificar a API do Registro em vez do documento da API do Registro do Hub , que é o link que você forneceu.

Problema 2

A API do registro do docker é um protocolo de cliente / servidor; depende da implementação do servidor a remoção das imagens no back-end. (Eu acho)

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

Explicação detalhada

Abaixo, demonstro como funciona agora a partir da sua descrição como meu entendimento para suas perguntas.

Você executa, você executa o registro do docker privado, eu uso o padrão e ouço na 5000porta

docker run -d -p 5000:5000 registry

Depois, identifico a imagem local e empurro para dentro dela.

$ 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}

Depois disso, você pode usar a API do Registro para verificar se ela existe no registro do docker privado

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

Agora eu posso excluir a tag usando essa API !!

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

Verifique novamente, a tag não existe no seu servidor de registro privado

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
Larry Cai
fonte
2
Por favor, veja minha resposta abaixo para uma explicação de por que esse comando não ajuda.
219 Konrad Kleine
2
Você exclui tags, mas não os dados da imagem. O último é feito pelo script que eu refiro depois que você excluiu uma tag. Eu também uso a API do registro para excluir uma marca.
precisa saber é o seguinte
3
depende da implementação de cada servidor da API do registro, do ponto de vista do protocolo, ele não existe.
Larry Cai
Você está certo que depende da implementação - mas eu estava procurando uma maneira de excluir os dados da registryimagem canônica . Fazer o SSH e executar um script funciona, mesmo que não seja o ideal. Como observação, ainda acho que é uma API incompleta - você pode excluir tags, mas, se obtiver, /imagesainda verá todos os dados restantes da imagem.
Leo
Obrigado por mencionar a API do registro, estava procurando uma maneira de excluir uma imagem no registro privado. Esse método é bom o suficiente para mim, mesmo que seja apenas 'sem marcação', sem realmente liberar espaço em disco.
Howard Lee
16

Isso é realmente feio, mas funciona, o texto é testado no registro: 2.5.1. Não consegui fazer com que a exclusão funcionasse sem problemas, mesmo depois de atualizar a configuração para ativar a exclusão. O ID era realmente difícil de recuperar, tinha que fazer login para obtê-lo, talvez algum mal-entendido. De qualquer forma, o seguinte funciona:

  1. Faça login no contêiner

    docker exec -it registry sh
    
  2. Defina variáveis ​​que correspondem ao seu contêiner e sua versão:

    export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. Vá para o diretório do registro:

    cd /var/lib/registry/docker/registry/v2
    
  4. Exclua arquivos relacionados ao seu hash:

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

    rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. Sair

    exit
    
  7. Execute o GC:

    docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. Se tudo foi feito corretamente, algumas informações sobre os blobs excluídos são mostradas.

hirro
fonte
Eu segui as mesmas etapas mencionadas acima, mas quando verifiquei o uso do disco do {dock} do registro do docker, ele estava mostrando o mesmo que antes de executar as etapas ... alguma idéia do porquê?
Savio Mathew
não funciona Fácil de verificar. Quando você executa as etapas a seguir e empurra o repositório novamente, ele diz "064794e955a6: a camada já existe"
Oduvan
8

Existem alguns clientes (em Python, Ruby, etc) que fazem exatamente isso. Para meu gosto, não é sustentável instalar um tempo de execução (por exemplo, Python) no meu servidor de registro, apenas para manter meu registro em casa!


Então deckschrubberé a minha solução:

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

imagens com mais de uma idade são excluídas automaticamente. Idade pode ser especificado usando -year, -month, -dayou uma combinação dos mesmos:

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

ATUALIZAÇÃO : aqui está uma breve introdução ao deckschrubber.

Yan Foto
fonte
1
deckschrubberé muito bom - muito fácil de instalar (binário único) e permite excluir imagens por nome (com correspondência de expressões regulares) e idade.
RichVel 5/09/17
ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon
@scythargon impossível dizer se sua especificação está correta.
Jitter #
Aviso: literalmente não fez nada no meu repositório.
Richard Warburton
Infelizmente, ele não removerá imagens que não possuem tags. Portanto, se você continuar pressionando imagens em uma única tag, ela só verificará a que está marcada, não as outras.
Krystian
1

Resumidamente;

1) Você deve digitar o seguinte comando para RepoDigests de um repo de docker;

## 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) Use a API REST do 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

Você deve obter um 202 aceito para uma chamada bem-sucedida.

3-) Execute o coletor de lixo

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

registro - nome do contêiner de registro.

Para uma explicação mais detalhada, insira a descrição do link aqui

fgul
fonte
0

Script abaixo do bash Exclui todas as marcas localizadas no registro, exceto as mais recentes.

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

Após esta execução

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Shrijan Tiwari
fonte
Você poderia elaborar um pouco mais sobre como usar esse bom script?
Khalil Gharbaoui 03/10/19
0

Script ruby ​​simples com base nesta resposta: registry_cleaner .

Você pode executá-lo na máquina local:

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

E então na máquina do registro remova os blobs com /bin/registry garbage-collect /etc/docker/registry/config.yml.

Ilya Krigouzov
fonte
0

Aqui está um script baseado na resposta de Yavuz Sert. Exclui todas as tags que não são a versão mais recente e sua tag é maior que 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
fonte