實驗環境:
server1 :172.25.254.1 是server2和server3的master結點
server2 :172.25.254.2
server3 :172.25.254.3
server4 :172.25.254.4 高可用結點
簡介
在企業的日常生產運營中,爲了保證日常的業務穩定,通常會注意以下幾個部分:
- 如果數據庫發生了宕機或者意外中斷等故障,能儘快恢復數據庫的可用性,儘可能的減少停機時間,保證業務不會因爲數據庫的故障而中斷。
- 用作備份、只讀副本等功能的非主節點的數據應該和主節點的數據實時或者最終保持一致。
- 當業務發生數據庫切換時,切換前後的數據庫內容應當一致,不會因爲數據缺失或者數據不一致而影響業務。
我們就需要對數據庫進行集羣,並做高可用的相關配置。
MHA 分爲兩種結點: manager 和 node 結點。
manager可以單獨部署到一臺服務器上,也可以部署到slave結點上,它可以管理多個master和slave結點,
目前大多的MHA部署會遵從 一主多從的架構,集羣中最少需要有三臺mysql服務器。
server1
server2
server3
工作原理:
在主節點的切換過程中,MHA會最大程度上去緩存二進制日誌,識別擁有最新日誌的slave結點,讓其充當新的master結點,讓其它的slave結點去同步新master結點的 relaylog 和 binlog ,更新差異,保證數據不丟失。同時MHA 可以和 半同步複製共同使用,以最大化的降低數據丟失,保持數據的一致性。
配置
集羣與高可用的配置需要兩個重要的內容,就是主從複製和結點之間的免密登陸(因爲i通過ssh連接)。
我們先配置
主從複製:
#server1:
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog
##下面這三行寫在配置文件中就不用每次在mysql中進行設置了,必須先在mysql中安裝這兩個插件纔可以,不然會報錯
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_slave_enabled = 1
rpl_semi_sync_master_timeout = 1000000000000000 #超時時間可以不用加,會影響實驗。
#server2:
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog
#server3:
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog
重啓mysqld。
在server1和2中配置半同步
server1:
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
安裝半同步插件
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
由於也可能成爲slave結點,所以也安裝slave插件
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
設置使用半同步複製
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000000000000000;
設置超時時間。
server2中;
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> stop slave io_thread;
mysql> start slave io_thread;
重啓io線程
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
此時我們在server1中插入數據,
在server2中查看
說明半同步複製操作已經配置好。
在進行綿密登陸的配置
server3 中也配置爲slave結點,因爲集羣中至少兩個slave結點:
mysql> change master to master_host='172.25.254.1',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
mysql> start slave;
#################################
如果在開啓slave時IO線程沒有啓動(使用show slave status\G查看),則是因爲前面server1已經產生數據的緣故,這裏我教大家怎樣解決:
mysql> show global variables like '%gtid%'; 查看數據。
server1:
命令行中,手動備份數據給server3。
mysqldump --all-databases --single-transaction --triggers --routines --events --host=127.0.0.1 --port=3306 --user=root --password=Cc990718-+ > cay.sql 這就是數據庫中全部的數據了。
scp cay.sql server3: 複製給server3.
server3:
mysql> stop slave;
Query OK, 0 rows affected (0.08 sec)
mysql> reset master; 清除master信息。
Query OK, 0 rows affected (0.37 sec)
命令行:
[root@server3 ~]# mysql -p < cay.sql
Enter password: 輸入密碼就ok了。
在重新change master
mysql> change master to master_host='172.25.254.1',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
mysql> start slave;
就ok了 此時IO線程開啓。
################################
在server4上進行高可用配置:
這裏server4既做manager結點,又做node結點。
安裝MHA的rpm包如下:
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
mha4mysql-node-0.58-0.el7.centos.noarch.rpm
perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm
perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm
perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
跟server1 2 3做免密登陸
我們用到了4臺主機,我們要保證每一臺主機都可以和其它主機進行免密登陸。
#在server4中
ssh-keygen
ssh-copy-id server1
ssh-copy-id server2
ssh-copy-id server3
scp -r ~/.ssh server1: 將目錄複製給其它三個主機,它們就不用去做免密了
scp -r ~/.ssh server2:
scp -r ~/.ssh server3:
在server1 2 3 上安裝node軟件包
yum install -y mha4mysql-node-0.58-0.el7.centos.noarch.rpm
然後配置server4上的配置文件:
mkdir /etc/masterha 建立MHA工作目錄
vim /etc/masterha/masterha.cnf 編寫配置文件
[server default]
manager_workdir=/etc/masterha
manager_log=/var/log/masterha.log
master_binlog_dir=/etc/masterha
#master_ip_failover_script= /usr/local/bin/master_ip_failover
#master_ip_online_change_script= /usr/local/bin/master_ip_online_change
password=Cc990718-+
user=root
ping_interval=1
remote_workdir=/tmp
repl_password=Cc990718-+
repl_user=repl
#report_script=/usr/local/send_report
#secondary_check_script= /usr/local/bin/master_secondary_check -s server03 -s server02
#shutdown_script=""
ssh_user=root
[server1]
hostname=172.25.254.1
port=3306
[server2]
hostname=172.25.254.2
port=3306
candidate_master=1 可以成爲新的master
check_repl_delay=0
[server3]
hostname=172.25.254.3
port=3306
no_master=1 表示不可以成爲新master
然後我們就可以去檢查我們的主從複製和免密登陸了:
masterha_check_ssh --conf=/etc/masterha/masterha.cnf
masterha_check_repl --conf=/etc/masterha/masterha.cnf
但是在測試複製時就報錯了,因爲我們最開始安裝mysql的時候設置了禁止root用戶遠程登陸,所以這裏我們要在server1 2 3中重新授權root用戶。
mysql> grant all on *.* to root@'%' identified by 'Cc990718-+';
Query OK, 0 rows affected, 1 warning (0.28 sec)
mysql> flush privileges; 刷新授權表
Query OK, 0 rows affected (0.28 sec)
再次測試:
然後我們就可以測試在server4中進行切換了。
測試切換
手動切換master結點
我們先測試手動切換 master 結點:
要注意每次切換都會生成/etc /masterha/masterha.failover.error|complete 文件,下次切換前必須刪除這個文件纔可以進行新的切換,
目前我們的master結點是server1,我們手動切換是需要掛掉server1的mysql服務,然後server4上執行:
server1:
systemctl stop mysqld
server4命令行中執行:
masterha_master_switch \
--master_state=dead \ 狀態爲掛掉的
--conf=/etc/masterha/masterha.cnf \ 配置文件
--dead_master_host=172.25.254.1 \ 掛掉的master地址
--dead_master_ip=172.25.254.1 \
--dead_master_port=3306 \
--new_master_host=172.25.254.2 \ 新的master地址
--new_master_port=3306
可以看出重置了master結點,切換到了server2。
此時server4的目錄中就出現了 .complete .這個文件,這就是切換的數據,代表切換成功,下次手動切換前需要刪除這個文件。
而且在server2中已經沒有slave的信息,而且server3 的master已經切換:
剛纔的是離線切換,現在我們嘗試在線切換:
先開啓server1的mysql,在把它加回到集羣中去:
systemctl start mysqld.service
#設置server1的主節點爲server2:
mysql> change master to master_host='172.25.254.2',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.51 sec)
mysql> start slave;
mysql> show slave status\G
命令行中執行:
masterha_master_switch --conf=/etc/masterha/masterha.cnf --master_state=alive --new_master_host=172.25.254.1 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
就切換成功了。
server1:
server2:
server3:
這就是我們手動切換的兩種方式。
自動切換
刪除 /etc/masterha/masterha.failover.complete 剛剛轉換完成產生的文件。
自動切換的工具爲: masterha_manager ,它會檢測數據庫的master結點的狀態,如果出現故障,會自動進行切換。
它只能執行一次,因爲每次執行都回生成我們上面刪除的文件,它自帶守護進程,不能kill -9 殺掉。
我們啓動:
在server4中:
nohup masterha_manager --conf=/etc/masterha/masterha.cnf &> /dev/null &
nohup 靜默輸出,& 後臺運行。
測試:
這時我們掛掉當前的master主機 server1 ,
systemctl stop mysqld.service
這時我們發現server4 中的manager進程消失, 說明它只執行一次
然後我們在server2 和server3 中查看:
server2:
server3:
就切換成功了。
我們可以查看日誌看看發生了神什麼動作:
還是執行了changermaster的動作,和我們手動切換執行的命令相同,只不過自動執行。
並且這時manager進程自動結束了,這就是manager的自動切換.
VIP故障切換
在實際情況下,客戶的訪問都有一個固定的ip ,這個ip是對外的,我們不能告訴用戶訪問的地址變了,所以應做一個固定的接口,不論後端的數據庫怎樣改變,都讓客戶使用這個IP進行訪問,所以我們應該設置VIP進行故障切換。
- 我們先將上面關閉的server1打開,在將其加入到集羣中:
mysql> change master to master_host='172.25.254.2',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
mysql> start slave;
VIP切換腳本
master_ip_failover:
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
my (
$command, $ssh_user, $orig_master_host, $orig_master_ip,
$orig_master_port, $new_master_host, $new_master_ip, $new_master_port
);
my $vip = '172.25.0.100/24';
my $ssh_start_vip = "/sbin/ip addr add $vip dev ens3";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev ens3";
GetOptions(
'command=s' => \$command,
'ssh_user=s' => \$ssh_user,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
);
exit &main();
sub main {
print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
exit 0;
}
else {
&usage();
exit 1;
}
}
sub start_vip() {
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
sub stop_vip() {
return 0 unless ($ssh_user);
`ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_failover --command=start|stop|stopssh|status --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}
master_ip_online_change:
#!/usr/bin/env perl
use strict;
use warnings FATAL =>'all';
use Getopt::Long;
my $vip = '172.25.0.100/24'; # Virtual IP
my $ssh_start_vip = "/sbin/ip addr add $vip dev ens3";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev ens3";
my $exit_code = 0;
my (
$command, $orig_master_is_new_slave, $orig_master_host,
$orig_master_ip, $orig_master_port, $orig_master_user,
$orig_master_password, $orig_master_ssh_user, $new_master_host,
$new_master_ip, $new_master_port, $new_master_user,
$new_master_password, $new_master_ssh_user,
);
GetOptions(
'command=s' => \$command,
'orig_master_is_new_slave' => \$orig_master_is_new_slave,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'orig_master_user=s' => \$orig_master_user,
'orig_master_password=s' => \$orig_master_password,
'orig_master_ssh_user=s' => \$orig_master_ssh_user,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
'new_master_user=s' => \$new_master_user,
'new_master_password=s' => \$new_master_password,
'new_master_ssh_user=s' => \$new_master_ssh_user,
);
exit &main();
sub main {
#print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
# $orig_master_host, $orig_master_ip, $orig_master_port are passed.
# If you manage master ip address at global catalog database,
# invalidate orig_master_ip here.
my $exit_code = 1;
eval {
print "\n\n\n***************************************************************\n";
print "Disabling the VIP - $vip on old master: $orig_master_host\n";
print "***************************************************************\n\n\n\n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {
# all arguments are passed.
# If you manage master ip address at global catalog database,
# activate new_master_ip here.
# You can also grant write access (create user, set read_only=0, etc) here.
my $exit_code = 10;
eval {
print "\n\n\n***************************************************************\n";
print "Enabling the VIP - $vip on new master: $new_master_host \n";
print "***************************************************************\n\n\n\n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
`ssh $orig_master_ssh_user\@$orig_master_host \" $ssh_start_vip \"`;
exit 0;
}
else {
&usage();
exit 1;
}
}
# A simple system call that enable the VIP on the new master
sub start_vip() {
`ssh $new_master_ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
`ssh $orig_master_ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_failover --command=start|stop|stopssh|status --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}
把他們放到配置文件中寫的位置,並給與執行權限:
[root@server4 ~]# cp master_ip_* /usr/local/bin/
[root@server4 ~]# chmod +x /usr/local/bin/master_ip_*
[root@server4 ~]# ll /usr/local/bin/master_ip_*
-rwxr-xr-x 1 root root 2156 May 15 10:07 /usr/local/bin/master_ip_failover
-rwxr-xr-x 1 root root 3829 May 15 10:07 /usr/local/bin/master_ip_online_change
測試(半自動切換)
當前master結點爲server2。server1 和 server3 是它的slave結點
先給server 添加172.25.254.100/24 的vip
測試我們的vip是否可以遷移到新的master結點中去,
在 serevr4中在先切換:
masterha_master_switch --conf=/etc/masterha/masterha.cnf --master_state=alive --new_master_host=172.25.254.1 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
執行成功:
並且vip已經添加到了server1上:
且server2的ip消失:
此時的主結點已經切換爲塞server1:
模擬故障全自動切換
我們還需要開啓 masterha_manager 進程
nohup masterha_manager --conf=/etc/masterha/masterha.cnf &> /dev/null &
我們掛掉server1的msyqld服務
[root@server1 ~]# systemctl stop mysqld
此時;
vip 又回到了server2上,而且master結點也已經切換。
server3:
這樣對於用戶來說只需要一直訪問100 這個vip就可以持續訪問了。