본문 바로가기
  • 오늘처럼
소프트웨어 아키텍처/Docker

Docker registry API - push

by bluefriday 2021. 10. 14.
반응형

앞의 'Docker registry API - pull' 과정에서는 registry 에 저장된 이미지를 curl 을 통한 API 통신으로 받아서 로컬 파일 시스템에 저장하는 과정을 다루었다. 이번에는 반대로 로컬 파일 시스템에 있는 컨테이너 이미지의 manifest, blob 레이어 들을 이용하여 push 를 구현하여본다.

기본적으로 앞의 과정을 진행하여 특정 이미지에 대하여 하단의 파일들을 이미 가지고 있다고 가정한다.

  • hello-world:test 이미지의 manifest 레이어 파일
  • hello-world:test 이미지의 config 레이어 파일
  • hello-world:test 이미지의 1개 이상의 blob 레이어 파일(들)

 

Image push 의 단계

하나의 컨테이너 이미지를 저장소에 올리는 과정은 하단의 3가지 유형의 파일을 업로드 하는 것과 동일하다. 물론 실제의 'docker push' 명령어의 경우 로컬에 있는 컨테이너 이미지를 gzip 형식으로 압축하는 과정이 포함된다. 파일의 업로드 순서는 pull 과의 역순이며 세부적으로는 다음과 같다.

  1. blobs 레이어 업로드
  2. config 레이어 업로드
  3. manifest 레이어 업로드

 

테스트 환경 구축

이 테스트는 로컬 환경에 테스트용 docker registry를 구축한 후에 Docker registry API 를 이용하여 push를 구현한다. 모든 push가 종료되면 docker pull 명령어로 도커 레벨에서 해당 이미지를 받아와서 push가 잘 되었음을 확인하기로 한다.

### 레지스트리 구동 
mkdir -p /root/registry-api 
docker run -d --name=test-registry -p 5000:5000 -v /root/registry-api:/var/lib/registry registry:latest

 

1. Blobs 레이어 업로드

manifest 를 먼저 받아와서 그 안에서 blobs 레이어의 이름을 확인한 후에 blob 레이어들을 받아오는 pull 과정과 달리, push 의 과정에서는 먼저 blob 레이어들을 registry 에 업로드한다. 그 이후에 마지막으로 manifest 파일을 보내면서, registry에서는 manifest 에 있는 blob 레이어들이 registry 내부에 존재하는지를 검사하면서 push 과정을 완료하게 된다. 

blob 레이어들은 registry 내부에서 주소에 의한 참조 형식으로 저장이 되므로, registry 내부에 업로드할 경로를 알아야 한다. 이를 위해서 blob 을 업로드 하기 전에 먼저 'initiate blob' 요청을 먼저 보낸다. 명령어와 예시는 다음과 같다.

### Initiate Blob Upload
curl -i -d '' -X POST http://[[도메인주소]]/v2/[[레포지토리 이름]]/blobs/uploads/

### 사용 예
curl -i -d '' -X POST http://localhost:5000/v2/hello-world/blobs/uploads/

위 명령어에서도 일부 확인할 수 있듯이, Initiate Blob Upload 요청과 blob upload 요청 모두 태그와 관련된 정보를 명시하지 않는다. Initiate Blob Upload 요청을 보낼 때는 헤더정보를 확인하기 위하여 반드시 '-i' 옵션을 같이 주도록 하자. 리턴되는 결과는 다음과 같다.

### Initiate Blob Upload 의 response header
HTTP/1.1 202 Accepted
Content-Length: 0
Docker-Distribution-Api-Version: registry/2.0
Docker-Upload-Uuid: 746d5971-afba-41ea-8cc0-e1301b4e2932
Location: http://localhost:5000/v2/test/blobs/uploads/746d5971-afba-41ea-8cc0-e1301b4e2932?_state=RolYGXDrLNAx0qWr8u6rg8hbGm-APXO94eiX......IyMDIxLTEwLTEzVDA5OjMwOjE0LjE2MDkwOTI3OFoifQ%3D%3D
Range: 0-0
X-Content-Type-Options: nosniff

Initiate Blob Upload 요청으로 registry는, 실제 blob 레이어를 업로드 할 수 있는 공간에 대한 정보를 Response header에 포함하여 '202 Accepted' 코드와 함께 반환한다. 위 리턴 값에서 Location 에 해당하는 값이 blob upload 에 사용될 주소값이다. 위 예제의 '_state' 필드에 해당하는 값은 실제로 매우 길어서 축약하여 표기하였으나 실제 사용에서는 당연히 모두 복사하여 요청하여야한다.

