Docker #6. 스웜을 사용한 실전 어플리케이션 개발 - Part1
Prog. Langs & Tools/Docker

Docker #6. 스웜을 사용한 실전 어플리케이션 개발 - Part1

이번 포스팅은 <도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문> 책의 CH04. 스웜을 이용한 실전 어플리케이션 개발 내용을 공부하고 정리한 내용이다. 도커를 사용하여 어플리케이션을 개발하는 흐름을 이해하고 스웜을 이용한 배포에 초점을 맞추어서 자세한 코드를 다 확인하면서 가지는 않음을 미리 명시한다.

아키텍처 구성

간단한 TODO App을 만들어 보려고 한다. 대략적인 아키텍처는 다음과 같다.

<도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문> p129

데이터베이스로는 MySQL, API는 Restful API(예제 코드는 Go 언어 기반), 웹 어플리케이션(Vue / React), Nginx 프록시 서버 이렇게 네 가지로 크게 구분해서 구성 요소를 생각해 볼 수 있다. Nginx의 경우 어플리케이션 프론트엔드 서버 및 API 앞단의 리버스 프록시 역할을 하며, 캐싱이나 라우팅 등에도 사용이 된다. 이전 포스팅에서 만들어 놓은 스웜 클러스터에 노드 4개가 있는 환경을 사용한다.

서비스는 다음과 같이 묶는다.

  • MySQL
    • mysql_master
    • mysql_slave
  • Application
    • app_nginx
    • app_api
  • Frontend
    • frontend_nginx
    • frontend_web

MySQL 서비스 구축

어플리케이션의 데이터 스토어 역할을 할 MySQL을 먼저 구축한다. MySQL 컨테이너 여러개를 마스터-슬레이브로 묶는 과정이 추가적으로 필요한 구성이다. 환경 변수 값에 따른 마스터/슬레이브 설정 이미지를 생성한 후, 컨테이너 안에 있는 설정 파일 및 스크립트를 다루는 방법, 데이터베이스 초기화, 마스터-슬레이브 간의 레플리케이션 설정 등을 통해 컨테이너 여러개로 구성된 데이터 스토어를 구축하는 방법을 알아본다.

MySQL 이미지를 만들기 위한 레포지토리를 클론한다.

$ git clone https://github.com/gihyodocoer/tododb

데이터베이스 컨테이너를 구성하는 방식은 다음과 같이 진행하고자 한다.

  • MySQL 컨테이너는 도커 허브에 등록된 mysql:5.7 이미지를 기반으로 생성
  • 마스터-슬레이브 컨테이너는 두 역할을 모두 수행할 수 있는 하나의 이미지로 생성
  • MYSQL_MASTER 환경변수의 유무에 따라 해당 컨테이너가 마스터로 동작할지, 슬레이브로 동작할지 결정
  • replicas 값을 조절해 슬레이브를 늘릴 수 있게 함

각 데이터베이스의 이름 및 인증 정보에 대한 환경변수 값들은 미리 설정해 둔다.

마스터-슬레이브를 구성하려면 MySQL의 설정 파일인 mysqld.cnf를 수정해야 한다. 수정해야 하는 항목들은 다음과 같다.

  • log-bin : 레플리케이션을 사용하려면 바이너리 로그가 필요하다. 따라서 log-bin 항목에 바이너리 로그를 출력할 경로를 설정한다. log-bin은 마스터와 슬레이브에 모두 사용하기 때문에 설정에 차이가 없다.
  • server-id : MySQL 서버를 식별하기 위한 유일 값이다. 마스터와 슬레이브로 구성된 스택 안에서 중복이 없도록 설정한다. 예를 들면 컨테이너의 IP주소에서 3,4번째 옥텟 값을 뽑아 서버 간 중복되지 않는 server-id값을 다음과 같이 설정할 수도 있다.
# tododb/add-server-id.sh

#!/bin/bash -e
OCTETS={`hostname -i | tr -s '.' ' '`}

MYSQL_SERVER_ID=`expr ${OCTETS[2]} \* 256 + ${OCTETS[3]}`
echo "server-id=$MYSQL_SERVER_ID" >> /etc/mysql/mysql.conf.d/mysqld.cnf

레플리케이션 설정

