Private Registry

Q·2022년 7월 24일
0

Docker

목록 보기
9/14

Contents

  • Private Registry 생성
  • Nginx 서버로 접근 권한 생성
  • Private Registry RESTful API
  • Private Registry 옵션 설정

이전 글에 이어서 Private Registry에 대해서 알아보도록 하겠습니다.

1. Docker Private Registry 생성


Docker Private Registry를 사용하면 개인 서버에 이미지를 저장할 수 있는 저장소를 만들 수 있습니다. 이 레지스트리는 컨테이너로 구현되고, 이에 해당하는 도커 이미지가 존재합니다. 이 이미지는 도커에서 공식적으로 제공하고 있기 때문에 간단하게 사용할 수 있습니다.

1. docker run -d --name myregistry \
2.     -p 5000:5000 \
3.     --restart=always \
4.     registry

--restart 옵션은 컨테이너가 종료되었을 때 재시작에 대한 규칙을 설정합니다. always는 컨테이너
가 정지될 때마다 다시 시작하도록 설정하기 때문에 도커 호스트나 도커 엔진을 재시작하면 컨테이너
도 함께 재시작됩니다.
다른 옵션으로 on-failure와 unless-stopped가 있는데, on-failure:5로 설정하게 되면 컨테이너의 >종료 코드가 0이 아닐 때 재시작을 5번까지 시도하게 합니다.
unless-stopped는 컨테이너를 stop 명령어로 정지했다면 도커 호스트나 도커 엔진을 재시작해도 컨
테이너가 시작되지 않도록 설정합니다.

레지스트리 컨테이너는 기본적으로 5000번 포트를 사용하므로 -p 옵션으로 컨테이너의 5000번 포트를 호스트의 5000번 포트와 연결했습니다. 이 포트로 레지스트리 컨테이너의 RESTful API를 사용할 수 있습니다.

다음 명령어로 컨테이너가 정상적으로 동작하는지 확인해보겠습니다. curl은 HTTP 요청을 보내는 도구 중의 하나입니다.

curl localhost:5000/v2/

이미지 Push 하기

위에서 사용했던 이미지를 레지스트리 컨테이너에 올리려면 다음의 방법으로 push할 수 있습니다.

먼저 이미지의 이름을 추가합니다. 여기서 사용되는 IP는 레지스트리 컨테이너를 생성한 도커 호스트의 IP주소입니다.

docker tag practice:first_push [HOST IP]:5000/practice:first_push

레지스트리 컨테이너에 이미지를 push하려면 이미지의 접두어를 레지스트리 컨테이너가 존재하는 호스트 IP주소와 레지스트리 컨테이너의 5000번 포트와 연결된 호스트의 포트를 설정해야 합니다. 만약 사용 중인 도메인 이름이 있다면, IP 대신 도메인 이름을 사용해도 됩니다.

그리고 다음 명령어로 레지스트리 컨테이너에 이미지를 push 합니다.

docker push [HOST IP]:5000/practice:first_push

(어짜피 호스트 PC에서 테스트하는 것이라서 IP대신 localhost로 입력하였습니다. 레지스트리 컨테이너를 생성한 호스트가 아닌 다른 호스트에서 접근하려면 컨테이너가 생성된 호스트의 IP주소와 포트를 입력해주어야 합니다.)

+) server gave HTTP response to HTTPS client 라는 에러를 출력할 때,

더보기

이미지를 pull할 때도 이미지의 접두어를 레지스트리 컨테이너의 URL로 입력해야 합니다.

docker pull $(HOST_IP):5000/practice:first_push

레지스트리 컨테이너는 생성됨과 동시에 컨테이너 내부 디렉토리에 마운트되는 도커 볼륨을 생성합니다. push된 이미지 파일은 이 볼륨에 저장되고, 레지스트리 컨테이너가 삭제되어도 볼륨은 남아있게 됩니다.

컨테이너를 삭제할 때 마운트된 도커 볼륨도 함께 삭제하고 싶다면, 아래와 같은 명령어로 컨테이너를 삭제하면 됩니다.

docker rm -f --volumes myregistry

2. Nginx 서버로 접근 권한 생성


도커 데몬에 --insecure-registries 옵션만 추가하면 별도의 인증절차없이 레지스트리 컨테이너에서 이미지를 push, pull할 수 있습니다. 그러나 도커허브에서 저장소를 사용하기 위해 docker login 커맨드를 사용한 것처럼 레지스트리 컨테이너 또한 미리 정의된 계정으로 로그인하도록 설정하여 접근을 제한할 수 있습니다.

레지스트리 컨테이너 자체에서 인증 정보를 설정할 수도 있지만, 별도의 Nginx 서버 컨테이너를 생성해 레지스트리 컨테이너와 연동하는 방식도 가능합니다.

-> 이 경우 레지스트리 컨테이너에 접근하는 프론트엔드가 Nginx 서버가 되며, 레지스트리 컨테이너는 외부에 노출되지 않습니다.

