docker를 이용한 mysql Replication 서버 구축

2023. 3. 28. 18:08Database

DB Replication이란?

DB 서버를 복제하는 기술이다.

일반적인 애플리케이션은 읽기 작업 비중이 쓰기 작업 보다 월등히 많다.

DB Replication을 사용하면 쓰기 전용 소스 서버를 하나 두고 읽기 전용 레플리카 서버를 여러대 두어서 서버의 부하를 줄일 수 있다.

보통 쓰기 전용 DB 서버를 master 서버라고 부르고 읽기 전용 서버를 slave 서버라고 부른다.

Replication Flow

출처: https://jongmin92.github.io/2019/11/13/Database/mysql-replication/

  1. Master 서버에서 변경 사항을 Binary Log에 기록한다.
  2. Slave 서버에 변경이 발생함을 통지한다.
  3. Slave 서버는 I/O Thread를 이용해서 Binary Log를 읽는다.
  4. 변경 사항을 Relay Log에 기록한다.
  5. SQL Thread를 이용해서 변경 사항을 반영한다.

Master Config 파일 설정

mysql 서버를 실행하기 전에 설정파일이 필요하다.

.cnf 파일은 MySQL 서버가 실행될 때 읽는 설정 파일이다.

default path는 /etc/my.cnf이다.

각각의 경로에 .cnf 확장자로 설정 파일을 생성해주자

# ~/Docker/mysql/master/config_file.cnf

[mysqld]
log-bin=mysql-bin
server-id = 1
binlog_cache_size = 5M
max_binlog_size = 512M
expire_logs_days = 30

mysql은 쓰기 작업시 /var/lib/mysql/ 경로에 바이너리 로그 파일을 남긴다.

log-bin : 바이너리 로그 파일명

server-id : Replication Group에서 서버를 식별하기 위한 id 값

binlog_cache_size : 바이너리 로그를 메모리에 버퍼링했다가 디스크로 기록된다. 해당 버퍼링용 메모리의 크기를 지정

max_binlog_size : 바이너리 로그 파일 최대 크기

expire_logs_days : 바이너리 로그를 보관할 날짜

Slave Config 파일 설정

Slave 서버는 Master 서버의 바이너리 로그를 읽어서 변경 내역을 Relay log에 기록하고 재실행하여 Master 서버와 동기화한다.

# ~/Docker/mysql/slave/config_file.cnf

[mysqld]
server-id = 2
relay_log = relay_log
relay_log_purge = TRUE
read_only

relay_log : CHANGE MASTER 명령을 실행하면 레플리카 서버는 기본 경로에 relay log를 생성한다. 이때 path를 지정하는 옵션

relay_log_purge : TRUE나 1로 설정하면 필요하지 않은 오래된 릴레이 로그를 자동으로 삭제한다.

read_only : 읽기 전용으로 설정하는 변수

Docker란?

도커는 프로그램을 컨테이너라는 독립된 환경으로 실행하고 관리할 수 있는 오픈소스 기술이다.

도커를 사용하면 쉽게 master/slave mysql container를 관리할 수 있다.

도커 버전 확인

$ docker -V

도커 MySQL 이미지 다운로드

$ docker pull mysql

$ docker pull mysql:8.0.22

도커 MySQL 이미지 컨테이너 생성 및 실행

아래의 명령어로 master/slave 컨테이너를 실행해준다.

docker run --name mysql-master -v ~/Docker/mysql/master:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD={password} -d mysql:latest
docker run --name mysql-slave -v ~/Docker/mysql/slave:/etc/mysql/conf.d --link mysql-master -e MYSQL_ROOT_PASSWORD={password} -d mysql:latest

—name : 컨테이너명

-e : 환경 변수 수정

-d : detached 모드로 실행, 백그라운드 모드로 실행

-p : 포트포워딩 {hostPort}:{containerPort}

-v : {Volume명}:{container 내부 mount될 경로}

—link : container끼리 ip 주소가 아닌 host명으로 접근하기 위해서 사용, slave 서버는 master 서버와 container간 통신을 하기 위해 사용

실행중인 container 확인

master, slave 두 컨테이너가 띄워지면 성공

> docker ps
CONTAINER ID   IMAGE                 COMMAND                   CREATED          STATUS          PORTS                               NAMES
6f4d80001a14   mysql:latest          "docker-entrypoint.s…"   34 minutes ago   Up 34 minutes   3306/tcp, 33060/tcp                 mysql-slave
ea5e5a9dd5cb   mysql:latest          "docker-entrypoint.s…"   34 minutes ago   Up 34 minutes   3306/tcp, 33060/tcp                 mysql-master

master 서버 접속

docker exec -it mysql-master bash

mysql -u root -p
Enter password:

