[Docker] Docker Compose 를 이용하여 ​컨테이너 스케일링 하기​ Computer Tip

본 내용의 원본은 <How to scale Docker Containers with Docker-Compose>에 있습니다.

Docker로 책을 읽고 세미나를 듣고 해서 처음에 그 개념을 잡고 정확히 
이해하는데 시간이 필요합니다.
그러던 중에 딱 원하는 내용의 블로그를 접했으나 적용하면서 안된 부분을 
정리해 보고자 합니다.


이 소스를 받아 작업을 한다고 가정합니다.

우선 지난번 <docker-machine 에서 ubuntu 를 generic 으로 연동할 때> 블로그를 참고하셔서
Docker Machine으로 만든 Docker Host 중에 하나를 대상으로 합니다.


1) 구성에 대한 설명

우선 저자의 허락없이 그림을 따서 죄송합니다만...

위와 같은 그림이 있습니다.

즉 3개의 컨테이너로 구성하는 Docker Compose 구성을 만들 것입니다.
왜 그냥 웹 서비스에 HAProxy를 전단에 놓았는가는
docker-compose 명령으로 간단히 다음과 같이

확장된 웹 서비스 컨테이너가 돌 수 있도록 하기 위하여 소프트웨어 로드밸런서 역할의 HAProxy를 둔 것입니다.

그러니까 하나의 Docker Host 안에 위와 같은 구성의 Scale In/Out 을 자유롭게 할 수 있는 구성이 되겠네요.
(나중에 Docker Host간의 자유로운 서비스 확장은 Kubernates, Mesos, Swarm 등을 이용하는게 정답이 되겠네요)

하여간 위와 같은 구성을 하여 하나의 Docker Host 안에 위와 같은 구성을 해 보는 것입니다.


2) 간단한 파이썬 Flask 웹서비스 컨테이너 구성

원 저자의 내용에서 설명이 빠진 부분 같은데요, 위의 소스를 받아 

$ docker-compose up -d

라고 명령을 내리면 파이썬 구성이 아니라 일반 PHP로 작성한 웹서버가 동작합니다.

docker-compose.yml 파일에 있는 web 서비스를 보면,

services:
  web:
    image: dockercloud/hello-world
    ports:
      - 8080
    networks:
      - front-tier
      - back-tier

라고 되어 있습니다.

$ docker-compose up -d
Pulling web (dockercloud/hello-world:latest)...
latest: Pulling from dockercloud/hello-world
e110a4a17941: Already exists
624ea5173301: Pull complete
5b03ab56e141: Pull complete
5410b66cf9b4: Pull complete
9800c12c3828: Pull complete
Digest: sha256:7c53ccb0442e81e924f1076538963fdfe6ea37d740c145d23b90cd9b313a4bf6
Status: Downloaded newer image for dockercloud/hello-world:latest
Creating webredis_web_1
Creating webredis_lb_1
Creating webredis_redis_1

그러고 위와 같이 돌려보면
webredis_web_1, webredis_lb_1, webredis_redis_1 세개의 컨테이너가 생성되고

$ docker-compose ps
      Name                    Command               State                   Ports                 
-------------------------------------------------------------------------------------------------
webredis_lb_1      /sbin/tini -- dockercloud- ...   Up      1936/tcp, 443/tcp, 0.0.0.0:80->80/tcp 
webredis_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp                              
webredis_web_1     /bin/sh -c php-fpm -d vari ...   Up      80/tcp, 0.0.0.0:32783->8080/tcp       

실행되고 있음을 알 수 있습니다.

$ docker-machine ls
NAME          ACTIVE   DRIVER         STATE     URL                         SWARM   DOCKER    ERRORS
sol-host-01   -        generic        Running   tcp://imot1:2376                    v1.12.1   
sol-host-02   -        generic        Running   tcp://imot2:2376                    v1.12.1   
sol-host-03   -        generic        Running   tcp://imot3:2376                    v1.12.1   
vm-host-01    *        vmwarefusion   Running   tcp://192.168.69.129:2376           v1.12.1   
vm-host-02    -        vmwarefusion   Running   tcp://192.168.69.130:2376           v1.12.1   
vm-host-03    -        vmwarefusion   Running   tcp://192.168.69.131:2376           v1.12.1   

