MySQL Replication 구성기

MySQLDB
by hwaneehwanee·Sep 2, 2024

최근 MySQL Replication을 구성할 일이 있었다. 이후 다시 리마인드할 수 있도록 로컬 환경에서 동일한 구성을 직접 다시 진행해보았고, 그 과정을 기록해두려고 한다.

본 구성은 Docker 컨테이너 환경에서 진행했으며, GTID 기반 Replication을 기준으로 설명한다.

Database Replication 이란

Database Replication은 하나의 데이터베이스에서 발생한 변경 사항을 다른 데이터베이스로 복제하여 데이터의 일관성을 유지하고 가용성과 성능을 향상시키는 기술이다.

일반적으로 데이터를 생성·수정하는 서버를 Master 서버라고 하며, 데이터를 복제받는 서버를 Slave(Replica) 서버라고 한다.

예를 들어 Master 서버에서 데이터가 변경되면 해당 변경 내용이 Slave 서버로 전달되고, Slave 서버는 이를 동일하게 반영한다. 이를 통해 여러 서버가 동일한 데이터를 유지할 수 있다.

Replication을 사용하는 대표적인 이유는 다음과 같다.

  • Slave 서버를 백업 서버로 활용할 수 있다.
  • 읽기(Read) 요청을 Slave 서버로 분산하여 Master 서버의 부하를 줄일 수 있다.
  • 운영 데이터에 영향을 주지 않고 분석 또는 테스트 환경으로 활용할 수 있다.
  • 서비스 가용성을 높일 수 있다.

정리하면 Database Replication은 여러 서버에 동일한 데이터를 유지하여 서비스의 안정성과 성능을 향상시키는 기술이라고 할 수 있다.

Docker 환경에서 MySQL Replication 구성하기

이제 도커환경에서 MySQL Replication을 구성해보자.
만약 도커가 설치되어있지 않다면 먼저 설치를 진행해야 한다.

도커 설치되었는지 확인(터미널)

$ docker -v

참고) MySQL Replication 구성을 위해 폴더 구성을 다음과 같이 구성하였다.

  • mysql_docker
    • master
      • config // mysql config 파일이 존재
      • data // mysql data를 저장
      • log // mysql log를 저장
    • slave
      • config
      • data
      • log

MySQL 설정파일 생성하기(마스터, 슬레이브)

먼저 아래와 같이 마스터용 config파일을 생성한다.

  • 파일 이름 : my.cnf
  • 파일 위치 : mysql_docker/master/config
[mysqld]
server-id=1
gtid-mode=ON
enforce-gtid-consistency=ON
log_slave_updates=ON

# binary log
log_bin=/var/log/mysql/bin.log
binlog_format=STATEMENT
binlog_expire_logs_seconds=604800
binlog_checksum=NONE
binlog_row_image=FULL

# general log
general_log=ON
log_output=FILE
general_log_file=/var/log/mysql/general.log

# error log
log_error=/var/log/mysql/error.log

# slow query log
slow_query_log=ON
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log-queries-not-using-indexes

참고 ) MySQL 8버전 부터는 바이너리 로그 보관주기 명령어가 expire_logs_days에서 binlog_expire_logs_seconds로 변경되었다.


다음으로 슬레이브용 config파일을 생성한다.
(주의할 점은 server-id가 식별자이기 때문에 마스터용 config파일에서 설정한 값과 달라야 한다.)

  • 파일 이름 : my.cnf
  • 파일 위치 : mysql_docker/slave/config
[mysqld]
server-id=2
gtid-mode=ON
enforce-gtid-consistency=ON
log_slave_updates=ON

# binary log
log_bin=/var/log/mysql/bin.log
binlog_format=STATEMENT
binlog_expire_logs_seconds=604800
binlog_checksum=NONE
binlog_row_image=FULL

# general log
general_log=ON
log_output=FILE
general_log_file=/var/log/mysql/general.log

# error log
log_error=/var/log/mysql/error.log

# slow query log
slow_query_log=ON
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log-queries-not-using-indexes

도커로 MySQL 서버 실행하기

