개발놀이터

도커를 이용해 MySQL 레플리케이션 구조 구현해보기 본문

CS 지식/데이터베이스

도커를 이용해 MySQL 레플리케이션 구조 구현해보기

마늘냄새폴폴 2023. 6. 17. 19:31

이번 포스팅에선 도커를 활용해 MySQL 서버를 레플리케이션 (복제) 하여 DB 부하를 줄여보겠습니다. 

 

제 프로젝트 컨셉상 레플리케이션이 필요한 구조이긴 합니다만... 조금 의구심이 들긴 합니다. 데이터베이스 서버를 여러대 두는게 아니고 한 인스턴스에서  MySQL 서버 여러개 둔다고 DB 부하가 줄어들지는 모르겠습니다. 

 

그래도 그냥 새로운거 도전한다고 생각하고 한번 해보도록 하겠습니다. 

 

레플리케이션

레플리케이션이 뭔지 먼저 짚고 넘어가도록 하죠. 

 

 

데이터베이스 레플리케이션이란, 데이터베이스 클러스터링에서 발전된 형태로서 DB 스토리지와 DB 서버가 하나의 쌍을 이뤄 원래라면 하나의 DB 서버가 부담해야할 부하를 여러대로 나눠 부하를 줄여줌으로써 데이터베이스의 성능이 떨어지지 않게 유지하는 방법입니다. 

 

클러스터링과 레플리케이션, 샤딩에 대해서 더 자세히 알고 싶으신 분들은 아래의 링크를 확인해주세요. 

 

https://coding-review.tistory.com/309

 

데이터베이스 튜닝 방법 (Clustering, Replication, Sharding)

오늘 포스팅에서는 데이터베이스 튜닝 방법인 Clustering, Replication, Sharding에 대해서 알아보도록 하겠습니다. 제가 설명할 이 세가지는 바로 분산시스템을 구현하는 방법들입니다. 분산시스템 혹

coding-review.tistory.com

 

보통 레플리케이션은 마스터 서버가 쓰기 작업 (INSERT, UPDATE, DELETE) 을 담당하고 슬레이브 서버가 읽기 작업 (SELECT) 을 담당합니다. 

 

왜냐하면 쓰기 작업은 읽기 작업에 비해 상대적으로 데이터베이스의 자원을 많이 사용하기 때문입니다. 

 

또한 쓰기 작업을 모든 DB 서버에서 하면 안되는 이유 중 하나가 바로 효율적인 문제에서 이슈가 있기 때문입니다. 

 

마스터 서버에서 쓰기 작업을 진행한 뒤에 모든 슬레이브 서버에 동기화 작업을 마치는 것이 벌크 연산을 진행하기에도 더 편한 부분이 있기 때문이죠. 

 

만약 모든 DB 서버가 쓰기 작업이 가능하다면 각각의 쓰기 작업을 모든 DB에 동기화 작업을 진행할 때 어마어마한 오버헤드가 발생할 것입니다. 

 

 

레플리케이션에 대해서 알아봤으니 본격적으로 MySQL 서버를 나눠서 레플리케이션을 진행해봅시다. 

 

MySQL 레플리케이션

먼저 고지하고 넘어가야 할 점은 이번 포스팅에서 사용할 도커에 관련한 사용방법은 기술하지 않고 넘어갑니다. 도커와 도커 컴포즈에 대해서 자세히 알고 싶으신 분들은 아래의 링크를 확인해주세요. 

 

https://coding-review.tistory.com/category/%EB%B0%B0%ED%8F%AC/docker

 

'배포/docker' 카테고리의 글 목록

 

coding-review.tistory.com

 

이제 준비물을 준비해보겠습니다. 

 

  1. MySQL configuration 파일
  2. 자신의 루트 주소

먼저 MySQL configuration 파일이 필요합니다. 이 파일은 추후 올라갈 MySQL 컨테이너와 마운트할 파일입니다. 

 

그 전에 먼저 제 인스턴스 구조를 보여드리겠습니다. 

 

mysql 바로 아래 있는 docker-compose 파일들은 잠시 무시하시구요. 주의깊게 봐야할 것은 config_file.cnf의 위치입니다. 이제 각각의 파일을 보여드리겠습니다. 

 

1. MySQL Configuration 파일 작성

