Database

[MariaDB] Replication 적용기 - 4 (MHA)

KAispread 2024. 2. 26. 17:14
728x90
반응형

개요

이전 실습에서 MariaDB Replication 구성과 LazyConnectionDatasourceProxy를 활용하여 읽기 / 쓰기 트랜잭션의 Connection을 분리하고 반동기 복제 적용까지 진행하였다. 이를 통해 데이터 복제를 통한 백업, 읽기/쓰기 작업 부하 분산을 통한 성능 개선 등의 이점을 가져갈 수 있었다. Replication 구성 방법이 궁금하다면 다음의 포스팅을 참고하자.

2024.01.05 - [Database] - [MariaDB] Replication 적용기 - 1 (DB Setting)

 

하지만 Replication 구성만으로는 SPOF 문제가 발생할 수 있다. 앞서 진행했던 Replication 구성은 한대의 Master 서버를 통해 쓰기 작업을 수행하고 Replica 서버를 통해 읽기 작업을 수행하는데, Master 서버에 장애 발생시 쓰기 작업을 수행할 수 없는 상태가 된다. 이는 곧 서비스 전체 장애로 이어질 수 있다. 이에 따라 자동화된 장애 복구 솔루션이 등장하게 되었다.

 

 

MHA 란?

이와 같은 상황에서 선택할 수 있는 대표적인 이중화 솔루션으로는 MMMMHA이 있다. 이 두개의 솔루션은 Master 를 대상으로 health check를 통해 상태를 확인하고, 장애 발생시 자동으로 Replica 서버를 Master 서버로 승격시켜준다.

MMM과 MHA의 차이점을 간략하게 설명하면 MMM은 Multi-Master Replication Manager의 약자로 Active-Active 혹은 Active-Stanby 형태의 두 대 이상의 Master가 양방향 복제 방식으로 데이터를 최신 상태로 유지한다. Active 상태의 Master 서버에 장애가 발생했을 때 예비 Master가 승격되어 Failover 되는 방식으로 동작한다.

반면 MHAMaster High Availability 의 약자로, MMM과는 달리 Master에서 Replica 서버로 단방향 복제만 존재한다. MHA는 Master 서버에 장애가 발생했을 때 가장 최신의 데이터를 가지고 있는 Replica 서버를 Master 서버로 승격시켜 Failover 를 진행한다. MMM과 MHA 둘 다 Failback은 지원하지 않으므로 복구 작업은 별도의 솔루션이 필요하다.

본 포스팅에서는 보다 신뢰성있는 솔루션인 MHA를 적용할 예정이다.

MHA(Master High Availability) 의 특징은 다음과 같다.

  • Perl 기반의 Auto Failover solution
  • MySQdml GTID등 신 버전들의 기능들을 지원
  • MHA manager 가 Master 서버에 select 쿼리를 전송하며 서버의 상태를 확인함
  • Agentless 방식으로, MHA manager가 Replica 서버에서 명령을 수행하는 방식으로 Master로 전환

 

 

MHA 동작 방식

MHA(Master High Availability) 구조는 다음과 같다.

  • Master 서버는 쓰기 작업을 처리하여 Replica 서버로 복제한다.
  • MHA Manager 는 SELECT 쿼리 (CONNECT / INSERT도 사용)를 통해 주기적으로 Master 서버의 상태를 체크한다.

 

  • MHA Manager 가 Master 서버로부터 SELECT 쿼리에 대한 응답을 받지 못하면 장애가 발생한 것으로 판단하고 Failover를 수행한다.
  • Replica 서버의 RelayLog를 통해 가장 최신의 데이터를 가지고 있는 서버를 선택하고 해당 서버를 새로운 Master로 승격한다.
  • ReplicaDB 중 데이터 복제가 가장 느린 곳의 SQL 스레드가 RelayLog에 기록된 모든 이벤트를 실행할때까지 대기한다.
  • 가장 최신의 RelayLog와 log 포지션을 비교하여 유실된 이벤트를 전부 나머지 Replica 서버에 반영한다.
  • Master로 승격된 Replica 서버는 BinaryLog 포지션을 확인하여 반영되지 못한 이벤트를 반영한다.

만약, 장애가 발생한 Master 서버에 접근이 불가능한 상황이라면 데이터 유실이 발생할 수 있다. Semi-Sync 를 적용했다면 최소 한대 이상의 Replica 서버에 이벤트가 반영된 이후 Transaction commit 되므로 데이터 유실 가능성이 적어진다.

 