먼저 아래 도커 명령어로 마스터 서버를 실행한다.
MySQL은 8.0버전으로 진행한다. 버전마다 실행 명령어가 다를 수 있으니 주의.

# 명령어
docker run -d -p {할당할 포트}:3306 \
 -e MYSQL_ROOT_PASSWORD={Root 패스워드} \
 -v {mysql data를 저장할 경로}:/var/lib/mysql \
 -v {mysql 적용할 config파일 경로}:/etc/mysql/conf.d/my.cnf \
 -v {mysql log를 저장할 경로}:/var/log/mysql \
 --name {docker container 이름} \
 mysql:8.0


# 아래는 예시
docker run -d -p 13306:3306 \
 -e MYSQL_ROOT_PASSWORD=test \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/master/data:/var/lib/mysql \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/master/config/my.cnf:/etc/mysql/conf.d/my.cnf \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/master/log:/var/log/mysql \
 --name mysql_master_container \
 mysql:8.0


다음으로 아래처럼 슬레이브 서버를 실행한다.
(도커 실행시 주의할점은 마스터에서 할당한 포트와  슬레이브에서 할당한 포트가 달라야한다.)

# 명령어
docker run -d -p {할당할 포트}:3306 \
 -e MYSQL_ROOT_PASSWORD={Root 패스워드} \
 -v {mysql data를 저장할 경로}:/var/lib/mysql \
 -v {mysql 적용할 config파일 경로}:/etc/mysql/conf.d/my.cnf \
 -v {mysql log를 저장할 경로}:/var/log/mysql \
 --name {docker container 이름} \
 mysql:8.0


# 아래는 예시
docker run -d -p 23306:3306 \
 -e MYSQL_ROOT_PASSWORD=test \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/slave/data:/var/lib/mysql \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/slave/config/my.cnf:/etc/mysql/conf.d/my.cnf \
 -v /Users/hwaneehwanee/Desktop/mysql_docker/slave/log:/var/log/mysql \
 --name mysql_slave_container \
 mysql:8.0


컨테이너가 실행된 모습 image.png


도커 컨테이너의 MySQL 접속
아래 명령어를 실행하여 MySQL로 접속한다. 패스워드는 docker로 mysql 컨테이너를 실행할때 설정한 ROOT 패스워드이다.

# 명령어
docker exec -it {컨테이너명 또는 컨테이너아이디} mysql -u root -p

# 아래는 예시
docker exec -it mysql_master_container mysql -u root -p

MySQL Replication 복제 방식 (GTID, Global Transaction ID)

전통적인 복제 방식에서는 binlog 파일과 포지션을 기반으로 복제를 설정했지만, 복제를 더 효율적이고 안전하게 관리하기위해 GTID방식으로 진행하였다.

  • GTID 방식은 각 트랜잭션이 실행될때, 고유한 ID가 할당된다.
  • GTID 구성은 "server_uuid:트랜잭션_ID"로 구성되어 있다.
  • 위와 같이 구성된 GTID 덕분에 슬레이브 서버는 정확히 어떤 트랜잭션이 마스터에서 실행되었는지 파악하고, 어떤 트랜잭션을 복제해야 하는지 쉽게 결정할 수 있다. 즉, 슬레이브는 이미 적용된 GTID를 기억하고, 이후에 실행되지 않은 트랜잭션만 복사하여 적용해야한다. 이러한 방식은 장애 복구 또는 서버 재시작시 상당히 편리하다.
  • 이와 같은 기능 덕분에 binlog파일로 복제를 관리하던 전통적인 방식보다 간편히 관리할 수 있게 되었다.
GTID 예시 : "3E11FA47-71CA-11E1-9E33-C80AA9429562:23"


gtid_mode 활성화 확인
my.cnf에서 gtid 모드를 활성화하였다면 실제 MySQL 서버에서 다음과 같은 설정값이 조회되어야 한다.
마스터, 슬레이브 서버 모두 실행하여 확인.

mysql > SHOW VARIABLES LIKE '%gtid_mode%'

image.png


MySQL 마스터에서 Replication을 위한 전용 유저 생성하기