이제 Location 정보를 이용하여 하단과 같이 curl 요청을 보내서 blob 레이어를 업로드한다.

### Blob upload
curl -v -X PUT -G \
 -H "Content-Type: application/octet-stream" \
 -T "[[blob파일 절대경로]]" \
 --data "digest=sha256:[[레이어의 digest값]]" \
 [[Location값]]

### 사용 예
curl -v -X PUT -G \
 -H "Content-Type: application/octet-stream" \
 -T "/root/registry-api/c6568d" \
 --data "digest=sha256:c6568d" \
 http://localhost:5000/v2/test/blobs/uploads/746d5971-afba-41ea-8cc0-e1301b4e2932?_state=RolYGXDrLNAx0qWr8u6rg8hbGm-APXO94eiX......IyMDIxLTEwLTEzVDA5OjMwOjE0LjE2MDkwOTI3OFoifQ%3D%3D

pull과 달리 사용방법이 조금 복잡하다. 먼저 curl 로 PUT method를 사용하며 헤더값으로 'Content-Type: application/octet-stream' 을 사용한다. 그리고 T 플래그로 업로드할 레이어파일의 절대 경로를 지정한다. digest 값의 경우 해당 upload 레이어의 digest 값인데, 앞의 pull 테스트에서는 레이어파일의 digest 값을 파일이름으로 사용하였기에 여기에서도 경로를 제외한 파일이름과 digest값은 동일하다. 또한 앞의 포스팅과 동일하게 너무 길어서 6자로 줄여 표기하였다. 이렇게 curl 요청을 실행할 경우 리턴값은 다음과 같다.

* Connected to localhost (127.0.0.1) port 5000 (#0)
...
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 201 Created
< Content-Length: 0
< Docker-Content-Digest: sha256:c6568d...
< Docker-Distribution-Api-Version: registry/2.0
< Location: http://localhost:5000/v2/world/blobs/sha256:c6568d...
< X-Content-Type-Options: nosniff
<
* Connection #0 to host localhost left intact

'100 Continue' 코드와 '201 Created' 코드를 확인할 수 있다. 이렇게 하나의 blob 레이어 업로드를 완료하고 동일한 방식으로 모든 blob 레이어를 업로드한다.

 

2. Config 레이어 업로드

위 1번 과정과 완전하게 동일하다. config 레이어 또한 blob 레이어의 성격이기에 blob 과 동일한 방식으로 2단계에 걸쳐 업로드를 수행한다.

 

3. Manifest 레이어 업로드

config 레이어를 포함하여 모든 blob 레이어를 전송하였다면 마지막으로 manifest 레이어를 전송하여 registry에게 참조해야할 이미지의 레이어 정보들을 알려줘야 한다. 별도의 initiate 요청은 필요하지 않으며, curl 요청은 다음과 같다.

### Manifest upload
curl -v -X PUT -G \
 -H "Content-Type: application/vnd.docker.distribution.manifest.v2+json" \
 -T "[[manifest 파일절대경로]]" \
 http://[[registry 도메인]]/v2/[[repo이름]]/manifests/[[태그명]]

### 사용 예
curl -v -X PUT -G \
 -H "Content-Type: application/vnd.docker.distribution.manifest.v2+json" \
 -T "/root/registry-api/image.manifest" \
 http://localhost:5000/v2/hello-world/manifests/test

위와 같이 마지막 manifest 파일을 업로드하면서 태그 이름을 함께 요청해준다. 결과는 다음과 같다.

* Connected to localhost (127.0.0.1) port 5000 (#0)
...
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 201 Created
< Docker-Content-Digest: sha256:242d44...
< Docker-Distribution-Api-Version: registry/2.0
< Location: http://localhost:5000/v2/hello-world/manifests/sha256:242d44...
< X-Content-Type-Options: nosniff
< Content-Length: 0
<
* Connection #0 to host localhost left intact

위와 같이 '201 Created' 코드를 확인할 수 있다.

 

이미지 파일 확인

이제 api 가 아닌 docker 명령어를 사용하여 push 한 이미지를 다시 pulling 한다.

[root@localhost/]] docker pull localhost:5000/hello-world:test
test: Pulling from hello-world
c6568d217a00: Pull complete
6937ebe10f02: Pull complete
Digest: sha256:242d44...
Status: Downloaded newer image for localhost:5000/hello-world:test
localhost:5000/world:test
[root@localhost/]

이를 통해 registry 내부에 단계적으로 넣은 layer 들이 하나의 이미지로 잘 참조되어 pull 이 가능함을 확인할 수 있다.

 

댓글