Failover 수행 이후 모습은 다음과 같다. Replica 서버가 Master 서버로 승격되고, 나머지 Replica 서버들은 새로운 Master 서버를 통해 데이터 복제가 일어난다. VIP(Virtual IP) 를 할당해서 사용하는 경우, VIP 회수 및 재할당 과정이 추가된다.

 

 

실습환경

  • Ubuntu 22.04
  • MariaDB 10.6.12
  • MHA 0.56

실습을 위해 Replica 서버 2대, Master 서버 1대, MHA 서버 1대를 준비한다. 각 서버에 필요한 설정들을 해주어야하므로 화면을 4분할하여 진행하는 것을 추천한다.

  • MHA          3.39.6.195
  • Master      15.164.245.19
  • Replica_1  54.180.148.81
  • Replica_2  54.180.148.82

MariaDB 및 SSH 접속을위해 각 서버간 3306포트, 22번 포트가 열려있어야 한다.

각 설정들을 어느 서버에서 진행해야하는지는 Master | Replica | MHA 와 같은 형태로 표현하겠다.

MHA 0.58 버전은 super_read_only 옵션을 지원하는데 이 기능은 MariaDB 10.6.x 버전에는 없는 옵션이므로 문제가 발생한다. 따라서, MHA 버전은 0.56 을 사용하겠다. 0.57을 사용해도 무방하다.

https://github.com/lzimd/mha-rpms/find/master

 

 

Replicaion 적용

Master | Replica

MHA를 적용하기위해선 DB단에서 Replication 구성이 되어있어야 한다. Replication 구성은 다음 포스팅을 참고하자.

2024.01.05 - [Database] - [MariaDB] Replication 적용기 - 1 (DB Setting)

앞선 포스팅을 잘 따라왔다면 Master-Replica 단방향 복제 설정이 되어있을텐데, 여기서 추가적으로 Replica 서버에서 Master 로 승격될 수 있도록 각 Replica 서버에도 Master 서버에서 했던 설정들을 적용시켜주어야한다.

// Replica_1
[mariadb]
server-id = 2
log-bin=mysql-bin
binlog_do_db=wemeet
read_only=1
relay_log_purge=0

// Replica_2
[mariadb]
server-id = 3
log-bin=mysql-bin
binlog_do_db=wemeet
read_only=1
relay_log_purge=0

각 Replica 서버에서 /etc/mysql/my.cnf 파일에 다음과 같이 복제 정보를 추가해준다. server-id 는 각 서버마다 달라야하고, binlog 이름은 모든 서버가 같아야함에 유의하자.

 

CREATE USER 'wemeet_master'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'wemeet_master'@'%';

각 Replica 서버에서 REPLICATION SLAVE 권한을 부여한 user를 생성해주면 Replication 설정은 완료된다.

 

 

기본 설정

Master | Replica | MHA

# PERL 설치 
sudo apt-get update
sudo apt-get install -y gcc make libdbi-perl libdbd-mysql-perl libio-socket-ssl-perl
sudo apt-get install -y libconfig-tiny-perl liblog-dispatch-perl libparallel-forkmanager-perl

sudo apt-get install libdbd-mysql-perl \
libconfig-tiny-perl \
libparams-validate-perl \
libparallel-forkmanager-perl \
liblog-dispatch-perl \
libtime-hires-perl \
libmodule-install-perl \

make

MHA 는 Perl 기반으로 동작하므로 관련 패키지를 설치해주자.

 

Master | Replica

grant all privileges on *.* to 'mha'@'%' identified by 'mha';
flush privileges;

MHA는 Manager 에서 각 서버에 접속하여 명령을 수행하는 방식으로 Failover 를 진행한다. Manager 에서 사용할 수 있는 User를 생성하자. (user name = mha, password = mha)

 

Master | Replica | MHA

# MHA 유저 생성
sudo useradd -g mysql -d /home/mhauser -m -s /bin/bash mhauser
sudo usermod -aG sudo mhauser

sudo passwd mhauser
New password: password
Retype new password: password
passwd: password updated successfully

# MHA 유저에게 Mysql 권한 부여
sudo chown -R mhauser:mysql /mha

각 서버에 접속할 수 있도록 user를 생성해준다.

 

 

MHA 설치

Master | Replica | MHA

# MHA 프로그램이 사용할 디렉터리 생성
sudo mkdir /mha

# MHA 프로그램이 위치할 디렉터리 생성
sudo mkdir /source
cd /source

