개인 도커 레지스트리에서 이미지를 삭제하는 방법은 무엇입니까?


163

개인 도커 레지스트리를 실행 latest하고 리포지토리에서 이미지를 제외한 모든 이미지를 삭제하고 싶습니다 . 전체 저장소를 삭제하고 싶지 않으며 그 안에있는 이미지 중 일부만 삭제하고 싶습니다. API 문서는 이 작업을 수행 할 수있는 방법을 언급하지 않지만, 분명히 그것은 가능?


2
수락 된 답변은 더 이상 정확하지 않습니다 (매우 훌륭하지만). 당신은 DELETE / V2 / <이름>을 사용하여 이미지를 제거 할 수 있습니다 / 매니페스트 / <참고> : docs.docker.com/registry/spec/api/#deleting-an-image
마이클 Zelensky

답변:


116

현재 해당 작업에 레지스트리 API를 사용할 수 없습니다. 리포지토리 또는 특정 태그 만 삭제할 수 있습니다.

일반적으로 리포지토리를 삭제하면이 리포지토리와 관련된 모든 태그가 삭제됩니다.

태그를 삭제하면 이미지와 태그 간의 연결이 삭제됩니다.

위의 어느 것도 단일 이미지를 삭제하지 않습니다. 그들은 당신의 디스크에 남아 있습니다.


해결 방법

이 문제를 해결하려면 도커 이미지를 로컬로 저장해야합니다.

솔루션의 해결 방법은 최신 태그를 제외한 모든 태그를 삭제하여 관련 이미지에 대한 참조를 제거하는 것입니다. 그런 다음 이 스크립트 를 실행 하여 태그 나 사용 된 이미지의 조상에서 참조하지 않는 모든 이미지를 제거 할 수 있습니다 .

용어 (이미지 및 태그)

대문자는 (여기서, 이와 같은 화상 그래프 고려 A, B...) 짧은 화상 ID와 대표 <-화상이 다른 화상에 기초되는 것을 의미 :

 A <- B <- C <- D

이제 그림에 태그를 추가합니다 :

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

여기서 태그 <version1>는 이미지 C<version2>참조하고 태그 는 이미지를 참조합니다 D.

질문 다듬기

귀하의 질문에 당신은 당신이 제거하고 싶다고 말했다

모든 이미지를 제외한 latest

. 이제이 용어는 정확하지 않습니다. 이미지와 태그를 혼합했습니다. 그래프를 보면 태그 <version2>가 최신 버전을 나타내는 것에 동의한다고 생각합니다 . 실제로이 질문 에 따르면 최신 버전을 나타내는 태그를 사용할 수 있습니다.

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

<latest>태그가 이미지를 참조 하기 때문에 D나는 당신에게 묻습니다 : 당신은 정말로 이미지를 제외한 모든 것을 삭제 하시겠습니까 D? 아마 아닙니다!

태그를 삭제하면 어떻게됩니까?

<version1>Docker REST API를 사용 하여 태그 를 삭제하면 다음을 얻을 수 있습니다.

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

기억하십시오 : Docker는 이미지를 절대 삭제하지 않습니다! 이 경우에도 C이미지 D는 태그가 지정된 이미지의 상위 항목 이므로 이미지를 삭제할 수 없습니다 .

이 스크립트 를 사용하더라도 이미지는 삭제되지 않습니다.

이미지를 삭제할 수있는 경우

누군가가 레지스트리를 가져 오거나 푸시 할 수있는시기를 제어 할 수있는 조건 하에서 (예 : REST 인터페이스 비활성화). 이미지를 기반으로하는 다른 이미지가없고 이미지를 참조하지 않는 경우 이미지 그래프에서 이미지를 삭제할 수 있습니다.

다음 그래프, 이미지가 통지 D되어 있지 기반으로 C하지만,에 B. 따라서에 D의존하지 않습니다 C. <version1>이 그래프에서 태그를 삭제 하면 이미지 C가 이미지 를 사용하지 않으며이 스크립트 는이를 제거 할 수 있습니다.

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

정리 후 이미지 그래프는 다음과 같습니다.

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

이것이 당신이 원하는 것입니까?


맞습니다. 태그를 삭제해도 실제로 연결된 이미지는 삭제되지 않습니다. 상자에 ssh 액세스 권한이 부여 된 이미지를 삭제할 수있는 방법이 있습니까?
Leo

1
나는 나를 위해 일하는 해결 방법으로 답변을 편집했습니다. 도움이 되길 바랍니다.
Konrad Kleine

이미지가 점진적으로 구축된다는 것을 이해하지만 죽은 나뭇 가지를 잘라내는 방법이 필요합니다. 감사!
Leo

@KonradKleine 레지스트리에서 가져온 이미지 목록에서 이미지 그래프를 어떻게 만들 수 있습니까?
Rafael Barros

9
쓰레기 수집은 마지막으로 레지스트리 v2.4.0에에 도착했습니다 docs.docker.com/registry/garbage-collection
마르쿠스 린드버그

