ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Docker] 도커와 Nginx를 이용한 로드밸런싱, 무중단 배포 (1) - 1~4개의 컨테이너 사용시 처리 속도 변화
    Portfolio/DevOps 2019. 3. 17. 22:37
    728x90



    참고

    https://subicura.com/2016/06/07/zero-downtime-docker-deployment.html

    https://blog.outsider.ne.kr/548

    https://subicura.com/2017/01/19/docker-guide-for-beginners-2.html



    먼저 이 글을 시리즈별로 다 읽고 진행해야한다.

    https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html

    VM 웨어 같은 가상 머신을 미리 접해본 사람이라면 접근이 쉬울 것이다.

    다만 가상 머신보다 도커를 훨씬 가볍고 빠르다. 


    나는 vultr를 사용해서 vps를 만들어서 실습환경을 만들었다.

    실습 환경 구성은


    클라이언트(내 노트북) - nginx -- docker 컨테이너1

                                            ㄴ docker 컨테이너2

                                            ㄴ docker 컨테이너3

                                            ㄴ docker 컨테이너4


    으로 해서 nginx에서 4개의 도커 컨테이너로 로드벨런싱을 해주도록 하였다. 각각의 도커 컨테이너는 vps로도 각각 하나씩의 서버이다.

    * 물론 내부적으로 이것도 docker로 나눠져 있을 것 같은 느낌이다.


    클라이언트에선 다음과 같은 코드로 요청을 한다.

    const request = require('request');
    let startTime = Date.now() / 1000;
    let count = 0;
    for(let i = 0; i < 100; i++) {
    setTimeout(() => {
    request('http://207.148.**.**:8801/hash/abcde', (err, res, body) => {
    count++;
    if(err)
    console.error(err);
    console.log(body);
    if(count === 100)
    console.log(`걸린 시간 : ${(Date.now() / 1000) - startTime}`);
    });
    }, i * 5);
    }



    저 ip주소(207.148.**.**:8801)은 nginx의 ip주소와 로드벨런싱 중인 포트이다.

    코드를 보면 /hash/abcde 라는 URL로 요청을 하고 있는데, abcde라는 문자열은 서버에서 10만번 sha512 알고리즘으로 해시화를 진행한다.

    그 후 그 결과를 클라이언트에게 반환해주는 식이다.

    추가적으로 시작한 시간과 끝나는 시간을 측정할 수 있도록 만들었다.

    이렇게 만들어야 서버가 1개일때부터 4개일때까지 어떻게 변화될 수 있는지 확인할 수 있을 것이다.

    요청마다 조금씩 텀을 주기위해 setTimeout을 넣었다. 한번에 100개의 요청을 하면 큐에 요청이 너무 쌓여버려서 더 느려지는 것 같음.


    nginx는 vps를 만들어주고 나서 도커로 만들어 줄 수도 있겠지만 우선 그냥 설치했다.

    apt-get install nginx  를 입력해서 nginx 설치

    sudo find / -name nginx.conf   를 해서 nginx.conf가 있는 경로를 찾아냄

    /etc/nginx/nginx.conf  에 있는 것으로 검색되어 vi 에디터를 열어서 아래 쪽을 편집함


    http {

    .....

            ##

            # Virtual Host Configs

            ##


            include /etc/nginx/conf.d/*.conf;

            include /etc/nginx/sites-enabled/*;

            #NodeJS 서버 로드 밸런싱

            upstream nodejs_server {

                    least_conn;

                    server 202.182.**.**:8801 weight=10;

                    server 45.76.**.**:8801 weight=10;

                    server 202.182.**.**:8801 weight=10;

                    server 66.42.**.**:8801 weight=10;

            }

            #8801번 포트 NodeJs 서버로 연결

            server {

                    listen 8801;

                    server_name localhost;


                    location / {

                            proxy_pass http://nodejs_server;

                    }

            }

    }



    #mail {

    #       # See sample authentication script at:

    #       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript

    #

    .....


    파랗게 된 부분이 새로 입력해줘야하는 부분이다. http 내에 입력해주면 된다.

    #은 주석처리이다.



    총 4개의 도커 컨테이너를 지정해주었다. 하나씩 빼고 싶으면 그 줄에 #으로 주석처리를 해주면 된다.

    이렇게 설정파일을 변경하고 나면


    nginx를 재시작 해줘야한다.

      nginx -s stop

      nginx

    순으로 입력하면 재시작된다.


    재시작하지 않으면 설정이 반영되지 않는다.

     


    마지막으로 도커 부분이다.


    위쪽에서 보라고 했던 글을 3편까지 보고 왔다면 알겠지만, 먼저 docker 파일을 만들어야한다.


    # 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)

    FROM       ubuntu:18.04

    MAINTAINER lleellee013@gmail.com

    RUN        apt-get -y update


    # 2. node 설치

    RUN apt-get -y install nodejs && \

        apt-get -y install npm && \

        npm install -g npm


    # 3. 소스 복사

    COPY . /usr/src/app


    # 4. npm 패키지 설치 (실행 디렉토리 설정)

    WORKDIR /usr/src/app

    RUN     npm install


    # 5. node 서버 실행 (Listen 포트 정의)

    EXPOSE 4567

    CMD    node app.js


    이런 식으로 만들어주면 되는데, 경로나 포트를 바꾸고 싶으면 맞춰서 바꿔주면 된다.


    다음은 app.js이다

    const restify = require('restify');
    const crypto = require('crypto');

    const server = restify.createServer({
    name: 'myapp',
    version: '1.0.0'
    });

    server.use(restify.plugins.acceptParser(server.acceptable));
    server.use(restify.plugins.queryParser());
    server.use(restify.plugins.bodyParser());

    server.get('/hash/:str', function (req, res, next) {
    crypto.randomBytes(64, (err, buf) => {
    crypto.pbkdf2('str', buf.toString('base64'), 100000, 64, 'sha512', (err, key) => {
    let res_obj = {};
    res_obj.digest = key.toString('base64');
    res_obj.container_name = process.env["CONTAINER_NAME"];
    res.send(res_obj);
    });
    });
    return next();
    });

    server.listen(4567, function () {
    console.log('%s listening at %s', server.name, server.url);
    });


    4567 포트를 사용하고 있고, url로 전달받은 문자열을 10만번 sha512 연산을 하고 반환한다.

    이때 CONTAINER_NAME 환경변수도 같이 반환해준다. 이것은 나중에 도커 컨테이너를 run 할때 환경변수로 지정해주면 로드벨런싱이 제대로 되고있는지 확인할 수 있다.



    package.json이 반드시 있어야 하는데, 그 이유는 필요한 node 종속성들을 npm install로 받아주고 있기 때문이다. node_modules를 통째로 이미지로 만들어버릴거면 물론 필요 없다. dockerfile은 내가 임의로 지정한 docker file의 이름이다.

    여기까지 구성된 상태에서 도커 이미지를 만들어보자.


    도커를 사용하기 위해선 먼저 도커를 설치해야한다.

    curl -fsSL https://get.docker.com/ | sudo sh

    를 실행하면 도커가 설치된다. 현재 root로 접속된게 아니라면 root의 비밀번호를 입력해야한다.


    그 후 docker build -t dockerfile . 를 입력하면 도커 이미지가 생성된다.

    뒤에 tag를 입력할 수도 있겠지만 생략할 경우 latest로 지정된다. (dockerfile:latest)

    다음으로 docker tag dockerfile lleellee0/dockerfile를 입력한다. (lleellee0은 사용자 이름이다. 그 이전에 도커 홈페이지에 가입을 했어야한다.)

    docker push lleellee0/dockerfile 를 입력하면 도커 허브에 내 레포지토리에 이미지가 올라간다. (만약 로그인해야된다고 나오면 docker login 을 실행하여 로그인한 후 다시 시도한다.)


    여기까지 진행했으면 배포 준비는 끝난거다.



    그럼 마지막으로 각각의 vps에서는 어떤 명령어를 실행하면 될까



    정답은 docker를 설치하고, 도커 허브에 올라간 도커 이미지를 다운받아서 run하면 되는 것이다.


    curl -fsSL https://get.docker.com/ | sudo sh

    docker run -d -p 8801:4567 -e CONTAINER_NAME=<컨테이너 이름> lleellee0/dockerfile


    이렇게 두줄 실행시켜주면 끝이다.

    run 명령에는 해당 docker 이미지가 없으면 다운 받는 것까지 포함되어있다.

    저기서 8801포트는 외부 포트이고, 4567포트는 내부포트(도커의 포트)이다.

    두개의 포트를 포트포워딩 해준 것과 같다.


    명령어만 정리하면


     <도커 이미지 빌드, 배포>

    curl -fsSL https://get.docker.com/ | sudo sh


    docker build -t dockerfile .

    docker tag dockerfile lleellee0/dockerfile

    docker push lleellee0/dockerfile


    <도커 이미지 다운로드 및 실행>

    curl -fsSL https://get.docker.com/ | sudo sh

    docker run -d -p 8801:4567 -e CONTAINER_NAME=??? lleellee0/dockerfile


    이다.



    마지막으로 1~4개의 컨테이너를 로드밸런싱하여 실행한 결과

    1개의 컨테이너만 실행시켰을 때의 결과 : 대략 14초 정도의 시간



    2개의 컨테이너만 실행시켰을 때의 결과 : 대략 7초 정도의 시간(10초가 걸릴 때도 있었다.)


    3개의 컨테이너만 실행시켰을 때의 결과 : 대략 5~6초 정도의 결과 (2개일 때와 마찬가지로 9초가 걸린 것도 있음)




    4개의 컨테이너만 실행시켰을 때의 결과 : 3~5초 정도의 결과





    결론 : 로드 밸런싱이 잘 되어 컨테이너를 추가할 수록 점점 시간이 짧아지고 있다.

    다만, 이 코드는 해시를 10만번 연산하는 것이라, CPU 로드가 너무 높은 코드이다.

    실제 웹 요청이라면, CPU 로드 보다는 DB에 대한 I/O 비율이 높을 것이다.

    그래도 이런 결과로도 유의미하다고 생각한다.



    만약 여기서 서버의 로직이 바뀌어야 한다면?

    단순히 Docker 이미지를 다시 받아서 컨테이너를 재시작해주면 된다.

    아직 자동화되어 있진 않지만, 다음 글에서는 자동으로 배포하는 것까지 진행해보겠다.


    Docker를 사용하지 않았다면 각각의 서버들을 설정하는데 훨씬 많은 시간이 걸렸을 것 같다.

    또한 서버 로직이 바뀌었을 때 서비스를 다시 시작하기 까지 도커처럼 순식간에 바꿀수 없을 것 같다.

Designed by Tistory.