안수찬의 개발이야기

Docker Registry (2) : EC2로 구동하고 S3에 이미지 저장하기

Introduction

안수찬 @dobestan

안수찬 @dobestan

소프트웨어 생태계에 기여할 수 있는 실용주의 프로그래머가 되고자 합니다. 나는 안수찬이다. 그러므로 나는 할 수 있다.


Docker Registry (2) : EC2로 구동하고 S3에 이미지 저장하기

Posted by 안수찬 @dobestan on .
Featured

Docker Registry (2) : EC2로 구동하고 S3에 이미지 저장하기

Posted by 안수찬 @dobestan on .

Docker Registry 1편에서는 로컬 머신에 도커 레지스트리 서버를 구동하고 이미지까지 로컬에 저장하는 방법을 알아봤다. 이번에는 로컬 머신이 아니라 서버에 띄워서 다른 사람들과 함께 사용할 수 있는 방법을 알아보자. 먼저 시작하기 전에 큰 구성을 살펴보자.

Q. 도커 이미지는 어디에 저장하면 좋을까?

이전에는 레지스트리 서버와 이미지 저장소의 구분이 없었다. 사실 레지스트리의 핵심 기능은 이미지를 저장하는 곳인데 이걸 전부 AWS S3에 올리면 안될까? 라는 생각이 들 수 있다. 하지만 Docker Registry는 flask 로 만들어진 웹 어플리케이션이므로 S3에 띄울 수 없고 별도의 서버로 돌려야 한다. 일단 도커 이미지는 AWS S3에 올리자.

Q. 도커 레지스티리 서버는 어디에 띄우면 좋을까?

이전에는 레지스트리 서버를 로컬 머신에서 띄워서 사용했다. 물론 S3에 이미지를 올리고 레지스트리 서버는 각자 로컬 머신에서 띄우고 S3와 연결해서 사용해도 되겠지만 이번에는 레지스트리 서버도 AWS EC2에 올려서 다같이 사용해보자.

자, 이제 어떻게 할지 큰 그림은 그려졌다. AWS EC2에는 도커 레지스트리 서버를 띄우고, S3에는 도커 이미지가 올라가도록 설정해보자.

시작하기에 앞서

먼저 시작하기에 앞서 블로그 포스팅에서 사용되는 URL이나 주소를 통일하고 진행하려고 한다. 당연한 말이겠지만 사용자마다 다를 수 있는 부분이기에 여기에서는 통일하고 진행하겠다.

1. AWS > EC2

자신의 AWS EC2 Console에서 Public IP 값을 확인하고 진행하자. 포스팅에서는 DNS로 registry.ansuchan.com 으로 연결되어 있다고 가정하고 진행하겠다. EC2에 접속하기 위해서 사용하는 키 이름은 dobestan-docker.pem 이라고 생각하고 진행하겠다.

2. AWS > S3

자신의 AWS S3 Console에서 Name 값을 확인하고 진행하자. 포스팅에서는 dobestan-docker-registry 이라고 가정하고 진행하겠다.

3. AWS > IAM

추후에 S3와 연결하기 위해서 ACCESS KEY IDACCESS SECRET KEY가 필요하다. AWS IAM Console > Security Credential에서 확인하자. 여기에서는 임의로 아래와 같은 값을 사용하는 것으로 가정하고 진행하겠다.

  • AWSAccessKeyId=QWERASCBCRTUN46NHTA
  • AWSSecretKey=GXzD8MWdh6KdYaB2wWkJJ9PcUENK3a

EC2에 Docker Registry 실행하기

1. 인스턴스 생성하기

먼저 생각을 한번 해보자. 우리가 앞으로 해야할 것을 쭉 생각해보면 EC2 우분투 이미지를 생성하고, 우분투에 Docker 클라이언트를 설치하고, 거기에다 다시 Docker Registry 이미지를 받아서 컨테이너를 구동할 것이다. 그러면 한번에 쉽게 Docker 클라이언트가 설치되도록 EC2 인스턴스를 생성할 수는 없을까?

