안수찬 블로그

Docker Registry (1) : 로컬머신에 설치해서 사용하기

Introduction

안수찬 @dobestan

안수찬 @dobestan

서울대학교에서 컴퓨터공학을 전공하고, 오랜 기간 서비스 기획 및 개발을 해 왔습니다. 이러한 전문성을 인정받아 미래부 소프트웨어 마에스트로에 선정된 바 있습니다. 현재는 모바일 방송국, 퍼스트캔버스에서 컨텐츠로 새로운 가치를 그리고 있습니다. 나는 안수찬이다. 그러므로 나는 할 수 있다. me@ansuchan.com


Docker Registry (1) : 로컬머신에 설치해서 사용하기

Posted by 안수찬 @dobestan on .
Featured

Docker Registry (1) : 로컬머신에 설치해서 사용하기

Posted by 안수찬 @dobestan on .

최근에 소프트웨어 마에스트로에서 Docker를 사용해서 프로젝트를 진행하고 있다. Docker로 프로젝트 이미지를 만들고 배포하게 되는데 일반적으로는 Docker 에서 기본적으로 제공하는 Docker Hub를 사용해서 배포하고 사용하게 된다. 우리가 Docker Image를 받을 때 사용하는 docker pull ubuntu라는 명령어는 사실 Docker Hub에 Public하게 등록되어 있는 이미지를 찾아 로컬에 저장하게 되는 것이다. 사실 프로젝트를 계속 진행하다보니 이런 생각이 들었다 :

Public Image를 간단하게 Docker Hub에 저장하는 것은 굉장히 편하다. 그러면 Private Image는 어디에 저장하면 좋을까?

그래서 Docker에서는 기본적으로 Docker Registry라고 불리우는 flask 기반의 서비스를 제공한다. 그리고 우리는 Docker Registry를 Docker를 이용해서 쉽게 띄워 사용할 수 있다. 개인적으로는 Docker HubDocker Registry의 관계가 GithubGitlab의 관계와 유사한 것 같다.

개발 환경

docker 명령어를 사용할 수 있는 환경이면 큰 문제없이 사용할 수 있습니다. 저는 개인적으로 MAC OS X에서 boot2docker를 이용해서 작업하고 있습니다. 포스팅에서 사용한 모든 명령어는 boot2docker ssh로 접속해서 Guest PC에서 수행한 것을 기준으로 합니다. ( 만약 MAC Host에서 실습하고 계신다면 curl 명령어를 사용하실 때만 localhostDOCKER_HOST로 변경해주시면 됩니다. 다른 명령어들은 동일하게 사용 가능합니다. )

Installation

Docker Registry도 다른 프로젝트들과 유사하게 두 가지 방법으로 설치하여 사용할 수 있다. Dockerfile을 다운받아 직접 빌드하는 방식과 Docker Hub에 이미 등록되어 있는 이미지를 사용하는 방식이 있다. 두 가지 방식 중 편한 방법으로 진행하자.

1. 직접 Dockerfile 빌드하기

사실 Dockerfile을 한번 살펴보면 별 내용이 없다. 그냥 만들어져있는 flask app을 배포하는 과정과 별반 다를게 없다. 다만, Dependency가 좀 많아서 설치하는 시간이 좀 오래 걸린다. 그래서 이미 빌드되어있는 이미지를 사용하는 방식을 추천한다. 직접 빌드하기로 결정했다면 먼저 docker-registry 프로젝트를 가져오자.

$ git clone https://github.com/docker/docker-registry.git

Cloning into 'docker-registry'...  
remote: Counting objects: 5010, done.  
remote: Compressing objects: 100% (1897/1897), done.  
remote: Total 5010 (delta 3082), reused 4888 (delta 3006)  
Receiving objects: 100% (5010/5010), 1.06 MiB | 120.00 KiB/s, done.  
Resolving deltas: 100% (3082/3082), done.  
Checking connectivity... done.  

