소프트웨어 아키텍처/Docker

Docker registry 와 이미지 삭제

bluefriday 2018. 6. 6. 18:46
반응형


Docker image의 물리적 삭제


Docker image는 registry 에 저장될 때 image의 레이어 단위로 저장이 되며, 이렇게 저장된 레이어는 registry 의 내부에서 manifest 파일에 의해서 참조 된다. 또한 사용되지 않는 registry 의 물리적 이미지 삭제는 Registry Garbage Collection (이하 Registry GC) 에 의해서 수행되며, Registry GC가 수행될 때 사용되지 않는(어떤 manifest파일에서도 참조하지 않는) blob 파일들이 삭제 되면서 용량이 확보된다. 단계적으로 보면 아래와 같다.


1. mark phase : registry 내부의 manifest 를 검색하여 manifest 가 참조하고 있는 image layer 에 대하여 마킹

2. sweep phase : 마킹이 되어 있지 않는 image layer에 대하여 레이어 삭제 수행


하지만, 이러한 Registry GC는 자동으로 수행되지 않으며, 명시적으로 수행하여 파일을 삭제하기 위해서 아래의 작업이 필요하다.


먼저 아래와 같이 Registry Config 설정을 수정하여 삭제가 가능하도록 delete 플래그를 변경(disable -> enable) 하여야 한다. 기존에 구동되어 있는 registry가 해당 플래그가 선언되어 있지 않다면, registry config 를 수정한 후에 컨테이너를 재기동하자. 이를 위해서 registry config 또한 docker volume 으로 외부 파일로 연결하는 것이 좋다. 1)그 후에 Docker registry API 를 이용하여 삭제할 image의 content-digest 를 조회한다. 그리고 2)조회한 digest 값을 이용하여 image manifest 를 삭제한다. image manifest를 삭제할 경우 논리적 삭제만 이뤄지며 실제로 물리적으로 디스크에서 이미지가 지워지지는 않는다. 물리적 삭제를 위해서 컨테이너 내부에서 'garbage-collect 명령어를 수행하여 Registry GC를 수동으로 수행한다. 위의 1), 2)와 명시적 gc 수행 명령어는 이 글의 최하단의 '관련 명령어' 를 참조하자.  


version: 0.1

...

  storage:

    delete:

      enabled: false (-> true 로 변경)

...



Image에 삭제에 따른 storage 용량 확보

위에서 소개한 것과 같이, docker image layer는 registry 내부의 모든 manifests 파일이 참조하지 않는 레이어에 대하여만 GC 시, 삭제를 수행한다. 이와 같이 여러개의 manifests 파일이 동일한 이미지 레이어를 참조하는 구조로 인하여, 특정 이미지를 삭제 하는 것이 disk 확보와 직결되지 않을 수 있다. 아래의 그림을 예시로 보자. image A와 image B는 서로 동일한 레이어를 포함하고 있다. 이와 같은 구조는 실제 도커 이미지를 빌드할 때, 같은 베이스 이미지를 사용하여 빌드를 계속 수행할 경우 나타날 수 있다. 이런 경우에 최종 빌드 이미지만을 남겨 놓기 위하여 image A를 삭제하는 경우 image B가 참조하고 있는 layer 들로 인하여 실제로 layer a,b,c 는 삭제 되지 않는다. 이와 다른 경우로 오른쪽의 그림을 보면 하나의 베이스 이미지로 여러가지 버전이 존재 한다. image C, D, E, F 중 일부를 삭제 하는 경우 해당 image에서만 사용하고 있는 레이어도 같이 삭제하여 디스크를 확보할 수 있다. 위와 같은 이미지 레이어의 참조 구조를 고려하여 도커 이미지를 빌드하거나 혹은 삭제하는 것이 좋다.



Caching 기능에 따른 re-push 문제

위와 같은 방식을 이용하면 특정 이미지를 물리적으로 제거할 수 있다. 하지만 이렇게 Registry GC를 수행한 후에 동일한 [이미지]:[태그]로 다시 push 할 경우에는 push가 수행 되는 것처럼 보이지만 내부적으로는 실제 push가 일어나지 않는다. 따라서 push 후에 동일한 이미지명으로 pull 할 경우에도 image가 존재하지 않는다는 오류가 발생한다. 이는 docker registry config에 caching 기능이 지정되어 있어 발생한다. Registry는 image layer에 접근을 빠르게 하기 위하여 caching 기능을 기본적으로 사용하며, inmemory, redis 등의 옵션으로 사용되어진다. 이 caching 기능을 없애기 위하여 registry config 파일에서 caching 과 관련된 항목을 제거한다. 반대로 caching을 기능 없애지 않는 경우에는, cache 를 수동으로 없애기 위하여 registry 컨테이너를 재시작하는 방법 또한 가능하다. 즉, 물리적 이미지 삭제 이후에 re-push가 되지 않는 문제를 해결하기 위해서는 1) in-memory cache를 지우기 위하여 registry 컨테이너를 재시작하거나, 2)구동하는 시점부터 cache 기능을 사용하지 않도록 설정하는 방법을 사용할 수 있다. (추가적으로 캐시 저장소를 redis를 사용하는 방법이 있으나 이는 별도로 기술한다.)


하지만 cache 를 없애기 위한 위 2가지 방식과 무관하게, disk size를 줄이기 위하여 수행해야 하는 GC 수행은 반드시 registry의 image로 구동되어 있는 container가 없으며, pull/push 가 일어나지 않는 중에 수행 되어야 한다. 그렇지 않을 경우, 기존 image가 깨지거나 pull/push 중이던 image가 손상될 수 있다.


결론

위의 방법 중 어떤 방법을 사용하더라도, 실제로 GC가 일어나는 중에는 registry의 기능이 정지되어 있어야 한다. 어차피 GC를 위하여 registry의 기능을 사용하지 않는다면, 캐싱기능을 그대로 사용하고 registry를 재시작하는 방법을 권장한다. 결과적으로 registry 삭제를 통한 disk 확보는 docker image 의 multi-layer 구조를 고려하여 진행하는 것이 좋으며, 삭제하지 않을 경우에는 처음부터 충분한 공간의 storage를 확보하는 것이 좋다.



관련 명령어

registry 와 관련된 API를 활용한 명령어는 아래와 같으며, 아래의 순서대로 차례대로 명령을 수행하여 해당 이미지를 논리적/물리적으로 삭제할 수 있다.


01. registry 내부의 repository 정보 조회

$ curl -X GET <REGISTRY URL:포트>/v2/_catalog


02. repository 에 대하여 tag 정보 조회

$ curl -X GET <REGISTRY URL:포트>/v2/<REPOSITORY 이름>/tags/list


03. content digest 조회

$ curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://<REGISTRY URL:포트>/v2/<REPOSITORY 이름>/manifests/<TAG 이름> 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}'


04. manifest 삭제

$ curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://<REGISTRY URL:포트>/v2/<REPOSITORY 이름>/manifests/<DIGEST 정보>


05. GC(Garbage Collection)

$ docker exec -it registry_dev registry garbage-collect /etc/docker/registry/config.yml