로그인 인증 기능은 보안을 적용하지 않은 레지스트리 컨테이너에서는 사용할 수 없기 때문에, 예제에서는 Self-signed 인증서와 키를 발급하여 TLS를 적용하는 방법으로 진행합니다.

더보기

3. Private Registry RESTful API


도커 엔진은 CLI가 제공되지만, 레지스트리 컨테이너는 별도의 인터페이스를 제공하지 않기 때문에 레지스트리 컨테이너를 제어하려면 RESTful API를 사용해야 합니다. 따라서, 별도의 RESTful API를 숙지해야 하므로 도커 허브보다 사용법이 복잡합니다.

레지스트리 컨테이너를 다루기 위한 모든 RESTful API를 확인하려면 아래 공식 문서를 참조하시기 바랍니다.

https://docs.docker.com/registry/spec/api/

레지스트리 API를 직접 사용하기 어렵다면, 다른 개발자들이 미리 만들어놓은 레지스트리 제어 CLI를 >사용할 수도 있습니다.

-p 옵션을 5000:5000으로 설정한 레지스트리 컨테이너를 사용하여 테스트 해보도록하겠습니다.

먼저 레지스트리 컨테이너를 생성합니다.

1. docker run -d --name myregistry \
2.     -p 5000:5000 \
3.     registry

그리고 curl 명령어를 다음과 같이 사용하면, 컨테이너에 저장된 이미지의 목록을 확인할 수 있습니다.

curl ${DOCKER_HOST_IP}:5000/v2/_catalog

현재 아무것도 존재하지 않습니다. 만약 이미지를 올려놓고 확인하면 다음과 같은 출력을 볼 수 있습니다.

위 요청은 이미지의 이름만 반환하고, 태그를 반환하지는 않습니다. 특정 이미지의 태그 리스트를 확인하려면 /v2/(이미지이름)/tags/list 를 사용합니다.

저장된 이미지의 자세한 정보를 반환하려면 /v2/(이미지이름)/manifests/(태그)를 사용합니다.

여기서 manifests(매니페스트)는 레지스트리 컨테이너에 저장된 이미지 정보의 묶음을 의미합니다.

매니페스트 형식은 버전 1과 2가 있습니다. 위 명령어는 버전1의 출력하도록 했으며, 아래 명령어는 헤더를 추가함으로써 버전 2에해당하는 매니페스트가 출력됩니다. 만약 별도의 헤더를 추가하지 않으면 스키마 버전 1의 매니페스트를 출력하게 됩니다.

1. curl -i \
2.     --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
3.     ${DOCKER_HOST_IP}:5000/v2/(이미지이름)/manifests/(태그)

도커 이미지는 이미지에 대한 정보를 저장하는 매니페스트와 실제로 이미지에 레이어 파일을 저장하는 바이너리 파일로 나뉩니다. 그리고 매니페스트와 각 레이어에 해당하는 파일은 서로 고유하게 식별하기 위한 ID로 Digest(다이제스트) 값을 가집니다.

매니페스트와 레이어에 부여된 다이제스트는 레지스트리 컨테이너에 저장된 이미지를 제어할 때 사용됩니다. 이번에는 다이제스트를 이용해 이미지를 삭제해보겠습니다. 이미지를 삭제하려면 매니페스트와 레이어 파일을 따로 삭제해야 합니다.

매니페스트를 삭제하는 URL은 다음과 같습니다.

DELETE /v2/(이미지이름)/manifests/(매니페스트 다이제스트)

매니페스트 다이제는 해당 이미지의 매니페스트를 가져올 때 출력한 Docker-Content-Digest를 사용합니다.

1. curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
2.     -X DELETE \
   3.  ${DOCKER_HOST_IP}:5000/v2/practice/manifests/sha256:a500c7ceb2d058d4afe77e4e60b4cd0a93b3b098b2ae8b6bf093bd6e0fbba211

하지만 위 명령어를 실행하면 지원하지 않는 작업이라는 에러가 반환됩니다. 레지스트리 컨테이너는 각종 설정을 컨테이너 환경변수로 저장해 이미지 저장 기능을 설정하는데, 그중에 이미지를 삭제하는 기능을 활성화할지가 포함되기 때문입니다. 이미지 삭제 기능을 활성화에 대한 환경변수를 지정하지 않으면 레지스트리 컨테이너는 이 환경변수를 false로 인식해 해당 기능을 비활성화합니다. 따라서 다음과 같이 레지스트리 컨테이너를 설정해야 합니다.

컨테이너를 생성하고 다시 이미지를 push합니다.

1. docker run -d --name registry_delete_enabled \
2.     -p 5001:5000 \
3.     --restart=always \
4.     -e REGISTRY_STORAGE_DELETE_ENABLED=true \
5.     registry

1. curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \
2.     -X DELETE -v \
   3.  ${DOCKER_HOST_IP}:5001/v2/practice/manifests/sha256:a500c7ceb2d058d4afe77e4e60b4cd0a93b3b098b2ae8b6bf093bd6e0fbba211