68

내 레지스트리와 동일한 문제에 직면 한 다음 블로그 페이지에서 아래 나열된 솔루션을 시도했습니다. 효과가있다.

1 단계 : 카탈로그 나열

이 URL을 호출하여 카탈로그를 나열 할 수 있습니다.

http://YourPrivateRegistyIP:5000/v2/_catalog

응답 형식은 다음과 같습니다.

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

2 단계 : 관련 카탈로그에 대한 태그 나열

이 URL을 호출하여 카탈로그의 태그를 나열 할 수 있습니다.

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

응답 형식은 다음과 같습니다.

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

}

3 단계 : 관련 태그의 매니페스트 값 나열

docker registry container에서이 명령을 실행할 수 있습니다.

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)}'

응답 형식은 다음과 같습니다.

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

매니페스트 값으로 아래 제공된 명령을 실행하십시오.

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

4 단계 : 표시된 매니페스트 삭제

docker registy 컨테이너에서이 명령을 실행하십시오.

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

여기 내 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

" Deleteing blob: /docker/..." 에 대한 메시지를 포함하여 명령이 성공 했지만 사용 된 디스크 공간을 변경하지 않았습니다. 사용 빈 / 레지스트리 github.com/docker/distribution의 v2.4.1을 .
JamesThomasMoon1979

3
이미지를 삭제하려면 REGISTRY_STORAGE_DELETE_ENABLED = true 매개 변수를 사용하여 레지스트리 컨테이너를 실행해야합니다.
Adil

3
/etc/docker/registry/config.yml 파일에서 "delete : enabled"값을 true로 설정했습니다. 이 구성 설정할 필요가 변수 @AdilIlhan을 REGISTRY_STORAGE_DELETE_ENABLED 없습니다
야 부즈 르트에게

다시 : 3 단계, "Docker-Content-Digest"가 헤더에 있어야합니까? (컬 출력에서 ​​보이지 않음). 표시된 매니페스트를 삭제하기 전에 매니페스트를 마킹 한 결과를보고 성공적으로 표시 한 것을 알 수 있습니까? 내가 보낸 내용에 관계없이 202 응답을주는 것 같습니다.
SteveW

Docker-Content-Digest부분 즉, 소문자 (고정 표시기 엔진 v18.09.2 테스트)에 있어야합니다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)}'
무하마드 압 두라 만

63

현재 v2레지스트리는 다음을 통한 삭제를 지원합니다.DELETE /v2/<name>/manifests/<reference>

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

작업 사용법 : https://github.com/byrnedo/docker-reg-tool

편집 : <reference>위 의 매니페스트 는 요청에서 검색 할 수 있습니다

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

Docker-Content-Digest응답 에서 헤더를 확인하는 단계 .

편집 2 : 다음 env 세트로 레지스트리를 실행해야 할 수도 있습니다.

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3 :이 디스크 공간을 확보하기 위해 가비지 수집을 실행해야 할 수도 있습니다 : https://docs.docker.com/registry/garbage-collection/


3
다른 답변은 매니페스트 참조 해시를 추출하는 방법을 보여줍니다. 태그를 전달할 무언가로 변환하는 데 필요합니다 DELETE.
JamesThomasMoon1979

지원되지 않는 오류가 발생했습니다. 적절한 다이제스트 값을 참조로 제공했습니다.
Winster February

1
@ JamesThomasMoon1979는 참조 해시를 얻는 방법에 대한 지침으로 업데이트되었습니다.
byrnedo

11
V2에서도 오류가 발생했습니다{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Gadelkareem

1
편집 @Gadelkareem
byrnedo

17

문제 1

개인 도커 레지스트리라고 언급 했으므로 제공 한 링크 인 Hub registry API doc 대신 Registry API 를 확인해야합니다 .

문제 2

docker registry API는 클라이언트 / 서버 프로토콜이며 백엔드에서 이미지를 제거할지 여부는 서버의 구현에 달려 있습니다. (추측)

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

세부 설명

아래는 귀하의 질문에 대한 나의 이해로서 귀하의 설명에서 현재 어떻게 작동하는지 보여줍니다.

개인 도커 레지스트리를 실행하면 기본 디렉토리를 사용하고 5000포트 에서 수신 대기합니다.

docker run -d -p 5000:5000 registry

그런 다음 로컬 이미지에 태그를 지정하고 밀어 넣습니다.

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

그런 다음 Registry API 를 사용 하여 개인 도커 레지스트리에 존재하는지 확인할 수 있습니다.

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

이제 해당 API를 사용하여 태그를 삭제할 수 있습니다 !!

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

다시 확인하십시오. 개인 레지스트리 서버에 태그가 없습니다

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}

2
이 명령이 도움이되지 않는 이유에 대한 설명은 아래 답변을 참조하십시오.
Konrad Kleine

2
태그는 삭제하지만 이미지 데이터는 삭제하지 않습니다. 후자는 태그를 삭제 한 후 참조하는 스크립트에 의해 수행됩니다. 또한 레지스트리 API를 사용하여 태그를 삭제합니다.
Konrad Kleine