[mysqld]
log-bin=mysql-bin
server-id=1
default_authentication_plugin=mysql_native_password

위의 파일은 마스터 서버에 마운트될 설정 파일입니다. 파일 확장자는 반드시 cnf로 해주세요! 

 

[mysqld]
server-id=2
log_bin=mysql-bin
relay_log=/var/lib/mysql/mysql-relay-bin
log_slave_updates=1
read_only=1
default_authentication_plugin=mysql_native_password
[mysqld]
server-id=3
log_bin=mysql-bin
relay_log=/var/lib/mysql/mysql-relay-bin
log_slave_updates=1
read_only=1
default_authentication_plugin=mysql_native_password

슬레이브 서버에 마운트 될 두개의 설정 파일입니다. 

 

이렇게 준비물이 끝났다면 이제 자신의 루트 주소를 알아야합니다. 

 

$ cd /

위의 명령어로 루트 주소로 들어가서 보시면 디렉토리들이 있을텐데 거기서 자신의 프로젝트가 있는 파일로 들어가주세요. 

 

저는 /Users/capston/datadir이 제 프로젝트가 있는 파일입니다. 

 

이제 본격적으로 도커 컴포즈를 작성해봅시다. 

 

2. docker-compose 작성

version: "3.8"
services:
  db-master:
    image: mysql:8.0.17
    container_name: capston-mysql-master
    restart: always
    environment:
      MYSQL_USER: "user"
      MYSQL_PASSWORD: "root"
      MYSQL_ROOT_PASSWORD: "root"
    ports:
      - "3306:3306"
    volumes:
      - /Users/capston/datadir/master:/var/lib/mysql
      - /Users/capston/datadir/master:/var/lib/mysql-files
      - /home/ubuntu/docker/mysql/master/config_file.cnf:/etc/mysql/conf.fd/my.cnf
    networks:
      - net-mysql
  db-slave1:
    image: mysql:8.0.17
    container_name: capston-mysql-slave1
    restart: always
    environment:
      MYSQL_USER: "user"
      MYSQL_PASSWORD: "root"
      MYSQL_ROOT_PASSWORD: "root"
    ports:
      - "3307:3306"
    volumes:
      - /Users/capston/datadir/slave1:/var/lib/mysql
      - /Users/capston/datadir/slave1:/var/lib/mysql-files
      - /home/ubuntu/docker/mysql/slave1/config_file.cnf:/etc/mysql/conf.d/my.cnf
    networks:
      - net-mysql
  db-slave2:
    image: mysql:8.0.17
    container_name: capston-mysql-slave2
    restart: always
    environment:
      MYSQL_USER: "user"
      MYSQL_PASSWORD: "root"
      MYSQL_ROOT_PASSWORD: "root"
    ports:
      - "3308:3306"
    volumes:
      - /Users/capston/datadir/slave2:/var/lib/mysql
      - /Users/capston/datadir/slave2:/var/lib/mysql-files
      - /home/ubuntu/docker/mysql/slave2/config_file.cnf:/etc/mysql/conf.d/my.cnf
    networks:
      - net-mysql
networks:
  net-mysql:
    driver: bridge

조금 길지만 복붙이 거의 다입니다. 

 

문법은 설명하지 않습니다만 중요한 부분 몇개를 소개해드리겠습니다. 

 

image : 저는 이미지를 8.0.17 버전으로 했습니다. MySQL의 LTS 버전이기 때문에 사용했습니다. 

volumes : 첫번째 줄과 두번째 줄은 제가 루트 주소를 알아야 한다고 했던 이유입니다. MySQL과 같은 데이터베이스를 컨테이너로 올릴 때는 컨테이너가 삭제되면 모든 데이터가 날아가기 때문에 외부 저장소에 저장해야 할 필요가 있습니다. 

세번째 줄은 아까 만들었던 MySQL 설정 파일을 실제 올라갈 컨테이너의 설정 파일과 마운트하기 위해 적은 코드입니다. 이 부분으로 인해 다양한 설정을 할 수 있습니다. 예를 들어서 read-only로 설정한다든가 로그 파일의 이름을 정한다든가 하는 것을 설정할 수 있습니다. 

