M-HA MySQL 설정
MySQL 은 인기있는 오픈소스 데이터베이스 시스템이다. 언제나 데이터베이스 시스템이라면 항상 따라오는 것이 Master-Slave 리플리케이션 환경이고 거기에 덧붙여 자동 페일오버(Auto FailOver) 를 어떻게 할 것이라는 골머리 앓는 주제가 따라서 나온다.
MySQL 의 경우에 자체 적인 FailOver 솔루션이 있다. Fabric 이 그것인데, 아직은 많이 쓰이지는 않는 모양이다. 대신에 AutoFailOver 를 지원하는 제3의 툴들이 존재하는데 M-HA 가 바로 이러한 솔루션이다.
M-HA 는 일본인이 Perl 을 이용해서 작성한 솔루션이다. github 저장소에 소스코드는 공개되어 있으며 저장소이름은 mha4mysql-manager 이다.
구조
M-HA 는 Manager 와 Node 로 이루어진다. Node 는 MySQL 이 설치된 서버에 모두 설치해줘야 한다. Mater/Slave 구조라면 Node 를 모두 설치해줘야 한다.
Manager 는 이들 Node 를 감시하고 이들 전체를 컨트롤하는 역활을 한다.
M-HA 특징
M-HA 를 AutoFailover 로 사용할만한 이유는 다음과 같다.
- Short downtime
- MySQL-Replication consistency
- Easy Installation
- No chagne to existing deployments
M-HA 를 고려할때에 중요한 부분이 짧은 다운타임이다. Master/Slave 구조에서 장애시에 Slave 를 Master 로 빠르게 승격을 시켜주어야 한다. 그래야만 두 서버간에 데이터가 달라지는 데이터 무결성이 깨지지 않게 된다. M-HA 는 비교적 이러한 데이터 무결성을 보장하기 위해서 많은 노력을 기울인 솔루션이라고 보면 된다.
M-HA 구성도
최소한의 환경에서 M-HA 를 구성도는 대략 다음과 같다.
M-HA Node 는 MySQL 이 설치된 서버라면 전부 설치해주어야 한다. M-HA Manager 는 외부서버에 설치해된다.
Node 설치
설치는 M-HA 구글 저장소에 가면 각 배포판별로 바이너리로 배포하고 있어 쉽게 설치가능하다. 여기서는 소스를 가지고 컴파일 설치하도록 하겠다.
git 저장소에 소스를 다운받거나 Clone 해서 가지고 와도 된다.
1 2 3 4 5 6 7 |
]# git clone https://github.com/yoshinorim/mha4mysql-node.git Cloning into 'mha4mysql-node'... remote: Counting objects: 374, done. remote: Total 374 (delta 0), reused 0 (delta 0), pack-reused 373 Receiving objects: 100% (374/374), 84.63 KiB | 0 bytes/s, done. Resolving deltas: 100% (203/203), done. Checking connectivity... done. |
의존성 패키지를 다음과 같이 설치해준다.
1 |
]# apt-get install libdbd-mysql-perl libmodule-install-perl build-essential |
Perl 을 이용해서 소스를 코드를 컴파일 하고 설치해주면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# cd mha4mysql-node/ # perl Makefile.PL PREFIX=/usr/local include /usr/src/mha4mysql-node/inc/Module/Install.pm include inc/Module/Install/Metadata.pm include inc/Module/Install/Base.pm include inc/Module/Install/Makefile.pm include inc/Module/Install/Scripts.pm include inc/Module/Install/AutoInstall.pm include inc/Module/Install/Include.pm include inc/Module/AutoInstall.pm *** Module::AutoInstall version 1.16 *** Checking for Perl dependencies... [Core Features] - DBI ...loaded. (1.634) - DBD::mysql ...loaded. (4.033) *** Module::AutoInstall configuration finished. include inc/Module/Install/WriteAll.pm include inc/Module/Install/Win32.pm include inc/Module/Install/Can.pm include inc/Module/Install/Fetch.pm Checking if your kit is complete... Warning: the following files are missing in your kit: META.yml Please inform the author. Generating a Unix-style Makefile Writing Makefile for mha4mysql::node Writing MYMETA.yml and MYMETA.json Writing META.yml # make cp lib/MHA/NodeUtil.pm blib/lib/MHA/NodeUtil.pm cp lib/MHA/BinlogHeaderParser.pm blib/lib/MHA/BinlogHeaderParser.pm cp lib/MHA/BinlogPosFinder.pm blib/lib/MHA/BinlogPosFinder.pm cp lib/MHA/BinlogPosFinderElp.pm blib/lib/MHA/BinlogPosFinderElp.pm cp lib/MHA/BinlogPosFindManager.pm blib/lib/MHA/BinlogPosFindManager.pm cp lib/MHA/BinlogManager.pm blib/lib/MHA/BinlogManager.pm cp lib/MHA/SlaveUtil.pm blib/lib/MHA/SlaveUtil.pm cp lib/MHA/NodeConst.pm blib/lib/MHA/NodeConst.pm cp lib/MHA/BinlogPosFinderXid.pm blib/lib/MHA/BinlogPosFinderXid.pm cp bin/filter_mysqlbinlog blib/script/filter_mysqlbinlog "/usr/bin/perl" "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/filter_mysqlbinlog cp bin/save_binary_logs blib/script/save_binary_logs "/usr/bin/perl" "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/save_binary_logs cp bin/purge_relay_logs blib/script/purge_relay_logs "/usr/bin/perl" "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/purge_relay_logs cp bin/apply_diff_relay_logs blib/script/apply_diff_relay_logs "/usr/bin/perl" "-Iinc" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/apply_diff_relay_logs Manifying 4 pod documents # make install Manifying 4 pod documents Installing /usr/local/share/perl/5.22.1/MHA/BinlogHeaderParser.pm Installing /usr/local/share/perl/5.22.1/MHA/BinlogPosFinder.pm Installing /usr/local/share/perl/5.22.1/MHA/SlaveUtil.pm Installing /usr/local/share/perl/5.22.1/MHA/NodeConst.pm Installing /usr/local/share/perl/5.22.1/MHA/BinlogPosFindManager.pm Installing /usr/local/share/perl/5.22.1/MHA/BinlogPosFinderElp.pm Installing /usr/local/share/perl/5.22.1/MHA/BinlogManager.pm Installing /usr/local/share/perl/5.22.1/MHA/BinlogPosFinderXid.pm Installing /usr/local/share/perl/5.22.1/MHA/NodeUtil.pm Installing /usr/local/man/man1/save_binary_logs.1p Installing /usr/local/man/man1/purge_relay_logs.1p Installing /usr/local/man/man1/filter_mysqlbinlog.1p Installing /usr/local/man/man1/apply_diff_relay_logs.1p Installing /usr/local/bin/purge_relay_logs Installing /usr/local/bin/filter_mysqlbinlog Installing /usr/local/bin/apply_diff_relay_logs Installing /usr/local/bin/save_binary_logs Appending installation info to /usr/local/lib/x86_64-linux-gnu/perl/5.22.1/perllocal.pod |
위와 같은 설치를 Master/Slave 모든 MySQL 서버에 설치해 준다.
그리고 각 서버에 M-HA 가 접속하기 위한 MySQL 계정을 생성해 준다.
1 2 |
mysql> grant all on *.* to 'mhauser'@'192.168.96.%' identified by '14321'; mysql> flush privileges; |
MySQL 각 서버에 /var/log/masterha 디렉토리를 만들어 준다.
1 |
]# mkdir -p /var/log/masterha |
Manager 설치
한가지 주의해야 할 것은 Manager 를 설치하기 전에 Node 도 같이 설치해줘야 한다는 것이다. Manager 설치도 소스코드를 다운받아 설치한다. 그 전에 다음과 같이 의존성 패키지를 설치해준다.
1 |
]# apt-get install libdbd-mysql-perl libmodule-install-perl build-essential libconfig-tiny-perl liblog-dispatch-perl libparallel-forkmanager-perl |
소스코드를 다운받는다.
1 2 3 4 5 6 |
# git clone https://github.com/yoshinorim/mha4mysql-manager.git Cloning into 'mha4mysql-manager'... remote: Counting objects: 1383, done. remote: Total 1383 (delta 0), reused 0 (delta 0), pack-reused 1383 Receiving objects: 100% (1383/1383), 371.44 KiB | 305.00 KiB/s, done. Resolving deltas: 100% (830/830), done. |
컴파일 설치해 준다.
1 2 3 |
]# perl Makefile.PL PREFIX=/usr/local ]# make ]# make install |
Manager 에 mha.cnf 파일 작성
Manager 의 mha.cnf 파일을 다음과 같이 작성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[server default] # mysql user and password user=mhauser password=14321 # working directory on the manager manager_workdir=/var/log/masterha # working directory on MySQL servers remote_workdir=/var/log/masterha [server1] hostname=192.168.96.30 [server2] hostname=192.168.96.32 |
SSH 접속 설정
M-HA 는 내부적으로 SSH를 이용해서 M-HA Node 의 릴레이 로그들을 전송하도록 한다. 그런데 자동으로 이게 되게 할라면 M-HA Manager 와 Node 들간 모두 SSH 를 무인증으로 접속이 가능해야 한다.
SSH RSA 를 생성하는데, 패스워드를 입력하지 않는다. 그러면 무인증 접속이 가능해진다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Ac6WpLXcWE58hFcxb3RPiALuejV2XZzMhn5oRb4qbxk root@192.168.96.30 The key's randomart image is: +---[RSA 2048]----+ | +.++..+o.oo| | B @o + .+B+o| | . O =o . .oB+| | . . . .o+..| | S + .+.o | | . o o.Eo | | . . . .o | | . oo | | .. | +----[SHA256]-----+ |
각각에 생성한 키들은 ~/.ssh/authorized_keys 파일에 추가해준다. 중요한 것은 각각 서버들은 서로서로 모두 무인증 SSH 접속이 가능해야 한다는 것이다.
SSH 무인증 테스트를 다음과 같이 할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 |
masterha_check_ssh --conf=/usr/local/etc/mha.cnf Sun Jul 17 21:01:07 2016 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Sun Jul 17 21:01:07 2016 - [info] Reading application default configuration from /usr/local/etc/mha.cnf.. Sun Jul 17 21:01:07 2016 - [info] Reading server configuration from /usr/local/etc/mha.cnf.. Sun Jul 17 21:01:08 2016 - [info] Starting SSH connection tests.. Sun Jul 17 21:01:09 2016 - [debug] Sun Jul 17 21:01:08 2016 - [debug] Connecting via SSH from root@192.168.96.30(192.168.96.30:22) to root@192.168.96.32(192.168.96.32:22).. Sun Jul 17 21:01:08 2016 - [debug] ok. Sun Jul 17 21:01:09 2016 - [debug] Sun Jul 17 21:01:08 2016 - [debug] Connecting via SSH from root@192.168.96.32(192.168.96.32:22) to root@192.168.96.30(192.168.96.30:22).. Sun Jul 17 21:01:09 2016 - [debug] ok. Sun Jul 17 21:01:09 2016 - [info] All SSH connection tests passed successfully. |
Replication 체크
다음과 같에 Replication 체크를 해본다.
1 2 3 4 5 6 7 8 9 10 11 |
# masterha_check_repl --conf=/usr/local/etc/mha.cnf Sun Jul 17 21:26:00 2016 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Sun Jul 17 21:26:00 2016 - [info] Reading application default configuration from /usr/local/etc/mha.cnf.. Sun Jul 17 21:26:00 2016 - [info] Reading server configuration from /usr/local/etc/mha.cnf.. Sun Jul 17 21:26:00 2016 - [info] MHA::MasterMonitor version 0.57. Sun Jul 17 21:26:01 2016 - [error][/usr/local/share/perl5/MHA/ServerManager.pm, ln188] There is no alive server. We can't do failover Sun Jul 17 21:26:01 2016 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln427] Error happened on checking configurations. at /usr/local/share/perl5/MHA/MasterMonitor.pm line 329. Sun Jul 17 21:26:01 2016 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln525] Error happened on monitoring servers. Sun Jul 17 21:26:01 2016 - [info] Got exit code 1 (Not master dead). MySQL Replication Health is NOT OK! |
여기서 버그가 있다. /usr/local/share/perl5/MHA/DBHelper.pm 파일 198 라인에 $host 변수에 문제가 있다.
1 2 3 4 |
- 155 $self->{dsn} = "DBI:mysql:;host=[$host];port=$port;mysql_connect_timeout=4"; + 155 $self->{dsn} = "DBI:mysql:;host=$host;port=$port;mysql_connect_timeout=4"; - 198 $self->{dsn} = "DBI:mysql:;host=[$host];port=$port;mysql_connect_timeout=4"; + 198 $self->{dsn} = "DBI:mysql:;host=$host;port=$port;mysql_connect_timeout=4"; |
위와같이 수정한 후에 다시 한번 해보면 오류를 해결할 수 있다.
이러한 문제는 호스트 이름에 ‘[‘, ‘]’ 를 붙임으로 나타는 현상이다. 이러한 문제는 다음의 파일에도 있다.
1 2 |
가 있는 오류"]HealthCheck.pm:98: "DBI:mysql:;host=[$self->{ip}];" NodeUtil.pm:85: my $ssh_user_host = $ssh_user . '@[' . $ssh_host . ']'; |
Manager 실행 시키기.
반드시 위에 버그를 제거한 후에 다음과 같이 실행을 한다.
1 2 3 4 |
# masterha_manager --conf=/usr/local/etc/mha.cnf Mon Jul 18 01:30:43 2016 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Mon Jul 18 01:30:43 2016 - [info] Reading application default configuration from /usr/local/etc/mha.cnf.. Mon Jul 18 01:30:43 2016 - [info] Reading server configuration from /usr/local/etc/mha.cnf.. |
위와같이 실행을 한 후 로그를 보면 다음과 같이 나와야 정상이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Mon Jul 18 01:30:49 2016 - [info] 192.168.96.32(192.168.96.32:3306) (current master) +--192.168.96.30(192.168.96.30:3306) Mon Jul 18 01:30:49 2016 - [warning] master_ip_failover_script is not defined. Mon Jul 18 01:30:49 2016 - [warning] shutdown_script is not defined. Mon Jul 18 01:30:49 2016 - [debug] Disconnected from 192.168.96.30(192.168.96.30:3306) Mon Jul 18 01:30:49 2016 - [debug] Disconnected from 192.168.96.32(192.168.96.32:3306) Mon Jul 18 01:30:49 2016 - [debug] SSH check command: save_binary_logs --command=test --start_pos=4 --binlog_dir=/var/log/mysql --output_file=/var/log/masterha/save_binary_logs_test --manager_version=0.57 --binlog_prefix=mysql-bin --debug Mon Jul 18 01:30:49 2016 - [info] Set master ping interval 3 seconds. Mon Jul 18 01:30:49 2016 - [warning] secondary_check_script is not defined. It is highly recommended setting it to check master reachability from two or more routes. Mon Jul 18 01:30:49 2016 - [info] Starting ping health check on 192.168.96.32(192.168.96.32:3306).. Mon Jul 18 01:30:49 2016 - [debug] Connected on master. Mon Jul 18 01:30:49 2016 - [debug] Set short wait_timeout on master: 6 seconds Mon Jul 18 01:30:49 2016 - [debug] Trying to get advisory lock.. Mon Jul 18 01:30:49 2016 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn't respond.. |
내용을 보면 192.168.96.32 서버가 현재 Master 고 30번 서버가 Slave 로 인식하고 있다.
FailOver 테스트.
간단한 FailOver 테스트를 해보자. 시나리오는 간단하다. 현재 Master 서버를 정지 시켜 보는 것이다. 그러면 다음과 같이 Manager 로그가 올라온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Mon Jul 18 01:27:19 2016 - [debug] Disconnected from 192.168.96.32(192.168.96.32:3306) Mon Jul 18 01:27:19 2016 - [info] ----- Failover Report ----- mha: MySQL Master failover 192.168.96.30(192.168.96.30:3306) to 192.168.96.32(192.168.96.32:3306) succeeded Master 192.168.96.30(192.168.96.30:3306) is down! Check MHA Manager logs at localhost.localdomain:/var/log/masterha/mha.log for details. Started automated(non-interactive) failover. The latest slave 192.168.96.32(192.168.96.32:3306) has all relay logs for recovery. Selected 192.168.96.32(192.168.96.32:3306) as a new master. 192.168.96.32(192.168.96.32:3306): OK: Applying all logs succeeded. Generating relay diff files from the latest slave succeeded. 192.168.96.32(192.168.96.32:3306): Resetting slave info succeeded. Master failover to 192.168.96.32(192.168.96.32:3306) completed successfully. |
위와같이 로그가 온다면 FailOver 가 정상적으로 된 것이다. 또, 로그 디렉토리 /var/log/masterha 디렉토리에 mha.failover.complete 파일이 생성되어 있을 것이다. 또한, manager 가 죽어 있다. 그러니까 FailOver 가 발생되면 다음과 같이 실행이 된다.
- Slave 를 Master 로 승격시킨다.
- Manager 에 지정한 로그 디렉토리에 mha.failover.complete 파일을 생성한다.
- Manager 가 죽는다.
FailOver 가 발생할때마다 Manager 는 정해진 동작을 수행하고 자동으로 죽게 된다.
승격된 Master 서버에서는 다음을 체크해봐야 한다.
- show slave status 명령을 입력했을때 아무것도 나오지 말아야 한다.
- show variables like ‘%read%’ 명령어를 입력했을때에 read_only 값이 OFF 여야 한다.
위 두가지가 정상적으로 나온다면 M-HA 가 정상적으로 동작한 것이다.