프로젝트를 가져왔다면 docker build 명령어를 이용하여 직접 빌드하면 된다. 여기에서는 -t 옵션을 이용하여 태그 이름을 지어주자. 일반적으로는 [USERNAME]/[IMAGE_NAME]을 사용하기에 dobestan/registry라고 옵션을 주고 빌드하였다.

$ cd docker-registry
$ docker build -t dobestan/registry .
Sending build context to Docker daemon 1.805 MB  
Sending build context to Docker daemon  
Step 0 : from    ubuntu:14.04  
 ---> 53bf7a53e890

...

Step 12 : cmd exec docker-registry  
 ---> Running in a7852b08bc07
 ---> 0873bd8f2830
Removing intermediate container a7852b08bc07  
Successfully built 0873bd8f2830  

빌드가 다 되었다면, docker images 명령어를 이용하여 실제로 제대로 빌드되어 로컬에 이미지가 있는지 확인하자 :

$ docker images | grep dobestan/registry
dobestan/registry           latest              0873bd8f2830        3 days ago          454.8 MB  

2. Pull Image

이미 빌드되어 있는 이미지를 가져오는 방식은 직접 빌드하는 방식보다 훨씬 간단하다. docker pull 명령어로 이미지를 바로 가져오자.

$ docker pull registry
Pulling repository registry  
e42d15ec8417: Download complete  
511136ea3c5a: Download complete  
...
$ docker images | grep registry
registry                    latest              e42d15ec8417        3 weeks ago              455 MB  

Execution

설치 과정이 정상적으로 되었다면 이제 로컬에는 Docker Registry 이미지가 있을 것이다. ( dobestan/registry 혹은 registry라는 이름으로 되어 있을텐데 registry 라는 이미지를 기준으로 설명하도록 하겠다. ) 다른 이미지를 띄우는 방식과 동일하게 docker run 명령어를 이용해서 컨테이너를 만들 수 있다.

$ docker run \
    --name local-registry  # 컨테이너의 이름을 지정
    -d                     # 데몬으로 실행 ( 백그라운드 실행 )
    -p 5000:5000           # 포트 매핑 
    registry               # 이미지 이름

d530e2564a47a8d5d42a6e2aa65dc9ab6975e5ff48d5602bfb9f6c524e941ab9  

우리가 만든 local-registry 컨테이너가 돌아가고 있는지 확인해보자. docker ps로 컨테이너가 실제로 동작하고 있는지 확인해봐도 좋고, 직접 localhost:5000으로 접속해봐도 동작하는지 확인할 수 있다.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES  
d530e2564a47        registry:0.8.1      "/bin/sh -c 'exec do   3 days ago          Up 4 minutes        0.0.0.0:5000->5000/tcp   local-registry  
$ curl localhost:5000 -i

HTTP/1.1 200 OK  
Server: gunicorn/18.0  
Date: Sat, 20 Sep 2014 14:54:36 GMT  
Connection: keep-alive  
Expires: -1  
Content-Type: application/json  
Pragma: no-cache  
Cache-Control: no-cache  
Content-Length: 39  
X-Docker-Registry-Version: 0.8.1  
X-Docker-Registry-Config: dev

"docker-registry server (dev) (v0.8.1)"

이렇게 정상적으로 설치되었음을 확인할 수 있다. 이제는 Docker Hub 대신에 로컬에서 띄운 Docker Registry를 이용하여 이미지를 올리고 받아서 사용해보자.

Usage

1. Build Test Image

이제는 push, pull 기능이 정상적으로 동작하는지 확인해볼텐데 하기 이전에 사용할 가벼운 이미지를 만들어보자. 2.5MB로 매우 가벼운 busybox 이미지를 이용하여 hello world를 출력하는 이미지를 만들 것이다. 아래와 같은 Dockerfile을 하나 생성하자.