사용한 docker host 는 내부 가상머신인 vm-host-01 입니다.

$ eval $(docker-machine env vm-host-01)
과 같이 환경 설정을 하면 그 이후의 docker 혹은 docker-compose 명령은
모두 대상 Docker Host (Docker 서비스가 돌고 있는 Linux 머신)로 명령이 내려지게 됩니다.

해당 머신의 80 포트로 접속을 해 보면,

위와 같이 잘 뜨기는 하는데 우리가 원하는 웹서비스가 아니라서 redis 서버 접속을 하지 않습니다.

이제는 블로그에 없는 내용인데 따라해 보겠습니다.

$ cat app.py
from flask import Flask
from redis import Redis
import os
import socket
app = Flask(__name__)
redis = Redis(host='redis', port=6379)
host = socket.gethostname()

@app.route('/')
def hello():
    redis.incr('hits')
    return '\nHello World!\nI have been seen %s times.\nMy Host name is %s\n\n' % (redis.get('hits') ,host)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True)

위와 같이 Flask를 이용하여 웹 서비스가 되도록 해 봅니다. '/' root로 포트 80을 접속하면 무언가 결과가
나옵니다.

수정한 것은 port = 80 을 추가한 것입니다. (디폴트로 돌리면 5000번 포트가 됩니다)

또한 새로운 이미지를 생성하기 위한 Dockerfile 도 다음과 같이 수정합니다.

$ cat Dockerfile 
FROM python:3.5.1-onbuild
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code
EXPOSE 80
CMD python app.py

기존에 수정된 부분은 이 컨테이너가 위부로 80 포트를 노출시킨다고 해 주어야 합니다.

이 상태에서 ​

$ docker build -t myweb .
Sending build context to Docker daemon 102.4 kB
Step 1 : FROM python:3.5.1-onbuild
...
Successfully built 36727ba97ce3

와 같이 이미지 build가 잘 되었습니다.

​그리고 docker-compose.yml 파일을 다음과 같이 수정합니다.

$ cat docker-compose.yml 
version: '2'
services:
  web:
    image: myweb
    ports:
      - 8080
    networks:
      - front-tier
      - back-tier

  redis:
    image: redis
    links:
      - web
    networks:
      - back-tier

  lb:
    image: dockercloud/haproxy
    ports:
      - 80:80
    links:
      - web
    networks:
      - front-tier
      - back-tier
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock 

networks:
  front-tier:
    driver: bridge
  back-tier:
    driver: bridge

웹 서비스 이미지만 바꿔놓았습니다.


3) ​서비스 구성

위와 같이 쉽게 (?) 구성이 끝났습니다.

$ docker-compose up -d
Recreating webredis_web_1
Recreating webredis_lb_1
Recreating webredis_redis_1

위와 같이 다시 build를 합니다. (처음 구성하면 메시지 결과가 약간 다를 수 있습니다)

$ docker-compose ps
      Name                    Command               State                   Ports                 
-------------------------------------------------------------------------------------------------
webredis_lb_1      /sbin/tini -- dockercloud- ...   Up      1936/tcp, 443/tcp, 0.0.0.0:80->80/tcp 
webredis_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp                              
webredis_web_1     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32786->8080/tcp       

결과도 위와 같이 python app.py 가 잘 돌고 있음을 알 수 있습니다.
(80 포트가 외부로 노출된 것도 확인합니다)


이제 다른 터미널 창에서 해당 컨테이너들의 로그를 보기 위하여 

$ docker-compose logs -f
Attaching to webredis_lb_1, webredis_redis_1, webredis_web_1
...
lb_1     | frontend default_port_80
lb_1     |   bind :80
...
lb_1     | backend default_service
lb_1     |   server webredis_web_1 webredis_web_1:80 check inter 2000 rise 2 fall 3
...
web_1    |  * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
web_1    |  * Restarting with stat
web_1    |  * Debugger is active!
...
redis_1  | 1:C 01 Sep 01:21:48.427 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
...

