이번에는 Docker로 MySQL Replication 구축 과정에 대해서 기록합니다. Replication에 관심을 가지게 된 계기는 디비를 사용하고 운영할 때 확장, 가용성이 중요하다. 일반적으로 가장 많이 사용되는 기술이 Replication이다.
최종적으로 MySQL에서 Replication에서 다음과 같은 학습을 하겠습니다.
이번에는 mysql PeronaServer을 기반으로 실행을 합니다. 물론 이 부분은 사용자에 따라서 다르게 설정이 가능합니다.
https://docs.percona.com/percona-server/8.0/quickstart-docker.html
이번에는 간단하게 설명하는 다음과 같은 구조를 가지고 있습니다.
Replication은 데이터베이스에서 데이터를 한 서버(Master)에서 다른 서버(Slave)로 복제하는 기술입니다. 이를 통해 데이터의 확장성, 가용성을 높이고, 장애 복구 시간을 줄이며, 읽기 성능을 향상시킬 수 있습니다. 특히 분산 시스템에서 자주 사용됩니다. 이를 통해 주요 서버의 부하를 줄이고 여러 서버에 걸쳐 데이터를 분산시킬 수 있습니다.
MySQL의 Replication은 일반적으로 Master-Slave 구조로 구성됩니다. Master는 데이터를 변경하는 트랜잭션의 로그를 기록하고, Slave는 이를 읽어 자신의 데이터베이스에 반영합니다.
Replication의 기본 동작 흐름은 다음과 같습니다:
Binlog 작성 (Master)
Master는 데이터베이스의 변경 사항(INSERT, UPDATE, DELETE 등)을 Binary Log(binlog)에 기록합니다.
이 로그는 트랜잭션의 순서와 상태를 포함하며 Slave에게 전달됩니다.
Relay Log 수신 및 저장 (Slave)
Slave는 Master의 Binlog를 복제하여 자신의 Relay Log에 저장합니다.
이 과정에서 Slave는 Master의 위치를 추적하여 복제를 지속적으로 유지합니다.
SQL 스레드 실행 (Slave)
Slave는 Relay Log를 기반으로 SQL 명령을 실행하여 Master와 동일한 데이터베이스 상태를 유지합니다.
비동기적 복제
MySQL Replication은 기본적으로 비동기 방식으로 동작합니다. Slave는 Master의 상태를 따라가지만 즉각적으로 동일한 상태가 되지는 않습니다. 필요에 따라 반동기적(Semi-synchronous) 또는 완전 동기적(Synchronous) 복제를 설정할 수도 있습니다.
확장성(Scalability)
Master는 데이터 변경 작업을 처리하고 Slave는 읽기 작업을 분담함으로써 읽기 성능을 향상시킬 수 있습니다.
가용성(High Availability)
Master 서버에 문제가 생기더라도 Slave 서버로 빠르게 전환(Failover)하여 시스템을 계속 운영할 수 있습니다.
백업 및 분석 용이성
Slave 서버를 이용해 데이터를 백업하거나 분석 작업을 수행할 수 있습니다. 이 과정에서 Master의 부하를 최소화할 수 있습니다.
유연성
다양한 복제 설정(예: Single-Master Multi-Slave, Multi-Master 등)을 지원하여 요구 사항에 맞게 구성을 조정할 수 있습니다.
비동기적 특성
기본적으로 비동기적으로 동작하므로, Master와 Slave 간 데이터 지연(Lag)이 발생할 수 있습니다.
반동기적 또는 동기적 복제를 설정하면 데이터 일관성이 향상되지만, 쓰기 성능에 영향을 미칠 수 있습니다.
Failover 복잡성
Master 장애 시 Slave를 Master로 승격(Promotion)하는 작업이 필요합니다. 이를 자동화하려면 추가적인 도구(예: Orchestrator)를 도입해야 합니다.
데이터 손실 가능성
Master 장애 시 Binlog가 Slave에 완전히 전달되지 않았다면 데이터 손실이 발생할 수 있습니다.
db001
|-- conf
|-- data
| |-- #innodb_redo
| |-- #innodb_temp
| |-- mysql
| |-- performance_schema
| `-- sys
`-- log
db002
|-- conf
|-- data
| |-- #innodb_redo
| |-- #innodb_temp
| |-- mysql
| |-- performance_schema
| `-- sys
`-- log
db003
|-- conf
|-- data
| |-- #innodb_redo
| |-- #innodb_temp
| |-- mysql
| |-- performance_schema
| `-- sys
`-- log
해당 경로를 살펴보면 크게 conf, data, log로 설정이 되어져 있습니다. 해당 부분은 docker는 stateless이기 때문에 결국에는 운영단계에서 사용하기 위해서는 volumn을 통해서 관련 정보를 저장합니다.
db 3개를 사용하기 때문에 3개의 폴더에 conf, data, log를 추가하겠습니다. 또한 권한을 추가가 필요합니다.
적절한 권한을 설정
테스트를 위해서 chmod 777를 주었을 때 mysql에서 권한의 범위가 매우 넓기 때문에 무시한 경우가 있습니다. 적절한 권한을 주기 위해서는 파일에 644, 폴더에는 755를 통해서 권한을 설정합니다.chmod 644 * chmod 755 *
server-id, report_host
를 적절하게 각 mysql에 맞게 설정을 합니다.[mysqld]
log_bin = mysql-bin
binlog_format = ROW
gtid_mode = ON
enforce-gtid-consistency = true
server-id = 100
log_slave_updates
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links = 0
log-error = /var/log/mysql/mysqld.log
pid-file = /var/run/mysqld/mysqld.pid
report_host = db001
[mysqld_safe]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/lib/mysql/mysql.sock
nice = 0
docker run -d -p 3306:3306 --name db001 \
-e MYSQL_ROOT_PASSWORD=1234 \
-v /Users/mugeon/docker/db/db001/log:/var/log/mysql \
-v /Users/mugeon/docker/db/db001/data:/var/lib/mysql \
-v /Users/mugeon/docker/db/db001/conf:/etc/my.cnf.d \
percona/percona-server:8.0.39-aarch64
docker run -d -p 3307:3306 --name db002 \
-e MYSQL_ROOT_PASSWORD=1234 \
-v /Users/mugeon/docker/db/db002/log:/var/log/mysql \
-v /Users/mugeon/docker/db/db002/data:/var/lib/mysql \
-v /Users/mugeon/docker/db/db002/conf:/etc/my.cnf.d \
percona/percona-server:8.0.39-aarch64
docker run -d -p 3308:3306 --name db003 \
-e MYSQL_ROOT_PASSWORD=1234 \
-v /Users/mugeon/docker/db/db003/log:/var/log/mysql \
-v /Users/mugeon/docker/db/db003/data:/var/lib/mysql \
-v /Users/mugeon/docker/db/db003/conf:/etc/my.cnf.d \
percona/percona-server:8.0.39-aarch64
docker exec -it -uroot db001 mysql -u root -p
# > 비밀번호 입력
docker exec -it -uroot db001 mysql -u root -p
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
# 권한 즉시 적용
FLUSH PRIVILEGES;
reset master;
CHANGE MASTER TO MASTER_HOST='${master_IP}', MASTER_USER='repl',
MASTER_PASSWORD='repl',
MASTER_AUTO_POSITION=1,
MASTER_SSL=1;
START REPLICA;
show slave status\G
하지만 조금의 문제점이 있습니다. 해당 docker를 통해서 꺼졌다가 켜지게 되면 IP가 변경이 됩니다. 또한 docker run을 통해서 관리하기 너무 힘들다는 문제가 있기 때문에 이 2가지 문제를 network, compose를 통해서 처리하겠습니다.
컨테이너는 언제든지 재 시작될 수 있고 Container가 재 시작하게 되면 IP가 변경될 수 있다. 기존의 Ifconfig를 통해서 Master DB의 IP를 기반으로 설정을 하여 HA 구성을 처리했다.
하지만 IP가 바뀐다면 Replication이 깨질 수 있기 때문에 Bridge Network를 구성하고 alias를 통해서 변경이 되어서 문제를 해결할 수 있게 하겠다.
docker network create --driver bridge mybridge
해당 과정을 통해서 이제는 docker run을 여러번 할 필요가 없어지고 관련설정을 따로 변수로 관리할 수 있습니다.
이제 생성된 Bridge Network를 이용하여 각 MySQL 서버의 컨테이너를 생성합니다. 각 컨테이너는 'mybridge'라는 이름의 네트워크에 연결되며, 'db001', 'db002', 'db003'라는 net alias를 각각 부여받습니다. 이를 통해 각 컨테이너는 서로 통신할 수 있는 환경을 구성하게 됩니다.
version: '3.8'
networks:
mybridge:
driver: bridge
services:
db001:
image: percona/percona-server:8.0.39-aarch64
container_name: db001
networks:
mybridge:
aliases:
- db001
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "1234"
volumes:
- /Users/mugeon/docker/db/db001/log:/var/log/mysql
- /Users/mugeon/docker/db/db001/data:/var/lib/mysql
- /Users/mugeon/docker/db/db001/conf/my.cnf:/etc/my.cnf
db002:
image: percona/percona-server:8.0.39-aarch64
container_name: db002
networks:
mybridge:
aliases:
- db002
ports:
- "3307:3306"
environment:
MYSQL_ROOT_PASSWORD: "1234"
volumes:
- /Users/mugeon/docker/db/db002/log:/var/log/mysql
- /Users/mugeon/docker/db/db002/data:/var/lib/mysql
- /Users/mugeon/docker/db/db002/conf/my.cnf:/etc/my.cnf
db003:
image: percona/percona-server:8.0.39-aarch64
container_name: db003
networks:
mybridge:
aliases:
- db003
ports:
- "3308:3306"
environment:
MYSQL_ROOT_PASSWORD: "1234"
volumes:
- /Users/mugeon/docker/db/db003/log:/var/log/mysql
- /Users/mugeon/docker/db/db003/data:/var/lib/mysql
- /Users/mugeon/docker/db/db003/conf/my.cnf:/etc/my.cnf
docker exec -it -uroot db001 mysql -u root -p
# > 비밀번호 입력
docker exec -it -uroot db001 mysql -u root -p
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
# 권한 즉시 적용
FLUSH PRIVILEGES;
docker exec -it db002 mysql -u root -p
# 비밀번호 입력
CHANGE MASTER TO
MASTER_HOST='db001',
MASTER_USER='repl',
MASTER_PASSWORD='repl',
MASTER_AUTO_POSITION=1;
START REPLICA;
SHOW SLAVE STATUS\G;
docker exec -it db003 mysql -u root -p
# 비밀번호 입력
CHANGE MASTER TO
MASTER_HOST='db001',
MASTER_USER='repl',
MASTER_PASSWORD='repl',
MASTER_AUTO_POSITION=1;
START REPLICA;
SHOW SLAVE STATUS\G;
이번 글에서는 Docker와 MySQL을 활용하여 Master-Slave Replication 환경을 구축하는 방법을 다뤘습니다. Replication을 통해 장애 복구 및 읽기 성능을 향상시킬 수 있음을 확인했습니다.
다음 단계에서는 Orchestrator를 활용하여 High Availability를 구현하고, 모니터링 도구를 통해 상태를 관리하는 과정을 다룰 예정입니다.
https://www.inflearn.com/course/mysql-docker/dashboard
https://kimdubi.github.io/cloud/docker_mysql_repl/
https://jane096.github.io/project/mysql-master-slave-replication/