# node 다운
sudo wget https://github.com/lzimd/mha-rpms/raw/master/mha4mysql-node-0.56.tar.gz

# 압축 해제 및 설치
sudo tar xvzf mha4mysql-node-0.56.tar.gz
cd /source/mha4mysql-node-0.56

sudo perl Makefile.PL
sudo make
sudo make install

모든 서버에 노드를 설치해준다. /source 디렉터리에 MHA node와 MHA manager 를 다운받아 관리하겠다.

 

MHA

# CPAN을 사용하여 YAML 모듈 설치
cpan YAML

# Perl 모듈 설치
# 시간 다소 소요
sudo perl -MCPAN -e "install File::Remove"
sudo perl -MCPAN -e "install Build"
sudo perl -MCPAN -e "install Module::Install"  
sudo perl -MCPAN -e "install Net::Telnet"
sudo perl -MCPAN -e "install Log::Dispatch"

# MHA Manager 설치
cd /source
sudo wget https://github.com/lzimd/mha-rpms/raw/master/mha4mysql-manager-0.56.tar.gz
sudo tar xvzf mha4mysql-manager-0.56.tar.gz

cd /source/mha4mysql-manager-0.56
sudo perl Makefile.PL
sudo make
sudo make install

MHA 서버에서 Perl CPAN 모듈을 사용하여 MHA 구동에 필요한 모듈들을 설치한다. 이후 Manager를 설치한다. 

 

 

SSH 연결 설정

Master | Replica | MHA

Failover를 수행하기 위해 각각의 서버들이 비밀번호 없이 각각 SSH 접속할 수 있어야한다.

# /etc/sudoers 파일 편집기 열기
sudo visudo

### 다음의 설정값 추가
# MHA config
mhauser ALL = (ALL) NOPASSWD:/sbin/ifconfig

다음과 같이 mhauser 에 대해 비밀번호를 요구하지 않는 설정 값을 추가하면 된다.

 

각 서버(Master, Replica, MHA)에서 다른 서버에 접속할 수 있도록 공개키를 줘야한다. 

각 서버간 SSH 접속이 가능하도록 22번 포트가 열려있어야함
- Master ↔ Replica, MHA
- Replica ↔ Master, MHA
- MHA  ↔ Master, Replica

 

#### MHA 서버부터 시작 (Master 서버와 Replica 1, 2 서버도 IP만 수정하여 동일하게 진행)

# Generate Key
sudo su - mhauser
ssh-keygen -t rsa # Press Enter 3 Times

---------------------------------------------------------
# 공개 키 인증 허용
sudo vim /etc/ssh/sshd_config

# 아래 내용을 찾아서 주석 해제 또는 추가
PubkeyAuthentication   yes
PasswordAuthentication no
AuthorizedKeysFile     .ssh/authorized_keys

# ssh 재시작
sudo service ssh restart
---------------------------------------------------------

# 나의 키를 Master, Replica 서버에 전송
# ssh-copy-id를 사용하여 키 복사
# 나의 키가 대상 서버의 .ssh/authorized_key 에 복사되고, 접속시 내 키를 사용하여 인스턴스에 접속함
ssh-copy-id -i ~/.ssh/id_rsa.pub mhauser@15.164.245.19
ssh-copy-id -i ~/.ssh/id_rsa.pub mhauser@54.180.148.81
ssh-copy-id -i ~/.ssh/id_rsa.pub mhauser@54.180.148.82

# Master, Replica 서버에 패스워드 입력 없이 접속 가능한지 확인
ssh mhauser@15.164.245.19
ssh mhauser@54.180.148.81
ssh mhauser@54.180.148.82

ssh 명령을 이용하여 패스워드 입력 없이 각 서버에 접속할 수 있도록 위 명령을 차례대로 수행한다. 

 

실습 도중 중간에 IP가 바뀌게되었는데, 이는 무시하길 바란다.

ssh-copy-id 명령이 정상적으로 수행되었다면 다음과 같은 문구가 출력되고 ssh 명령으로 패스워드 입력 없이 각 서버에 접속할 수 있다.

 

# 접속 정보를 수정할 수 없도록 read only로 권한 수정
cd .ssh
chmod 400 authorized_keys

접속 정보를 수정할 수 없도록 read only로 권한을 수정해준다. 이후 변경사항이 생긴다면 다시 권한을 변경하고 수정해주어야한다.

 

 

MHA 명령어 커스텀

MHA

mha 명령어는 masterha_manager --conf= "설정 파일 위치" 로 실행하는데, 더 편하게 명령을 입력할 수 있도록 커스텀해주자.