FROM busybox  
MAINTAINER dobestan <dobestan@gmail.com>  
CMD /bin/echo "hello world"  

이제 만들어진 Dockerfile을 가지고 dobestan/hello_world라는 이름의 이미지를 만들어주자.

$ docker build -t dobestan/hello_world .
Sending build context to Docker daemon  2.56 kB  
Sending build context to Docker daemon  
Step 0 : FROM busybox  
 ---> a9eb17255234
Step 1 : MAINTAINER dobestan <dobestan@gmail.com>  
 ---> Running in 28d0d8946c86
 ---> 1ca10bda6835
Removing intermediate container 28d0d8946c86  
Step 2 : CMD /bin/echo "hello world"  
 ---> Running in 1d1c96781eae
 ---> 82bdf77324c2
Removing intermediate container 1d1c96781eae  
Successfully built 82bdf77324c2  
$ docker images | grep dobestan/hello_world
REPOSITORY                  TAG                   IMAGE ID            CREATED  
         VIRTUAL SIZE
dobestan/hello_world        latest                82bdf77324c2        About a minute ago       2.433 MB  

이미지를 우리가 로컬머신에 띄운 Docker Registry에 Push하기 이전에 제대로 실행되는지만 확인하고 넘어가자.

$ docker run dobestan/hello_world
hello world  

2. Push Image

이제 테스트 이미지를 생성하고 우리가 의도한 대로 "hello world"를 출력하는 것을 확인했으니 이미지를 Push하면 되는데, 개인적으로는 조금 마음에 들지 않는 부분인데 Docker Registry를 사용하려면 이름을 무조건 [REGISTRY]/[IMAGE_NAME]으로 사용해야한다. ( 예: registry.dobestan.com/hello_world ) 그래서 일단 우리가 만든 이미지의 이름을 변경하자.

$ docker tag \
    dobestan/hello_world
    localhost:5000/hello_world

이름을 localhost:5000/hello_world로 변경했다면 docker push localhost:5000/image_name를 수행했을 때 docker가 알아서, Docker Hub로 보내지 않고 Docker Registry localhost:5000로 이미지를 push하게 된다.