우분투 클라우드 이미지에 기본적으로 CloudInit 패키지가 포함되어 있다. 그래서 처음에 인스턴스를 초기화할 때, 함께 docker 를 설치하여 생성할 수 있다.

Docker Docs > Installation > Amazon EC2에서 자세한 내용을 참고하자. 일반 EC2 인스턴스를 생성할 때와 동일한데, Configure Instance Details > Advanced Details 에서 User Data에 #include https://get.docker.io를 추가해주고 인스턴스를 생성하자. 사실 이게 대단한 역할을 하는게 아니라 그냥 현재 OS를 판단해서 Docker를 설치해준다.

local$ ssh -i dobestan-docker.pem ubuntu@registry.ansuchan.com  

서버에 접속해서 실제로 Docker가 설치되었음을 확인하자. 인스턴스가 접속되더라도 Docker가 CloudInit 으로 설치되므로 접속 이후에 약간의 시간이 걸릴 수도 있다.

$ docker
Usage: docker [OPTIONS] COMMAND [arg...]  
 -H=[unix:///var/run/docker.sock]: tcp://host:port to bind/connect to or unix://path/t
o/socket to use

A self-sufficient runtime for linux containers.  
...

2. Docker Registry Container

이제 우리가 생성한 EC2 서버 ( registry.ansuchan.com )에 Docker가 설치되었음을 확인했다. Docker Registry를 설치하는 과정은 이전 포스팅에서 로컬에 설치한 방법과 완전히 동일하다. 여기서는 간단하게 넘어가겠다.

$ sudo docker run
    --name docker-registry    # 컨테이너 이름을 지정
    -d                        # 데몬으로 실행 ( 백그라운드 실행 )
    -p 5000:5000              # 포트 매핑 ( 컨테이너 내부 5000번, 호스트 5000번 )
    registry:latest           # 이미지 이름
1fa1eeaa48975680315d73b1499883bc416bdbba63adf4a94b913e3774d3a549  
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES  
1fa1eeaa4897        registry:latest     "/bin/sh -c 'exec do   4 minutes ago       Up 4 minutes        5000/tcp             docker-registry  

3. Nginx Container

이전에는 로컬 머신에서 테스트를 진행했기에 그냥 5000번 포트로 바로 진행하였다. 하지만 실제로 사용하기 위해서 우리가 일반적으로 사용하는 HTTP(80포트)나 HTTPS(443포트)를 사용하도록 설정해주자. 일단 이 포스팅에서는 SSL 인증이 없는 기본 HTTP를 이용하여 내부 프록시로 연결해주는 설정을 해보자.

웹 서버로는 Nginx를 사용할 것이다. 물론 Nginx도 새로운 컨테이너로 띄워서 진행할 예정이기에 먼저 Nginx 이미지를 받아주자.

$ sudo docker pull nginx:latest
Pulling repository nginx  
61e8f94e1d65: Download complete  
511136ea3c5a: Download complete  
...
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE  
registry            latest              e42d15ec8417        4 weeks ago         455 MB  
nginx               latest              61e8f94e1d65        9 weeks ago         499.1 MB  

Nginx 컨테이너를 올리기 이전에 지금까지의 과정과 앞으로 할 내용을 간단하게 정리해보자. 앞으로 서버에는 2개의 컨테이너가 올라갈 예정이다. 첫 번째는 Docker Registry 컨테이너, 그리고 두 번째는 Nginx 컨테이너. 서버의 입장에서 한번 생각해보자.

  • 누군가가 docker push registry.ansuchan.com 명령어를 통해서 80번 포트로 요청을 보낸다.
  • 서버의 80번 포트로 온 요청을 Nginx 컨테이너가 80번 포트로 받는다. ( -p 80:80 )
  • Nginx 컨테이너의 80번 포트로 온 요청을 어떠한 방법으로 Docker Registry 컨테이너의 5000번 포트로 보낸다.
  • Docker Registry가 받은 요청을 처리한다. ( 일단 여기에서는 이미지가 어디에 저장되는지는 생각하지 말고 넘어가자. )

어떠한 방법으로는 대체 어떻게 하는걸까? Docker는 이렇게 컨테이너간의 연결을 위해서 링킹이라는 기능을 제공한다. 도커 컨테이너를 띄울 때, --link 옵션을 통해서 서로 다룬 두 컨테이너를 연결할 수 있다. ( /etc/hosts 에 추가하는 것이라고 생각하면 쉽게 이해할 수 있을 것이다. ) 따라서 만약에 docker run --link docker-registry:docker-registry ... 로 컨테이너를 생성한다면 http://docker-registry로 다른 컨테이너에 연결할 수 있게 된다.

이런 전체적인 과정을 이해하면서 nginx.conf를 설정해보자. nginx.conf는 나중에 컨테이너를 띄울 때, 컨테이너 내부의 /etc 에 넣을 것이다. 만드는 위치는 아무 곳이나 상관 없기에 그냥 여기에서는 root_path(~/)에 만들었다.

# ~/nginx.conf
worker_processes  1;

events {  
    worker_connections  1024;
}

http {  
    server {
        listen       80;
        server_name  localhost;

        proxy_set_header Host           $http_host;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header Authorization  "";

        client_max_body_size 0;

        chunked_transfer_encoding on;

        location / {
            proxy_pass          http://docker-registry:5000;
            proxy_set_header    Host  $host;
            proxy_read_timeout  900;
        }
    }
}

# daemon off;
# 아래의 내용을 참고해주세요.

nginx 1.7.4 버전까지는 daemon off; 설정을 포함하여 진행하여야 했습니다. 1.7.5 버전부터 기본 이미지에 CMD ["nginx", "-g", "daemon off;"] 명령어가 포함됨에 따라 nginx.confdaemon off; 설정이 추가되면 "daemon" directive is duplicate in /etc/nginx/nginx.conf 오류로 컨테이너가 실행되지 않습니다.

자세한 내용은 제가 작성한 My First Pull Request on Dockerfile/nginx 포스트를 확인해주세요.

일반적인 Nginx 설정이라서 딱히 살펴보아햘 부분은 없지만 location 부분만 한번 살펴보자. proxy_pass http://docker-registry:5000 에서 Nginx가 받은 요청을 내부 프록시로 docker-registry 컨테이너의 5000번 포트로 전달한다. 이제는 이 설정 파일을 이용해서 실제 컨테이너를 실행시켜보자.

$ docker run \
    --name nginx-registry \
    -d \
    -v ~/nginx.conf:/etc/nginx.conf \         # 설정 파일
    --link docker-registry:docker-registry \  # 컨테이너 링킹
    -p 80:80 \
    nginx:latest
b1cc971d1b29bb8cec936a0206eb0a2a1ee91410d2d8a05594b809be15a127d3  

이제 docker ps로 실행되고 있는 컨테이너를 살펴보면 기존과 약간 다르게 docker-registry,nginx-registry/docker-registry 이렇게 두 컨테이너가 연결되어 있음을 확인할 수 있다.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                NAMES  
b1cc971d1b29        nginx:latest        "nginx"                6 minutes ago       Up 6 minutes        0.0.0.0:80->80/tcp   nginx-registry  
1fa1eeaa4897        registry:latest     "/bin/sh -c 'exec do   8 minutes ago       Up 8 minutes        5000/tcp             docker-registry,nginx-registry/docker-registry  

이제 실제로 registry.ansuchan.comdocker push를 해보면 정상적으로 동작함을 알 수 있다. 물론 아직 이미지는 아무런 설정을 해두지 않았기에 EC2에 그대로 저장된다.

$ docker tag \
    dobestan/hello_world \     # 기존에 만들어진 이미지
    registry.ansuchan.com/hello_world

$ docker push 54.64.193.126/hello_world
The push refers to a repository [54.64.193.126/hello_world] (len: 1)  
Sending image list  
Pushing repository 54.64.193.126/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://registry.ansuchan.com/v1/repositories/hello_world/tags/latest}

$ curl http://registry.ansuchan.com/v1/repositories/hello_world/tags/latest
"82bdf77324c2f24758372d4bc36c72be41718d1050349556a556c337b8139968"

S3에 이미지 저장하기

사실 이미지를 저장하는 위치를 변경하는 것은 굉장히 쉽다. 기존의 Docker Registry를 종료하고 새로운 컨테이너를 띄울 예정이다. 다만, Docker Registry 컨테이너를 띄울 때, -e 옵션을 통해서 특정 환경변수를 설정해주면 된다. 아래의 명령어를 참고해보자.

$ sudo docker run -d -p 5000:5000 --name docker-registry \
     -e SETTINGS_FLAVOR=s3 \
     -e AWS_BUCKET=dobestan-docker-registry \
     -e STORAGE_PATH=/registry \
     -e AWS_KEY=QWERASCBCRTUN46NHTA \
     -e AWS_SECRET=GXzD8MWdh6KdYaB2wWkJJ9PcUENK3a \
     registry

이렇게 새로 Docker Registry 컨테이너가 생성되었으면 이제는 이미지가 자동적으로 AWS S3에 업로드되게 된다. 확인하기 이전에 기존의 Nginx 컨테이너는 사실 이전의 Docker Registry 컨테이너와 연결되어 있었으므로 종료하고 새롭게 만들어서 새로운 컨테이너와 연결해주자.

docker push

기존과 동일하게 docker push 명령어를 수행해보자. 사실 여기서 보이는 결과에는 차이가 없지만 이제는 Docker Registry 컨테이너에 이미지를 저장하는 것이 아니라 우리가 설정해준 dobestan-docker-registry S3 버킷에 이미지를 저장한다.

$ docker push registry.ansuchan.com/hello_world
The push refers to a repository [registry.ansuchan.com/hello_world] (len: 1)  
Sending image list  
Pushing repository registry.ansuchan.com/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://registry.ansuchan.com/v1/repositories/hello_world/tags/latest}  

실제로 올라간 이미지를 확인해보면 우리가 기존에 만들었던 hello_world(82bdf77324c2) 이미지가 올라가있음을 확인할 수 있다.

docker pull

이제 이렇게 다른 호스트에서도 docker pull로 내가 만든 EC2 Docker Registry에 접속해서 이미지를 올리고 받을 수 있게 되었다.

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

정리

EC2로 구동하고 이미지 저장소를 S3로 변경하면서 진행한 과정을 쭉 생각해보자. 맨 처음에는 EC2 인스턴스를 생성하면서 CloudInit을 이용해서 Docker를 함께 설치하였다. Docker Registry 컨테이너를 띄웠고, 이 컨테이너와 연결하여 Nginx 컨테이너를 띄워 성공적으로 동작함을 확인했다. 지금까지는 이미지가 Docker Registry 컨테이너에 저장되게 되는데 새롭게 컨테이너를 띄우면서 옵션을 통해 S3로 이미지 저장소를 변경하고 실제로 업로드됨을 확인하였다.

여기까지 하면 다시 의문이 생긴다. 자, 지금까지 누구나 함께 사용할 수 있는 Docker Registry 앱을 잘 배포했다. 그런데 누구나 사용하면 될까? ( 그럼 내 EC2랑 S3 요금은 어떻게...? ) 이 다음 포스팅에서는 Nginx Basic Authentication 을 이용해서 인증된 사용자만 Docker Registry를 사용하도록 변경해보자.

References

안수찬 @dobestan

안수찬 @dobestan

https://dobest.io/

소프트웨어 생태계에 기여할 수 있는 실용주의 프로그래머가 되고자 합니다. 나는 안수찬이다. 그러므로 나는 할 수 있다.

View Comments...