mysql>

master 서버 상태 확인

아래는 바이너리 로그를 확인하는 명령어이다.

현재 mysql-bin.000003 라는 이름의 바이너리 로그에 기록하고 있으며 현재까지 기록된 위치는 2179라는 것을 의미한다.

Position은 실제 바이트 수를 의미한다.

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |     2179 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

Replication용 유저 생성

slave 서버가 master 서버로부터 바이너리 로그를 읽어오기 위해선 해당 권한을 가진 유저를 생성해야한다.

mysql> CREATE USER 'replication_user'@'%' IDENTIFIED WITH mysql_native_password BY 'replication_pass';

mysql> GRANT REPLICATION SLAVE ON *.* TO 'replication_user'@'%';

IDENTIFIED WITH mysql_native_password BY :

유저 인증 방식을 mysql_native_password로 지정한다. mysql 8 버전 default 유저 인증 방식은 caching_sha2_password인데 패스워드를 sha2 방식으로 암호화하지 않으면 에러가 발생한다.

REPLICATION SLAVE : Replication용 유저는 해당 권한을 가져야 한다.

mysql> use mysql;

mysql> select user, host, plugin from user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| replication_user | %         | mysql_native_password |
| root             | %         | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+

Sample 데이터 입력

CREATE DATABASE practice;

CREATE TABLE IF NOT EXISTS `practice`.`replication`
(
    `id`          bigint(20) NOT NULL AUTO_INCREMENT,
    `name`     varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = InnoDB;

USE practice;

INSERT INTO replication(name) VALUES('sixhustle');

Master 서버 백업

Master 서버의 현재까지 변경사항을 백업하고 이를 .sql 파일로 dump한다.

bash-4.4# mysqldump -u root -p --opt --single-transaction --hex-blob --master-data=2 --routines --triggers --all-databases > master-dump.sql;
Enter password:

-u : 계정명

-p : 패스워드

—opt : 사람들이 가장 많이 사용하는 옵션을 모아 놓은 것

—single-transaction : innodb 엔진을 사용할 경우 lock 없이 일관된 데이터 덤프를 받으려면 추가해줘야함

--hex-blob : BINARY, VARBINARY, BLOB, BIT 컬럼에 대해서 값을 명시할 때 hexadecimal 형태로 기술하게 함

--master-data : dump 파일 안에 어느 바이너리 로그 위치까지 사용할 것인지 CHANGE MASTER 절로 자동으로 작성해준다. 2는 주석형태로 작성한다는 의미

—routines : stored routines 정보를 모두 포함시킨다.

—triggers : triggers 정보를 모두 포함시킨다.

—all-databases : 데이터베이스 전부를 dump

덤프 파일을 host 서버에 copy 한다.

docker cp mysql-master:/master-dump.sql .

Master & Slave 서버 동기화

카피된 덤프 파일을 slave container로 copy한다.

docker cp master-dump.sql mysql-slave:/.

slave 컨테이너 shell 창에 접속한 뒤, 덤프파일을 import하여 DB 변경 내용을 동기화한다.

bash-4.4# mysql -u root -p < master-dump.sql
Enter password:

Replication 설정

dump 파일을 생성할 때 --master-data 옵션으로 생성된 CHANGE MASTER 절을 복사한다.(주석으로 되어있다)

vi master-dump.sql

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=2179;

Slave 서버에 접속한 뒤, 복사한 CHANGE MASTER 절을 실행한다.

mysql> CHANGE MASTER TO 
MASTER_HOST='mysql-master',
MASTER_USER='replication_user',
MASTER_PASSWORD='replication_pass',
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=2179;

slave thread를 시작한다.

mysql> START SLAVE;

slave 상태를 출력하여 정상적으로 연동되었는지 확인한다.

Last_Errno, Last_Error, Last_IO_Errno, Last_IO_Error 가 비어있다면 정상 연동된 것이다.

mysql> SHOW SLAVE STATUS\\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: mysql-master
                  Master_User: replication_user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 2179
               Relay_Log_File: relay_log.000002
                Relay_Log_Pos: 636
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 2179
              Relay_Log_Space: 840
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
                  Master_UUID: a0e6d99b-cd31-11ed-b4a4-0242ac110003
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
       Master_public_key_path:
        Get_master_public_key: 0
            Network_Namespace:

정리

이번 포스팅에선 Docker를 이용해서 MySQL Replication 서버를 구축해보았다.

다음 포스팅에선 애플리케이션단에서 DB Replication을 설정하는 방법에 대해서 다뤄보겠다.

Reference

https://poiemaweb.com/docker-mysql

https://jupiny.com/2017/11/07/docker-mysql-replicaiton/

https://velog.io/@sixhustle/mysql-replication