마스터와 슬레이브 간 레플리케이션 설정을 준비하고, 슬레이브 컨테이너가 실행될 때 자동으로 레플리카로 설정되게 한다. 다음과 같은 순서로 shell 스크립트를 작성한다.(tododb/prepare.sh)

  1. 환경 변수에 따라 마스터/슬레이브로 결정
  2. 슬레이브와 마스터 간의 통신 확인 : 슬레이브가 마스터에 MySQL 명령을 실행하려면 마스터의 호스트명을 알고 있어야 한다. 이 호스트명을 전달하기 위해 환경변수 MYSQL_MASTER_HOST를 사용한다. 슬레이브에서 마스터에 MySQL 명령을 실행하려면 호스트 명을 -h 옵션값으로 주면 된다.
    $ mysql -h MYSQL_MASTER_HOST ...
  3. 마스터에 레플리카 사용자 및 권한 추가 : 레플리케이션을 사용하려면 마스터에 레플리카 및 사용자 권한을 추가해야 한다. CREATE USER 명령과 GRANT REPLICATION SLAVE 명령에 IP 주소가 필요하다는 점에 주목한다. 마스터에서는 슬레이브의 호스트명이나 IP를 알 수 없기 때문에 hostname -i 명령을 사용해 이 값을 받아온다.
    mysql> CREATE USER IF NOT EXISTS '레플리케이션_사용자명'@'슬레이브_IP주소' identified by '레플리케이션_사용자_패스워드'; mysql> GRANT REPLICATION SLAVE on *.* to '레플리케이션_사용자명'@'슬레이브_IP주소';
  4. 마스터 binlog의 포지션 설정 : 레플리케이션을 시작하려면 슬레이브가 마스터의 호스트 및 binlog 파일명, binlog 포지션을 알아야 한다. 다음 명령어로 마스터에서 이 값을 확인할 수 있다. 이렇게 확인한 binlog 파일과 포지션을 슬레이브에 설정한다. prepare.sh 스크립트에서는 CHANGE MASTER에 설정할 값을 이 과정을 통해서 받아온다. SHOW MASTER STATUS 명령의 결과를 먼저 파일에 저장한 후, 이 파일 내용에서 binlog 파일명과 포지션을 파싱해 이 두 값을 각각 BINLOG_FILE과 BINLOG_POSITION 두 셸 변수에 대입한다.
    mysql> show master status;
  5. 레플리케이션 시작 : 4번에서 받아온 binlog 파일명(BINLOG_FILE)과 포지션(BINLOG_POSITION) 값을 사용해 슬레이브에서 CHANGE MASTER 명령과 START SLAVE 명령을 실행해 레플리케이션을 시작한다.

MySQL Dockerfile

Dockerfile을 다음과 같은 순서로 작성한다.

  1. 패키지 업데이트 및 wget 설치 : 패키지 목록을 업데이트 하고 docker image build 명령에서 사용하는 entrykit을 받아오기 위해 wget을 설치한다.
    RUN apt-get update RUN apt-get install -y wget
  2. entrykit 설치 : entrykit은 컨테이너 실행 시 처리할 내용을 기술하기 위한 도구로, 주 프로세스보다 먼저 실행할 명령이 있는 경우에 유용하다.
  3. 스크립트 및 각종 설정 파일 복사 : MySQL 컨테이너를 구성하기 위한 파일과 스크립트를 COPY 인스트럭션을 사용해 tododb에서 컨테이너로 복사.
  4. 스크립트, mysqld 실행 : 컨테이너에서 실행할 내용을 기술한다.
    ENTRYPOINT [ "prehook, "add-server-id.sh", "--", "docker-entrypont.sh" ]

이 도커파일을 빌드해 이미지를 만든다. 이 이미지를 스웜 클러스터의 워커 노드에서 사용할 수 있도록 레지스트리에 등록한다.

$ docker image build -t ch04/tododb:latest .
$ docker image tag ch04/tododb:latest localhost:5000/ch04/tododb:latest
$ docker image push localhost:5000/ch04/tododb:latest

빌드된 이미지를 사용해서 스웜에서 MySQL 마스터 및 슬레이브 역할을 할 2개의 서비스를 생성하고 실행한다. 마스터 서비스의 레플리카 수는 1, 슬레이브 서비스의 레플리카 서비스의 수는 2로 설정한다. stack 디렉터리에 todo-mysql.yml 파일을 만들고 이 파일에 2개의 서비스를 각각 정의한다. 앞에서 만든 overlay 네트워크 todoapp의 external 속성을 true로 정의해 각 서비스가 이 네트워크에 속하게 한다.

version: "3"

service: 
  master:
    image: registry:5000/ch04/tododb:latest
    deploy:
      replicas: 1
      placement:
        constraints: [node.role != manager]
      environment:
        MYSQL_ROOT_PASSWORD: gihyo
        ...
      networks:
        - todoapp

  slave:
    image: registry:5000/ch04/tododb:latest
    deploy:
      replicas: 2
      placement:
        constraints: [node.role != manager]
      depends_on:
        - master
      environment:
        MYSQL_ROOT_PASSWORD: gihyo
        ...
      networks:
        - todoapp

networks:
  todoapps:
    external: true

스웜을 사용하여 todo-mysql.yml에 정의된 서비스를 todo_mysql 스택으로 manager 컨테이너에 배포한다. 스택을 사용해 여러 서비스를 배포하면 서비스명 앞에 스택명이 붙어서 마스터는 todo_mysql_master, 슬레이브는 todo_mysql_slave라는 이름이 된다.

$ docker container exec -it manager
$ docker stack deploy -c /stack/todo-mysql.yml todo_mysql

양이 길어져서 데이터 투입 테스트, API, 프론트엔드 영역은 다음 포스팅에서 이어서 해 보려고 한다.