# MHA - mhauser
vim .bash_profile 에 아래 내용 추가

set -o vi
alias sshcheck='/usr/local/bin/masterha_check_ssh --conf=/etc/mha.cnf'
alias replcheck='/usr/local/bin/masterha_check_repl --conf=/etc/mha.cnf'
alias start='nohup masterha_manager --conf=/etc/mha.cnf --last_failover_minute=1 &'
alias stop='/usr/local/bin/masterha_stop --conf=/etc/mha.cnf'
alias status='/usr/local/bin/masterha_check_status --conf=/etc/mha.cnf'
alias log='tail -f /mha/manager.log'

# 저장 후
source .bash_profile

.bash_profile 파일

각각의 명령은 각 서버간 연결을 체크하거나, MHA manager 를 구동 혹은 정지, 로그 확인 명령을 나타낸다.

 

 

MHA config 파일 생성

MHA

MHA manager 가 참조할 설정 값을 입력하기위해 파일을 하나 생성하자.

# config 수정
sudo cp /source/mha4mysql-manager-0.58/samples/conf/app1.cnf /etc/mha.cnf

sudo vim /etc/mha.cnf

 

해당 파일에 다음과 같은 값들을 추가하자.

[server default]
# mha용 DB user 정보
user=mha
password=mha

# 각 서버의 User명
ssh_user=mhauser

# replication용 DB user 정보
repl_user=wemeet_master
repl_password=password

# manager base directory
manager_workdir=/mha
manager_log=/mha/manager.log
remote_workdir=/mha

# Binary Log 위치
master_binlog_dir=/var/lib/mysql

[server1] # master
hostname=15.164.245.19
candidate_master=1

[server2] # replica_1
hostname=54.180.148.81
candidate_master=1

[server3] # replica_2
hostname=54.180.148.82
candidate_master=1

주석은 제거해주자.

 

Checking

# ssh 접속 확인
sshcheck

mha 설정 값을 추가했다면 sshcheck 명령으로 각 서버간 ssh 접속이 가능한지 확인해보자.

정상적으로 ssh 접속이 가능하다면 다음과 같은 결과를 볼 수 있다.

 

 

# failover 체크
replcheck

이번에는 이전 단계에서 작성했던 mha 설정 값이 유효한지 확인하는 replcheck 를 수행하자.

이 때 Perl 5.24 버전에서는 다음과 같은 문제가 발생하는데, 이에 대한 내용은 다음 Issue 에서 확인할 수 있었다.

https://github.com/yoshinorim/mha4mysql-manager/issues/116

/usr/local/share/perl/5.24.1/MHA/NodeUtil.pm

 

sub parse_mysql_version($) {
  my $str = shift;
  ($str) =  $str =~ m/^[^-]*/g;
  my $result = sprintf( '%03d%03d%03d', $str =~ m/(\d+)/g );
  return $result;
}

sub parse_mysql_major_version($) {
  my $str = shift;
  $str =~ /(\d+)\.(\d+)/;
  my $strmajor = "$1.$2";
  my $result = sprintf( '%03d%03d', $strmajor =~ m/(\d+)/g );
  return $result;
}

NodeUtil 파일에서 parse_mysql_versionparse_mysql_major_version 부분을 찾아 다음과 같이 수정해주면 된다. 이 내용은 180 ~ 200 line 정도에 위치해있다.

 

 

스크립트 추가 (IP script, Failover script)

IP Script

# Replica IP <-> Master IP 변경 스크립트 작성
mkdir /mha/scripts
cp /source/mha4mysql-manager-0.56/samples/scripts/master_ip_online_change /mha/scripts/master_ip_online_change

vim /mha/scripts/master_ip_online_change

스크립트 파일을 복사하고 여기서 일부 내용을 수정해주자.

150 ~ 152 주석
244 ~ 248 주석

 

# mha 설정 파일 수정
sudo vim /etc/mha.cnf 


[server default]

...

master_ip_online_change_script=/mha/scripts/master_ip_online_change

...

[server1]
hostname=15.164.245.19
candidate_master=1

...

스크립트 수정 이후 해당 스크립트를 참조할 수 있도록 설정 파일에 파일 위치를 추가해준다.

 

 

Failover Script

cp /source/mha4mysql-manager-0.56/samples/scripts/master_ip_failover /mha/scripts/

sudo vim /mha/scripts/master_ip_failover

마찬가지로 스크립트 복사 이후 내용을 수정한다.