마스터 MySQL로 접속하여 유저를 생성하고 권한을 부여한다.
MySQL8.0 이상 부터는 기본 비밀번호 인증 플러그인이 mysql_native_password에서 caching_sha2_password로 변경되어, 유저 생성시 mysql_native_password로 사용하도록 설정한다.

# 명령어
mysql > CREATE USER '{아이디}'@'%' IDENTIFIED WITH mysql_native_password BY '{패스워드}';
mysql > GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO '{아이디}'@'%';
mysql > FLUSH PRIVILEGES;
mysql > SELECT user,host,authentication_string FROM mysql.user;


# 아래는 예시
mysql > CREATE USER 'replication'@'%' IDENTIFIED WITH mysql_native_password BY 'test';
mysql > GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'replication'@'%';
mysql > FLUSH PRIVILEGES;
mysql > SELECT user,host,authentication_string FROM mysql.user;

image.png


이제 슬레이브에서 마스터를 연결하고 실행해보자

슬레이브 MySQL로 접속하여 마스터 서버에 대한 연결정보를 셋팅한다. 본문에서는 로컬환경에서 구축했기 때문에 마스터 서버아이피를 PC IP로 설정하였다. 도커 컨테이너 환경이기 때문에 localhot, 또는 127.0.0.1로 하면 안된다.

# 명령어 (MySQL v8.0.23 이상)
mysql > CHANGE REPLICATION SOURCE TO 
	SOURCE_HOST='{마스터 아이피}',
    SOURCE_PORT=13306,
    SOURCE_USER='{마스터 replication 유저}',
    SOURCE_PASSWORD='{마스터 replication 패스워드}',
    SOURCE_AUTO_POSITION=1;

# 아래는 예시
mysql > CHANGE REPLICATION SOURCE TO 
	SOURCE_HOST='192.168.1.83',
    SOURCE_PORT=13306,
    SOURCE_USER='replication',
    SOURCE_PASSWORD='test',
    SOURCE_AUTO_POSITION=1;
    
---------------------------------------------------

# 명령어 (MySQL v8.0.22 이하)
mysql > CHANGE MASTER TO 
    MASTER_HOST='{마스터 아이피}',
    MASTER_PORT=13306,
    MASTER_USER='{마스터 replication 유저}',
    MASTER_PASSWORD='{마스터 replication 패스워드}',
    MASTER_AUTO_POSITION=1;

# 아래는 예시
mysql > CHANGE MASTER TO 
    MASTER_HOST='192.168.1.83', 
    MASTER_PORT=13306,
    MASTER_USER='replication', 
    MASTER_PASSWORD='test',
    MASTER_AUTO_POSITION=1;

image.png

슬레이브 실행
아래 명령어로 슬레이브를 실행한다.

mysql > start slave;


슬레이브 상태 확인
아래 명령어로 슬레이브 상태를 확인한다. "Slave_IO_Running", "Slave_SQL_Running" 값이 모두 "YES"여야 정상적으로 연결된 상태이다.

mysql > show slave status \G;

image.png


Replication 설정완료, 복제 테스트를 해보자

마스터 서버에서 쓰기작업을 진행해 본다. 간단하게 데이터베이스 스키마를 만들고 테이블을 만든 뒤 데이터를 insert 해본다.

데이터 베이스 스키마 생성

mysql > create database test_replication;
mysql > use test_replication;


테이블 생성

mysql > CREATE TABLE MY_TABLE (
	 id INT(11) NOT NULL AUTO_INCREMENT,
     name VARCHAR(20),
     CONSTRAINT MY_TABLE_PK PRIMARY KEY(id)
    );


데이터 생성

mysql > INSERT INTO MY_TABLE (name) VALUES ('test');


슬레이브에서 위의 작업들이 복제되었는지 확인

mysql > show databases;
mysql > use test_replication;
mysql > show tables;
mysql > select * from my_table;

image.png


마치며

도커환경에서 MySQL을 실행하고, Replication 설정을 통해 쓰기작업들이 자동으로 슬레이브로 복제된것을 확인하였다.

다음 내용에서는 Replication 운영시 맞이하게 될 장애상황들과 상황별 복구 방법을 여러가지로 정리해볼까 한다.

연관 포스트