networks : 우리는 데이터베이스를 여러대 띄울 것이기 때문에 각각의 데이터베이스끼리 통신할 무언가가 있어야합니다. 이 networks 설정으로 MySQL 서버들은 브릿지 형태로 연결될겁니다. 

 

이제 컨테이너를 띄우시면 컨테이너가 잘 올라간 모습을 볼 수 있습니다. 

 

$ docker-compose -f ./docker-compose-mysql.yml up -d
$ docker ps

이제 슬레이브 서버에 가서 동기화 작업을 진행해야 합니다. 

 

3. 동기화 작업

동기화 작업은 다음과 같은 순서로 진행됩니다. 

 

  1. 마스터 서버의 IP 주소 찾기
  2. 마스터 서버의 로그 파일 찾기
  3. 슬레이브 서버에서 명령어 입력
  4. 테스트

1. 마스터 서버의 IP 주소 찾기

마스터 서버의 IP 주소는 아래의 명령어를 이용해 찾을 수 있습니다. 

 

$ docker network ls

우리가 필요한 건 저겁니다. 네트워크 아이디를 입력해야합니다. 

 

jq 가 없으면 설치하시면 됩니다. 

 

$ sudo apt-get install jq

 

저 마스터 서버의 IP 주소를 어디다 적어주세요. 

 

2. 마스터 서버의 로그 파일 찾기

이제 bash 로 컨테이너에 접속한 뒤 MySQL에 접속해봅시다. 

 

순서대로 

 

$ docker exec -it ${자신의 MySQL 컨테이너 이름} bash
# mysql -u root -p
Enter password:

mysql> show binary logs;

 

이렇게 치면 로그 이름이 나옵니다. 이 로그 중 사이즈가 작은 두개를 선정합니다. 저는 2번하고 3번을 선정하겠습니다. 로그 이름은 binlog.000002, binlog.000003 이렇게 적어두시면 됩니다. 

 

이제 빠져나와서 슬레이브 서버로 들어갑니다. 

 

여기서 빠져나오는 방법은 
mysql> quit
하시고 ctrl + P + Q 하시면 컨테이너 종료 없이 빠져나올 수 있습니다.

 

마스터 서버로 들어왔을 때와 똑같은 방법으로 슬레이브 서버로 들어옵니다. 

 

3. 슬레이브 서버에서 명령어 입력하기

CHANGE MASTER TO MASTER_HOST='${아까 적어둔 마스터 서버의 IP주소}', MASTER_USER='root', MASTER_PASSWORD='${아까 docker-compose에서 만든 마스터 서버의 비밀번호}', MASTER_LOG_FILE='${아까 적어둔 마스터 서버의 로그 파일 이름}', MASTER_LOG_POS=0, GET_MASTER_PUBLIC_KEY=1;

start slave;

show slave status\G;

이렇게 하고 상태를 보면?

 

저 두개가 Yes 가 뜨면 정상적으로 완료된겁니다. 

 

이제 테스트 해봅시다. 

 

4. 테스트

마스터 서버로 다시 넘어와서 몇가지 명령어로 확인해보죠. 

 

마스터 서버로 접속
mysql> show databases;
mysql> create database test_db;
mysql> show databases;

마스터 서버에 정상적으로 데이터베이스를 하나 만들고

 

슬레이브 서버로 접속
mysql> show databases;

슬레이브 서버에도 정상적으로 잘 들어간 모습을 확인할 수 있습니다. 

 

 

마치며

사실 이 부분을 진행할까 말까 굉장히 고민했습니다. 데이터베이스 서버만 여러개 둔다고 DB 부하가 줄어들까..? 

 

이 궁금증은 아직 해결해야 할 문제 중 하나입니다. 다음 목표는 프로메테우스라는 모니터링 툴을 이용해서 DB 서버의 상태를 확인해서 실제 서버가 하나일 때랑 여러개일 때를 비교해서 분석하는 포스팅을 진행하도록 하겠습니다. 

 

 

 

출처

https://huisam.tistory.com/entry/mysql-replication

 

MySql - Master Slave Replication 구조 만들어보기

MySQL Master Slave 구조 만들어보기 일반적으로 디비에 대한 트래픽 분산을 위해서 Mysql Replication 를 통해서 트래픽 집중 문제를 해결할 수 있는데요 Master에게는 데이터 동시성이 아주 높게 요구되는

huisam.tistory.com