87 ~ 93 주석

# mha 설정 파일 수정
sudo vim /etc/mha.cnf 


[server default]

...

master_ip_online_change_script=/mha/scripts/master_ip_online_change
master_ip_failover_script=/mha/scripts/master_ip_failover

...

[server1]
hostname=15.164.245.19
candidate_master=1

...

스크립트 수정 이후 해당 스크립트를 참조할 수 있도록 설정 파일에 파일 위치를 추가해준다.

 

 

결과 확인

failover가 정상적으로 수행되는지 확인해보자. 우선, Master 서버와 Replica 서버의 mariadb 서버로 접속하여 임시 로그를 활성화하자.

# 임시 로그 활성화
SET GLOBAL log_output = 'table';
SET GLOBAL general_log = 1;

# 임시 로그 삭제
TRUNCATE TABLE mysql.general_log;

# 로그 보기
SELECT convert(argument using utf8) FROM mysql.general_log\G;

 

# MHA manager 시작 (start 혹은 명령어 직접 입력)
start
nohup masterha_manager --conf=/etc/mha.cnf --last_failover_minute=1 &

start 혹은 명령어를 직접 입력하여 MHA manager를 실행한다.

/etc/mha.cnf 파일의 설정 값을 참조하여 mha manager가 실행되고 Master 의 상태를 체크한다.

 

Master DB

MHA manager가 Master 서버를 대상으로 SELECT 쿼리를 날리면서 주기적으로 상태를 확인하는 것을 볼 수 있다.

 

Replica_1 과 Replica_2 의 Replication 상태

SHOW REPLICA STATUS\G; 명령을 통해 Replication의 상태를 확인할 수 있다. 현재는 두 Replica 서버 모두 동일한 Master 서버와 연결되어 있는 모습이다.

 

이제 MasterDB에 장애가 발생한 것을 가정해보자. 간단히 service stop 명령을 통해 mariadb 프로세스를 중지시키자.

# 서비스 중지
sudo service mariadb stop

# 상태 확인
sudo service mariadb status

 

Master 서버가 중지되었다면 MHA manager가 이를 인지하고 Failover를 수행할 것이다. log를 통해 확인해보자.

# MHA log 확인
log
tail -f /mha/manager.log

MHA Log

로그에서 확인할 수 있듯, Master가 down 되었다는 것을 인지하고 자동으로 Failover 를 수행하여 Replica 서버를 Master 서버로 변경한다.

 

Replica_1은 Master가 되고 Replica_2는 새로운 Master에 연결된 모습

다시 각 Replica 서버에서 SHOW REPLICA STATUS\G; 명령을 수행해보았다. 결과는 위와 같이 Replica_1 이 Master가 되어 Replica_2 가 새로운 마스터에 연결된 모습을 확인할 수 있다. 새로운 Master에 쓰기작업을 수행하면 Replica_2에도 반영되는 상태가 된 것이다.

 

 

마무리

Replication 을 공부하며 정말 많은 것들을 배울 수 있었다. 데이터베이스단에서 어떻게 데이터를 복제하는지는 물론이고, Spring에서 Transaction 수행 방식과 커스텀 방법, Failover 솔루션까지 학습하며 꽤 많이 성장했음을 느꼈다. 다만, Replication 이라는 것은 리소스를 추가적으로 사용하는 비용이기때문에 이를 어떻게하면 더 효율적으로 관리할 수 있을지에 대한 고민을 더욱 필요하다고 생각된다. 특히, MHA를 학습하며 VIP라는 개념까지 알게되었는데 다음에는 VIP 스크립트도 추가하여 더욱 완성도 있는 솔루션을 구축해보고싶다는 생각이 들었다.

개발하다보면 정말 다양한 문제 상황들에 마주하게된다. 내가 직접 겪었던 문제를 해결하는 경험도 중요하지만 스스로 문제가 될만한 요소들을 찾아서 개선해보는 노력도 꼭 필요하다고 생각한다. 그래야 내가 경험해보지 않은 상황에서도 유연하게 대처할 수 있는 힘이 생기기 때문이다.  앞으로도 기술에 대해 고민하고 성장하여 더 많은 문제를 해결할 수 있는 개발자가 되고싶다는 생각을 끝으로 글을 마친다.

 

 

Reference

https://mariadb.com/kb/en/standard-replication/

https://onestone-note.tistory.com/34

https://github.com/yoshinorim/mha4mysql-manager/issues/116

https://yhjin.tistory.com/2

 

728x90
반응형