위와 같이 HAProxy인 lb_1은 80 포트를 열어 내부에 80 포트로 전달을 잘 하고 있음을 알 수 있으며
web_1은 80 포트로 잘 시작되었고 redis도 잘 돌고 있음을 알 수 있습니다.

이제 터미널에서 

$ curl http://192.168.69.129
명령을 내리면

Hello World!
I have been seen b'1' times.
My Host name is b338ca5a27f4

라고 결과가 나오며...

네번을 더하면

Hello World!
I have been seen b'5' times.
My Host name is b338ca5a27f4

와 같이 1,2,..5 라고 증가되는 값이 보이는데 이것이 바로 redis에서 unique 한 증가값을 가져오는 것입니다.

앞서 켜 놓은 logs -f 터미널에는

web_1    | 172.19.0.4 - - [01/Sep/2016 01:28:51] "GET / HTTP/1.1" 200 -
web_1    | 172.19.0.4 - - [01/Sep/2016 01:28:51] "GET / HTTP/1.1" 200 -
web_1    | 172.19.0.4 - - [01/Sep/2016 01:28:51] "GET / HTTP/1.1" 200 -
web_1    | 172.19.0.4 - - [01/Sep/2016 01:28:51] "GET / HTTP/1.1" 200 -
web_1    | 172.19.0.4 - - [01/Sep/2016 01:28:51] "GET / HTTP/1.1" 200 -

와 같이 잘 나오고 있습니다.

웹으로 확인해도 동일합니다. (앞서 다섯번을 curl 로 호출했고 웹으로 호출했으므로 6번째 입니다)


4) ​스케일링

너무나 쉬운 스케일링 입니다.

$ docker-compose scale web=5

현재 돌고 있는 상황에서 위와 같이 명령만 수정합니다.

그러면,

Creating and starting webredis_web_2 ... done
Creating and starting webredis_web_3 ... done
Creating and starting webredis_web_4 ... done
Creating and starting webredis_web_5 ... done

알아서 위에처럼 web_2 .. web_5 가지 네가지 웹 컨테이너가 추가되었습니다.

$ docker-compose ps
      Name                    Command               State                   Ports                 
-------------------------------------------------------------------------------------------------
webredis_lb_1      /sbin/tini -- dockercloud- ...   Up      1936/tcp, 443/tcp, 0.0.0.0:80->80/tcp 
webredis_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp                              
webredis_web_1     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32786->8080/tcp       
webredis_web_2     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32791->8080/tcp       
webredis_web_3     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32794->8080/tcp       
webredis_web_4     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32793->8080/tcp       
webredis_web_5     /bin/sh -c python app.py         Up      80/tcp, 0.0.0.0:32792->8080/tcp       

모든 것이 자동으로 이루어 진다는 것이 무척 신기합니다.

이제 
$ curl http://192.168.69.129
명령을 10번만 호출해 보면,

로그는,

web_1    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_2    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_3    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_4    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_5    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_1    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_2    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_3    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_4    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -
web_5    | 172.19.0.4 - - [01/Sep/2016 01:38:56] "GET / HTTP/1.1" 200 -

이라고 잘 나오고 (순차적으로 도는 것으로 보아 HAProxy 에서 RoundRobin 방식의 호출이군요)

그 결과도

Hello World!
I have been seen b'7' times.
My Host name is b338ca5a27f4


Hello World!
I have been seen b'8' times.
My Host name is 7da7d94bc415

...
Hello World!
I have been seen b'16' times.
My Host name is 5f584dc01e6f

와 같이 잘 나옵니다.

순차적으로 web 컨테이너가 돌았는데도 redis를 통하여 Atomic INC 호출로 결과는
이상없이 잘 나오는 것을 확인할 수 있네요.


만약 앞서 생성했던 것을 모두 삭제 하려면,

$ docker-compose stop
우선 컨테이너 서비스 멈춤

$ docker-compose rm -f
해당 컨테이너 삭제

$ docker rmi myweb
만들었던 웹 서비스 컨테이너 삭제


잘 구축해 놓으면 정말 편한 것이 컨테이너라는 것을 느낄 수 있었습니다.


어느 분께는 도움이 되셨기를...






덧글

댓글 입력 영역

구글애드텍스트