202 Accepted 메세지가 반환되면 해당 이미지의 매니페스트가 정상적으로 삭제된 것입니다. 그러나 이는 실제 이미지 레이어 파일이 아닌 매니페스트 파일만 삭제된 것이며, 같은 이미지를 다시 push하면 레지스트리 컨테이너에 레이어 파일이 존재하기 때문에 Layer Already Exist라는 출력을 확인할 수 있습니다.

이미지 레이어 파일을 실제로 삭제하려면 각 레이어 파일의 다이제스트를 입력해 삭제 API를 호출해야 합니다. 레이어 파일을 삭제하는 URL은 다음과 같습니다.

DELETE /v2/(이미지이름)/blobs/(레이어 다이제스트)

위 예제에서 실제 레이어 파일을 삭제하려면 매니페스트에 명시된 레이어 파일의 다이제스트를 다음과 같이 하나씩 입력합니다. 각 레이어의 다이제스트는 이미지의 매니페스트를 가져올 때 Layer 항목에서 확인할 수있습니다.

1. curl -X DELETE \
2. -v localhost:5001/v2/practice/blobs/sha256:c549ccf8d472c3bce9ce02e49c62b8f6cbc736ea2b8ba812a1ae9390c69d0b71
3.  
4. curl -X DELETE \
5. -v localhost:5001/v2/practice/blobs/sha256:41fde07e165e2f22e32980b963687a663d16b943320f7687e7c4e43203c3f461

확인해보면 first_push가 삭제된 것을 확인할 수 있습니다.

4. Private Registry 옵션 설정


레지스트리 컨테이너는 컨테이너 내부의 환경변수를 써서 레지스트리 서비스를 설정합니다. 그중의 하나가 위에서 사용한 REGISTRY_STORAGE_DELETE_ENABLED와 같은 환경변수입니다. 이러한 환경변수로는 이미지 삭제뿐만 아니라 스토리지 백엔드, 이미지 레이어 파일이 저장될 디렉토리, 웹훅(Webhook) 설정 등이 있습니다.

환경변수는 위에서 사용한 것처럼 docker run 명령어에 -e 옵션을 추가해 설정할 수 있습니다.

1. docker run -d -p 5001:5000 --name registry_delete_enabled --restart=always \
2.     -e REGISTRY_STORAGE_DELETE_ENABLED=true \
3.     -e REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/mydocker \
4.     registry

위에서 nginx로 설정했던 레지스트리의 로그인 기능, 인증서 보안 기능 또한 환경변수를 통해 설정할 수 있으며, 이 경우에 인증 작업은 레지스트리가 자체적으로 수행하게 됩니다.

그러나 위와 같이 매번 -e 옵션으로 입력할 필요없이 yml 파일을 정의해 레지스트리 컨테이너의 환경변수를 설정할 수도 있습니다. yml 파일을 사용하지 않으면 레지스트리 컨테이너는 기본 yml 파일을 사용합니다. yml 파일은 레지스트리 컨테이너의 /etc/docker/registry/config.yml 에 위치합니다.

docker exec registry_delete_enabled cat /etc/docker/registry/config.yml

이제 레지스트리 컨테이너의 환경변수를 설정하기 위해 직접 yml 파일을 작성하여 적용해보겠습니다.

먼저 다음 내용의 config.yml 파일을 로컬에 저장합니다. (tab을 사용하면 안됩니다.)

1. # vi config.yml
2. version: 0.1
3. log:
4.     level: info
5. storage:
6.     filesystem:
7.         rootdirectory: /registry_data
8.     delete:
9.         enabled: true
10. http:
11.     addr: 0.0.0.0:5000

레지스트리 컨테이너에 적용할 수 있는 전체 환경변수는 아래 링크를 참조하기 바랍니다.
https://docs.docker.com/registry/configuration/

이 파일의 내용은 로그 출력 레벨을 info로 설정하고, 이미지 파일이 저장되는 디렉토리를 컨테이너 내부의 /registry_data로 설정하며, 이미지 삭제 API를 활성화하는 것입니다. http 항목의 addr은 레지스트리 서비스를 바인딩할 주소를 나타내므로 반드시 명시해야 합니다.

이제 다음 명령어로 직접 작성한 yml 파일을 적용한 레지스트리 컨테이너를 생성합니다. -v 옵션으로 레지스트리 컨테이너에 존재하는 config.yml 파일을 위에서 작성한 파일로 교체합니다.

1. docker run -d --name yml_registry \
2. -p 5002:5000 \
3. --restart=always \
4. -v $(pwd)/config.yml:/etc/docker/registry/config.yml \
5. registry

직접 작성한 yml 파일을 적용한 레지스트리 컨테이너에 이미지를 push하면 해당 이미지 파일이 yml 파일의 rootdirectory 설정 변수에 저장되는 것을 확인할 수 있습니다.

yml 파일에 설정하는 옵션은 run 명령어의 -e 옵션에서 REGISTRY를 접두어로 두고, 상위 항목을 포
함한 이름으로 직접 설정할 수 있습니다.
예를 들어,
-e REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/registry_data 는
yml 파일에서
storage:
filesystem:
rootdirectory: /registry_data
와 동일합니다.

profile
Data Engineer

0개의 댓글