3
프로토콜의 관점에서 볼 때 존재하지 않는 것은 각 레지스트리 API 서버의 구현에 달려 있습니다.
Larry Cai

당신은 그것이 구현에 달려 있다고 생각하지만, 표준 registry이미지 에서 이미지 데이터를 삭제하는 방법을 찾고있었습니다 . 스크립트가 이상적이지 않더라도 SSH를 실행하고 실행하는 것이 좋습니다. 참고로 여전히 불완전한 API라고 생각합니다. 태그를 삭제할 수 있지만 GET /images을 사용하면 남은 이미지 데이터가 모두 표시됩니다.
레오

Registry API를 언급 해 주셔서 감사합니다. 개인 레지스트리에서 이미지를 삭제하는 방법을 찾고있었습니다. 이 방법은 실제로 디스크 공간을 확보하지 않고 단지 '언 태그'하는 경우에도 충분합니다.
Howard Lee

16

이것은 실제로 추악하지만 작동하지만 텍스트는 레지스트리에서 테스트되었습니다 : 2.5.1. 삭제를 활성화하기 위해 구성을 업데이트 한 후에도 삭제가 원활하게 작동하지 못했습니다. ID를 찾기가 정말 어려웠으며, 로그인하기 위해 로그인해야했습니다. 어쨌든 다음과 같이 작동합니다.

  1. 컨테이너에 로그인

    docker exec -it registry sh
    
  2. 컨테이너 및 컨테이너 버전과 일치하는 변수를 정의하십시오.

    export NAME="google/cadvisor"
    export VERSION="v0.24.1"
    
  3. 레지스트리 디렉토리로 이동하십시오.

    cd /var/lib/registry/docker/registry/v2
    
  4. 해시와 관련된 파일을 삭제하십시오.

    find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. 매니페스트 삭제 :

    rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. 로그 아웃

    exit
    
  7. GC를 실행하십시오.

    docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. 모든 작업이 올바르게 완료되면 삭제 된 얼룩에 대한 정보가 표시됩니다.


위에서 언급 한 것과 동일한 단계를 수행했지만 도커 레지스트리 {container} 디스크 사용량을 확인했을 때 단계를 수행하기 전과 동일하게 표시되었습니다 ... 어떻게 생각하십니까?
Savio Mathew

작동하지 않습니다. 확인하기 쉽습니다. 다음 단계를 수행하고 리포지를 다시 누르면 "064794e955a6 : 레이어가 이미 있습니다"
Oduvan

8

정확히 수행하는 클라이언트 (Python, Ruby 등)가 있습니다. 내 취향을 위해 레지스트리 서버에 런타임 (예 : Python)을 설치하는 것이 아니라 레지스트리를 유지하기 위해 지속 가능하지 않습니다!


그래서 deckschrubber내 솔루션입니다 :

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

특정 연령보다 오래된 이미지는 자동으로 삭제됩니다. 나이를 사용하여 지정할 수 있습니다 -year, -month, -day, 또는 이들의 조합 :

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

업데이트 : deckschrubber에 대한 간단한 소개 입니다.


1
deckschrubber설치가 매우 쉽고 (단일 바이너리), 이름 (정규식 일치)과 나이로 이미지를 삭제할 수 있습니다.
RichVel

ERRO[0000] Could not delete image! repo=.... tag=latest: /
scythargon

@ scythargon 당신의 사양이 맞다고 말할 수는 없습니다.
지터

경고 : 내 저장소에는 문자가 없습니다.
Richard Warburton

불행히도 태그가없는 이미지는 제거되지 않습니다. 따라서 하나의 태그에 이미지를 계속 밀어 넣으면 다른 태그가 아닌 태그가 지정된 태그 만 확인합니다.
Krystian

1

간단히;

1) docker repo의 RepoDigests에 다음 명령을 입력해야합니다.

## 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) 레지스트리 REST API 사용

  ##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

성공적인 호출을 위해서는 202 Accepted를 받아야합니다.

3) 가비지 콜렉터 실행

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

registry — 레지스트리 컨테이너 이름입니다.

자세한 설명을 보려면 여기에 링크 설명을 입력하십시오



0

Bash Script 아래 최신을 제외한 레지스트리에있는 모든 태그를 삭제합니다.

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

이 실행 후

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml

이 훌륭한 스크립트를 사용하는 방법에 대해 좀 더 자세히 설명해 주시겠습니까?
Khalil Gharbaoui 님의

0

답변을 기반으로 한 간단한 루비 스크립트 : registry_cleaner .

로컬 컴퓨터에서 실행할 수 있습니다.

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

그런 다음 레지스트리 시스템에서으로 얼룩을 제거하십시오 /bin/registry garbage-collect /etc/docker/registry/config.yml.


0

다음은 Yavuz Sert의 답변을 기반으로 한 스크립트입니다. 최신 버전이 아닌 모든 태그를 삭제하고 태그가 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.