$ docker push localhost:5000/hello_world
The push refers to a repository [localhost:5000/hello_world] (len: 1)  
Sending image list  
Pushing repository localhost:5000/hello_world (1 tags)  
511136ea3c5a: Image successfully pushed  
42eed7f1bf2a: Image successfully pushed  
120e218dd395: Image successfully pushed  
a9eb17255234: Image successfully pushed  
1ca10bda6835: Image successfully pushed  
82bdf77324c2: Image successfully pushed  
Pushing tag for rev [82bdf77324c2] on {http://localhost:5000/v1/repositories/hello_world/tags/latest}  

그리고 실제로 도커 레지스트리 서버에 이미지가 정상적으로 올라갔는지 확인해보자. 여기에서 나온 82bdf77...는 기존에 우리가 만들었던 이미지의 해쉬값과 같다.

$ curl http://localhost:5000/v1/repositories/hello_world/tags/latest
"82bdf77324c2f24758372d4bc36c72be41718d1050349556a556c337b8139968"

3. Pull Image

이제 이미지를 Docker Registry에 Push하는 과정은 정상적으로 동작함을 확인했다. 이제 Pull, 즉 이미지를 가져오는 과정이 동작하는지 확인해봐야하는데 우리는 로컬 머신에서 작업하고 있기에 기존의 이미지를 지우고 다시 받아지는지 확인해봐야한다. docker rm CONTAINER_ID, docker rmi IMAGE_NAME 으로 기존에 띄워져있는 컨테이너를 종료하고 테스트 이미지를 삭제하자.

$ docker ps -a
CONTAINER ID        IMAGE                              COMMAND                CREATED             STATUS                         PORTS                    NAMES  
79d4019a55df        dobestan/hello_world:latest        "\"/bin/sh -c '/bin/   47 minutes ago      Exited (0) 47 minutes ago                               lonely_newton  
7e744a4d786f        0.0.0.0:5000/hello_docker:latest   "\"/bin/sh -c 'echo    About an hour ago   Exited (0) About an hour ago                            cranky_perlman

$ docker rm 79d4019a55df 7e744a4d786f
79d4019a55df  
7e744a4d786f  
$ docker rmi dobestan/hello_world localhost:5000/hello_world
Untagged: dobestan/hello_world:latest  
Untagged: localhost:5000/hello_world:latest  
Deleted: 82bdf77324c2f24758372d4bc36c72be41718d1050349556a556c337b8139968  
Deleted: 1ca10bda68359f05e01a858030c5c9b18848c4a314a86c1025581275918fe954  

이제 삭제가 완료되었으므로, 로컬머신에는 hello_world 이미지가 없을 것이다. ( 물론 로컬 Docker Registry 서버에는 있다. )

$ docker images | grep hello_world

우리가 docker run 명령어를 내리면 일차적으로는 로컬 이미지에 있는지 찾고, 만약에 없다면 Docker Hub에서 찾도록 되어있다. 하지만 docker run REGISTRY/IMAGE로 명령을 내리게 되면 로컬 이미지를 찾고, Registry에서 찾고 Pull 해오게 된다. 물론 직접 Pull을 하고 Run을 해도 되겠지만 여기서는 바로 테스트 이미지를 Run 해보겠다.

$ docker run localhost:5000/hello_world
Unable to find image 'localhost:5000/hello_world' locally  
Pulling repository localhost:5000/hello_world  
82bdf77324c2: Download complete  
511136ea3c5a: Download complete  
42eed7f1bf2a: Download complete  
120e218dd395: Download complete  
a9eb17255234: Download complete  
1ca10bda6835: Download complete  
hello world  

위의 상황을 간단하게 정리해보자.

  • docker run localhost:5000/hello_world로 명령을 수행하여 localhost:5000/hello_world 컨테이너를 띄우려고 시도하였다.
  • 로컬에는 ( 우리가 아까 docker rmi로 이미지 삭제하였으므로 ) 이미지가 없어서 localhost:5000, 즉 Docker Registry 에서 이미지를 찾는다.
  • 이미지를 다운 받아 컨테이너를 생성한다.
  • 우리가 의도했던대로 "hello world"를 출력한다.

정리

우리가 지금까지 다뤄온 내용을 쭉 생각해보자. Docker를 이용하여 docker/docker-registry 컨테이너를 로커 서버 ( localhost:5000 )에 띄웠다. 그리고 실제로 레지스트리 서버가 동작하는지 확인하기 위해서 간단한 테스트 이미지를 실제로 생성해보았다. 생성한 테스트 이미지를 가지고 docker push, docker pull, docker run 명령어가 성공적으로 수행됨을 확인하였다.

사실 이까지 진행해보면 이런 생각이 든다. "로컬머신에 레지스트리 서버를 띄우는게 큰 의미가 있을까?" 즉, 실제로 협업이 가능하도록 외부 서버에 레지스트리 서버를 띄우고 인증된 사용자만 사용하도록 하면 어떨까 라는 생각이 든다. 그럼 이제 AWS EC2에 Docker Registry 어플리케이션을 올리고 S3에 이미지를 저장하도록 변경해보자.

References

안수찬 @dobestan

안수찬 @dobestan

https://ansuchan.com/

서울대학교에서 컴퓨터공학을 전공하고, 오랜 기간 서비스 기획 및 개발을 해 왔습니다. 이러한 전문성을 인정받아 미래부 소프트웨어 마에스트로에 선정된 바 있습니다. 현재는 모바일 방송국, 퍼스트캔버스에서 컨텐츠로 새로운 가치를 그리고 있습니다. 나는 안수찬이다. 그러므로 나는 할 수 있다. me@ansuchan.com

View Comments...