MySQL高可用架構-MHA(下)

關注我「

程式猿

集錦」,獲取更多分享。

VIP漂移的三種方式

透過MHA自定義master_ip_failover維護

透過keepalive來維護

安裝keepalive

配置keepalive

啟動keepalive

透過keepalive和master_ip_failover結合來使用

驗證VIP漂移

驗證VIP是否可用

驗證VIP是否可以漂移

如何恢復失敗節點

讀服務怎麼解決

思考

總結

前置閱讀:

MySQL高可用架構-MHA(上)

VIP漂移的三種方式

MySQL高可用叢集已經搭建好,但是當master1節點宕機之後,從節點提升為主節點後,應用連線到MySQL服務的主節點地址就變成了某一個slave從節點,那如何保證應用可用是一個固定的IP來連線MySQL資料庫呢?這就需要使用到VIP虛擬IP地址。

這個VIP地址就是應用中配置的IP地址,它可用在活著的主節點上面繫結,如果主宕機了,這個VIP可用漂移到新的主上。這就是VIP要完成功能。那麼如何讓這個VIP自動漂移到活著的主節點呢?目前可有三種方式:

透過指令碼自己去維護VIP繫結的節點,這要在MHA的擴充套件指令碼中去做,使用perl指令碼去做。

或者使用keepalive外掛自己去維護,這種方式是在MHA做故障遷移的時候,順便把發生故障的主機上面的keepalive服務給停止掉,這樣VIP就可以從故障的節點遷移到指定的節點了。

就是上面兩種1和2的方式結合的一種方式。在Perl指令碼中不去維護VIP的建立繫結等操作,而是交給keepalive服務去做,只要在perl指令碼中,決定什麼時候在發生主節點MySQL服務不能用的時候,把該主節點上面的keepalive服務給停止,就可以把對應VIP漂移到其他主機上去了。

透過MHA自定義master_ip_failover維護

這個指令碼就是

master_ip_failover

自定義指令碼。因為我不會寫Perl,下面是網上扒來的,供參考。

大體的邏輯就是:我們在指令碼中定義一個VIP地址,然後定義一個在遠端伺服器建立VIP,還有一個取消VIP的方法。然後MAH的manager程序在進行主從切換的時候,會呼叫這個自定義的

master_ip_failover

指令碼,並給這個指令碼傳入對應的引數和命令,這個指令碼就根據manager程序轉入的命令走對應的邏輯,來呼叫自定義的建立和取消VIP的方法,從而來實現VIP從原主節點到新節點的漂移。

在使用這個指令碼之前,需要原先正常的master節點上,先使用下面的命令來建立一個VIP,然後才能將這個VIP漂移到新的主節點上。

root@master1:/var/log/mha/app1# ifconfig eth0:1 172。21。0。10 uproot@master1:/var/log/mha/app1# ifconfigeth0: flags=4163 mtu 1500 inet 172。21。0。11 netmask 255。255。255。0 broadcast 172。21。0。255 ether 02:42:ac:15:00:0b txqueuelen 0 (Ethernet) RX packets 1251 bytes 162986 (159。1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3342 bytes 342702 (334。6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0:1: flags=4163 mtu 1500 inet 172。21。0。10 netmask 255。255。0。0 broadcast 172。21。255。255 ether 02:42:ac:15:00:0b txqueuelen 0 (Ethernet)lo: flags=73 mtu 65536 inet 127。0。0。1 netmask 255。0。0。0 loop txqueuelen 1000 (Local Loopback) RX packets 154 bytes 21841 (21。3 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 154 bytes 21841 (21。3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

master_ip_failover

的示例指令碼如下:

root@manager:/var/log/mha/app1# cat /etc/mha/app1/master_ip_failover#!/usr/bin/env perluse strict;use warnings FATAL => ‘all’;use Getopt::Long;use MHA::DBHelper;my ( $command, $ssh_user, $orig_master_host, $orig_master_ip, $orig_master_port, $new_master_host, $new_master_ip, $new_master_port, $new_master_user, $new_master_password);# MySQL cluster VIP for write requestmy $vip = ‘172。21。0。10’;my $key = ‘1’;my $ssh_start_vip = “/sbin/ifconfig eth0:$key $vip”;my $ssh_stop_vip = “/sbin/ifconfig eth0:$key down”;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, ‘new_master_user=s’ => \$new_master_user, ‘new_master_password=s’ => \$new_master_password,);exit &main();sub main { 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 “Disabling the VIP on old master: $orig_master_host \n”; &stop_vip(); # updating global catalog, etc $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 { my $new_master_handler = new MHA::DBHelper(); # args: hostname, port, user, password, raise_error_or_not $new_master_handler->connect( $new_master_ip, $new_master_port, $new_master_user, $new_master_password, 1 ); ## Set read_only=0 on the new master $new_master_handler->disable_log_bin_local(); print “Set read_only=0 on the new master。\n”; $new_master_handler->disable_read_only(); ## Creating an app user on the new master #print “Creating app user on the new master。。\n”; #FIXME_xxx_create_user( $new_master_handler->{dbh} ); $new_master_handler->enable_log_bin_local(); $new_master_handler->disconnect(); ## Update master ip on the catalog database, etc #FIXME_xxx; print “Enabling the VIP - $vip on the new master - $new_master_host \n”; &start_vip(); $exit_code = 0; }; if ($@) { warn $@; # If you want to continue failover, exit 10。 exit $exit_code; } exit $exit_code; } elsif ( $command eq “status” ) { print “Checking the Status of the script。。 OK \n”; # do nothing exit 0; } else { &usage(); exit 1; }}# A simple system call that enable the VIP on the new mastersub start_vip() { `ssh $ssh_user\@$new_master_host \“ $ssh_start_vip \”`;}# A simple system call that disable the VIP on the old_mastersub 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”;}root@manager:/var/log/mha/app1#

透過keepalive來維護

我們接下來安裝keepalive插接,目的是為了在MySQL資料叢集中配置一個虛擬的VIP,然後讓這個VIP可以根據主節點的狀態來自動的在可用的主節點進行漂移。此時我們不需要自己在master節點上建立VIP,當我們在master節點和備選master節點上,配置好keepalive的配置檔案

/etc/keepalived/keepalived。conf

之後,啟動keepalive服務後會自動在對應的節點建立VIP地址。

安裝keepalive

我們在master1節點和slave1節點上面都安裝keepalive外掛,master1作為預設的主節點,slave1作為備用主節點(如果是有多個備選主節點,那麼所有的備選主節點都要安裝並啟動keepalive服務),使用apt-get命令來安裝keepalive軟體,如果是centos使用yum來安裝。

root@master1:/# apt-get install keepalived -yroot@slave1:/# apt-get install keepalived -y

keepalive安裝完成之後,會在

/etc/init。d

目錄裡下面生成一個啟動指令碼檔案

keepalived

,如下所示,我們只是master1節點上示例展示。檢視這個指令碼檔案,可用看到它的配置檔案是在

/etc/keepalived/keepalived。conf

這裡。所以,後續配置keepalive的時候,我們需要在這個配置檔案中進行配置。

配置keepalive

在master1節點和slave1節點上面分別建立配置檔案

/etc/keepalived/keepalived。conf

,然後分別編輯配置他們。

在maste1上的keepalived。conf`配置檔案如下:

root@master1:/etc/keepalived# cat /etc/keepalived/keepalived。conf! Configuration File for keepalivedglobal_defs { router_id 172。21。0。11}vrrp_script chk_mysql_status { script “/etc/keepalived/check_mysql_port。sh 3306” interval 2 weight -20}vrrp_instance VI_1 { state BACKUP interface eth0 virtual_router_id 251 priority 100 advert_int 1 mcast_src_ip 172。21。0。11 nopreempt authentication { auth_type PASS auth_pass 11111111 } track_script { chk_mysql_status } virtual_ipaddress { 172。21。0。10 }}root@master1:/etc/keepalived#

在slave1上的keepalived。conf`配置檔案如下:

root@slave1:/etc/keepalived# cat keepalived。conf! Configuration File for keepalivedglobal_defs { router_id 172。21。0。12}vrrp_script chk_mysql_status { script “/etc/keepalived/check_mysql_port。sh 3306” interval 2 weight -20}vrrp_instance VI_1 { state BACKUP interface eth0 virtual_router_id 251 priority 90 advert_int 1 mcast_src_ip 172。21。0。12 nopreempt authentication { auth_type PASS auth_pass 11111111 } track_script { chk_mysql_status } virtual_ipaddress { 172。21。0。10 }}root@slave1:/etc/keepalived#

現在,針對上面的配置檔案的引數,使用master1節點上面的配置檔案,做如下簡單介紹說明,以便於立即keepalive的配置檔案怎麼配置。

root@master1:/etc/keepalived# cat /etc/keepalived/keepalived。conf! Configuration File for keepalivedglobal_defs { router_id 172。21。0。11 # keepalive所在主機的IP地址,一個名字而已,這裡用IP地址代替了。}vrrp_script chk_mysql_status { # 檢查主機3306埠的shell指令碼,發生VIP漂移的觸發原因就是某一個主機的某個埠不可用。這個指令碼需要自定義去寫一下,比較簡單,後面會給出示例。 script “/etc/keepalived/check_mysql_port。sh 3306” interval 2 # 埠檢查的頻次,每2秒中一次 weight -20}vrrp_instance VI_1 { state BACKUP # 當前keepalive節點的角色 interface eth0 # 當前主機的物理網絡卡名稱 virtual_router_id 251 priority 100 advert_int 1 mcast_src_ip 172。21。0。11 # 當前主機的物理IP地址 nopreempt # keepalive多個節點互相通訊的認證方式,用於標識哪些keepalive節點是同一個keepalive叢集。 authentication { auth_type PASS auth_pass 11111111 } track_script { chk_mysql_status # 檢查服務埠的指令碼,VIP發生漂移的時候就是從這裡觸發的。 } virtual_ipaddress { 172。21。0。10 # 這就是我們設定的VIP的地址 }}root@master1:/etc/keepalived#

注意:上面兩臺伺服器的keepalived都設定為了BACKUP模式,目的是為了儘量減少VIP漂移的次數。具體原因如下:

在keepalived中2種模式,分別是master->backup模式和backup->backup模式。這兩種模式有很大區別。

在master->backup模式下,一旦主庫宕機,虛擬ip會自動漂移到從庫,當主庫修復後,keepalived啟動後,還會把虛擬ip搶佔過來,即使設定了非搶佔模式(nopreempt)搶佔ip的動作也會發生。

在backup->backup模式下,當主庫宕機後虛擬ip會自動漂移到從庫上,當原主庫恢復和keepalived服務啟動後,並不會搶佔新主的虛擬ip,即使是優先順序高於從庫的優先級別,也不會發生搶佔。

所以,為了減少ip漂移次數,通常是把修復好的主庫當做新的備庫。同時把keepalive中的角色都設定為backup角色。

在配置好keepalive的配置檔案後,配置檔案中有引用到一個監控某個服務是否可以的指令碼,這裡我們要監控的是MySQL資料庫服務是否可用,所以我們監控的是MySQL的3306埠是否在提供監聽服務。如果是要監聽其他服務的埠,只要作出對應的埠作出修改即可。下面給出監聽MySQL服務的shell指令碼,這個指令碼在每一個keepalive節點上都是一樣的,在我們的master1和slave1上使用相同的指令碼檔案。

root@master1:/etc/keepalived# cat chk_mysql_status。sh#!/bin/bashCHK_PORT=$1if [ -n “$CHK_PORT” ]; then PORT_PROCESS=`ss -lnt | grep $CHK_PORT | wc -l` if [ $PORT_PROCESS -eq 0 ]; then echo “請注意:埠號$CHK_PORT現在已經不可用,下面將退出keepalive的服務,VIP即將發生漂移。” >> 。/chk_mysql_status。log # 發現當前主機的MySQL埠不能訪問後,把當前主機的keepalive服務也停止,讓這個主機上的VIP漂移到備選主節點上 /etc/init。d/keepalived stop exit 1 fielse echo “待監控的服務埠號不能為空!” >> 。/chk_mysql_status。logfiroot@master1:/etc/keepalived# pwd/etc/keepalivedroot@master1:/etc/keepalived#

需要注意的是指令碼的放置目錄,我們是放在和keepalive配置檔案相同的目錄

/etc/keepalive

目錄下面了。

啟動keepalive

在master1節點上啟動keepalive服務,如下所示:

root@master1:/etc/keepalived# /etc/init。d/keepalived start[ ok ] Starting keepalived: keepalived。# 檢視啟動keepalive後,VIP已經建立成功root@master1:/etc/keepalived# ip addr1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127。0。0。1/8 scope host lo valid_lft forever preferred_lft forever2: tunl0@NONE: mtu 1480 qdisc noop state DOWN group default qlen 1000 link/ipip 0。0。0。0 brd 0。0。0。03: ip6tnl0@NONE: mtu 1452 qdisc noop state DOWN group default qlen 1000 link/tunnel6 :: brd ::59: eth0@if60: mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:15:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172。21。0。11/24 brd 172。21。0。255 scope global eth0 valid_lft forever preferred_lft forever inet 172。21。0。10/32 scope global eth0 # 這裡就是我們配置的VIP,預設是master1上面,當master1節點的3306埠掛掉,或漂移到slave1上。 valid_lft forever preferred_lft foreverroot@master1:/etc/keepalived#

在slave1節點上,也啟動keepalive服務,如下所示:

root@slave1:/etc/keepalived# /etc/init。d/keepalived start[ ok ] Starting keepalived: keepalived。root@slave1:/etc/keepalived## 此時slave1上面是沒有我們配置的VIP的,因為master1目前還是可用狀態。root@slave1:/etc/keepalived# ip addr1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127。0。0。1/8 scope host lo valid_lft forever preferred_lft forever2: tunl0@NONE: mtu 1480 qdisc noop state DOWN group default qlen 1000 link/ipip 0。0。0。0 brd 0。0。0。03: ip6tnl0@NONE: mtu 1452 qdisc noop state DOWN group default qlen 1000 link/tunnel6 :: brd ::61: eth0@if62: mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:15:00:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172。21。0。12/24 brd 172。21。0。255 scope global eth0 valid_lft forever preferred_lft foreverroot@slave1:/etc/keepalived#

透過keepalive和master_ip_failover結合來使用

這種方式就結合前面使用MHA的

master_ip_failover

指令碼和keepalive兩種方式的變形。實現的方式就是:VIP的建立我們交給keepalive來做,但是這個VIP要向完成漂移的動作,發生故障的伺服器上面的keepalive程序必須終止後,VIP才會發生漂移,那麼如何讓這個發生故障的伺服器上面的keepalive終止程序呢?就在我們的

master_ip_failover

指令碼中去終止對應的伺服器上面的keepalive程序服務,這樣這個伺服器上面的VIP就會發生漂移了。

指令碼的實現方式和第一種

master_ip_failover

的指令碼類似,只是此時我們不需要只是用keepalive的時候自定義的那個

chk_mysql_status。sh

指令碼來終止keepalive程序了。在

master_ip_failover

指令碼中去終止對應的keepalive服務程序。指令碼如下:

root@manager:/var/log/mha/app1# cat /etc/mha/app1/master_ip_failover#!/usr/bin/env perl## Note: This is a sample script and is not complete。 Modify the script based on your environment。use strict;use warnings FATAL => ‘all’;use Getopt::Long;use MHA::DBHelper;my ( $command, $ssh_user, $orig_master_host, $orig_master_ip, $orig_master_port, $new_master_host, $new_master_ip, $new_master_port, $new_master_user, $new_master_password);# MySQL叢集中的VIP地址my $vip = ‘172。21。0。10’;# 開啟和關閉VIP的命令,下面是透過停止對應的伺服器上面的keepalive的命令,來完成VIP的漂移的。my $ssh_start_vip = “/etc/init。d/keepalived start”;my $ssh_stop_vip = “/etc/init。d/keepalived stop”;# 也可以使用下面的方式,自己透過ifconfig命令在對應的伺服器上面增加和刪除VIP,這樣在master節點和備選master節點就不用安裝keepalive服務了。#my $key = ‘1’;#my $ssh_start_vip = “/sbin/ifconfig eth0:$key $vip up”;#my $ssh_stop_vip = “/sbin/ifconfig eth0:$key down”;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, ‘new_master_user=s’ => \$new_master_user, ‘new_master_password=s’ => \$new_master_password,);exit &main();sub main { 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 “Disabling the VIP on old master: $orig_master_host \n”; &stop_vip(); # 呼叫停止VIP的方法 # updating global catalog, etc $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 { my $new_master_handler = new MHA::DBHelper(); # args: hostname, port, user, password, raise_error_or_not $new_master_handler->connect( $new_master_ip, $new_master_port, $new_master_user, $new_master_password, 1 ); ## Set read_only=0 on the new master $new_master_handler->disable_log_bin_local(); print “Set read_only=0 on the new master。\n”; $new_master_handler->disable_read_only(); ## Creating an app user on the new master #print “Creating app user on the new master。。\n”; #FIXME_xxx_create_user( $new_master_handler->{dbh} ); # 如果應用使用的MySQL使用者並沒有在salve節點建立,可以在切換的時候,在這裡定義並建立。但是一般情況下,整個叢集中的所有使用者都是相同的,許可權也是相通的,所以一般情況用不上這個操作。 $new_master_handler->enable_log_bin_local(); $new_master_handler->disconnect(); ## Update master ip on the catalog database, etc #FIXME_xxx; print “Enabling the VIP - $vip on the new master - $new_master_host \n”; &start_vip(); # 呼叫開啟VIP的方法 $exit_code = 0; }; if ($@) { warn $@; # If you want to continue failover, exit 10。 exit $exit_code; } exit $exit_code; } elsif ( $command eq “status” ) { print “Checking the Status of the script。。 OK \n”; # do nothing exit 0; } else { &usage(); exit 1; }}# 在遠端伺服器開啟VIP的方法sub start_vip() { `ssh $ssh_user\@$new_master_host \“ $ssh_start_vip \”`;}# 在遠端伺服器停止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”;}root@manager:/var/log/mha/app1#

驗證VIP漂移

在驗證VIP是否可以正常漂移的實驗中,針對上面提出來的3種實現方式,我們選擇第一種方式來驗證一下。其他兩種方式,我也一併做過,但是這裡不再一一貼出實驗過程。

第一種方式:透過

master_ip_failover

指令碼來維護VIP的漂移。在master1節點上建立一個VIP,如下所示,用於對外提供寫的服務。然後我們看下在master1節點宕機的時候,這個上面的VIP是否可以自動漂移到salve1備用主主節點上。

# 在master1節點上建立VIP地址root@master1:/var/log/mha/app1# ifconfig eth0:1 172。21。0。10 up# VIP地址已經在master1節點上建立完成root@master1:/var/log/mha/app1# ifconfigeth0: flags=4163 mtu 1500 inet 172。21。0。11 netmask 255。255。255。0 broadcast 172。21。0。255 ether 02:42:ac:15:00:0b txqueuelen 0 (Ethernet) RX packets 1251 bytes 162986 (159。1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3342 bytes 342702 (334。6 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0:1: flags=4163 mtu 1500 inet 172。21。0。10 netmask 255。255。0。0 broadcast 172。21。255。255 ether 02:42:ac:15:00:0b txqueuelen 0 (Ethernet)lo: flags=73 mtu 65536 inet 127。0。0。1 netmask 255。0。0。0 loop txqueuelen 1000 (Local Loopback) RX packets 154 bytes 21841 (21。3 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 154 bytes 21841 (21。3 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0# 此時的salve1節點上是沒有VIP地址的root@slave1:/# ifconfigeth0: flags=4163 mtu 1500 inet 172。21。0。12 netmask 255。255。255。0 broadcast 172。21。0。255 ether 02:42:ac:15:00:0c txqueuelen 0 (Ethernet) RX packets 9 bytes 726 (726。0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0。0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73 mtu 65536 inet 127。0。0。1 netmask 255。0。0。0 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0。0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0。0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0root@slave1:/#

對於

master_ip_failover

指令碼,參考上面提到VIP漂移第一種實現方式的貼出來的指令碼內容,這不再重複貼出。

驗證VIP是否可用

現在的VIP已經在master1上面建立成功了,下面我們來嘗試使用這個VIP是否可以正常方式到MySQL服務。我們在安裝MHA服務的manager節點上,嘗試透過VIP來訪問MySQL資料庫服務,看是否可以成功。透過如下結果可以看出透過VIP是可以正常方式MySQL服務的。

root@manager:/etc/mha# mysql -uroot -proot -h172。21。0。10mysql: [Warning] Using a password on the command line interface can be insecure。Welcome to the MySQL monitor。 Commands end with ; or \g。Your MySQL connection id is 48Server version: 5。7。31-log MySQL Community Server (GPL)Copyright (c) 2000, 2020, Oracle and/or its affiliates。 All rights reserved。Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates。 Other names may be trademarks of their respectiveowners。Type ‘help;’ or ‘\h’ for help。 Type ‘\c’ to clear the current input statement。mysql> select @@hostname;+————————-+| @@hostname |+————————-+| master1。mysql |+————————-+1 row in set (0。00 sec)mysql>

透過上面的實驗可以看出,在manager節點

172。21。0。100

上是可以透過

172。21。0。10

這個VIP來訪問MySQL資料庫的。

驗證VIP是否可以漂移

manager節點的操作

首先,我們在manager節點上面,啟動MHA的manager服務。啟動方式如下所示,我們選擇後臺啟動。

# 以後程序的方式啟動MHA的manager服務root@manager:/var/log/mha/app1# nohup masterha_manager ——conf=/etc/mha/app1。cnf &[1] 1230root@manager:/var/log/mha/app1# nohup: ignoring input and appending output to ‘nohup。out’

檢視manager服務啟動後,日誌目錄下面的檔案有哪些。日誌檔案的目錄在/etc/mha/app1。cnf配置檔案中定義。

# 啟動後,發現會在/var/log/mha/app1目錄下面生如下3個檔案,一個是nohup的日誌輸出,一個是manager服務的日誌輸出檔案,一個是MySQL資料庫叢集中master節點監控狀態標識檔案。root@manager:/var/log/mha/app1# ls -lstrtotal 164 -rw————- 1 root root 299 Mar 7 09:56 nohup。out8 -rw-r——r—— 1 root root 5784 Mar 7 09:56 app1。log4 -rw-r——r—— 1 root root 33 Mar 7 09:56 app1。master_status。health

用如下的命令動態監控manager服務的日誌輸出是什麼,以方便我們在測試主從切換的時候,動態檢視manager服務的日誌內容。

# 用tail -f的命令動態的觀察manager服務日誌的輸出內容,發現目前已經啟動manager服務,它在等待MySQL服務不可用的時候,才會有動作以及日誌的輸出。root@manager:/var/log/mha/app1# tail -f app1。logSun Mar 7 09:56:55 2021 - [info] Checking master_ip_failover_script status:Sun Mar 7 09:56:55 2021 - [info] /etc/mha/app1/master_ip_failover ——command=status ——ssh_user=root ——orig_master_host=172。21。0。11 ——orig_master_ip=172。21。0。11 ——orig_master_port=3306Checking the Status of the script。。 OKSun Mar 7 09:56:55 2021 - [info] OK。Sun Mar 7 09:56:55 2021 - [warning] shutdown_script is not defined。Sun Mar 7 09:56:55 2021 - [info] Set master ping interval 1 seconds。Sun Mar 7 09:56:55 2021 - [warning] secondary_check_script is not defined。 It is highly recommended setting it to check master reachability from two or more routes。Sun Mar 7 09:56:55 2021 - [info] Starting ping health check on 172。21。0。11(172。21。0。11:3306)。。Sun Mar 7 09:56:55 2021 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn‘t respond。。

master1主節點的操作

下面我們可以嘗試把master1節點MySQL服務停止掉,看下VIP是否會自動漂移到slave1上。在master1節點上執行如下操作來停止MySQL資料庫服務。由於我們的MySQL服務是用docker容器啟動的,當我們停止容器中的MySQL之後,這個容器也就退出了。

root@master1:~# /etc/init。d/mysql stop……。。。。。% ➜ ~

檢視manager節點的日誌

當發生主從切換之後,manager節點上面的manager服務會停止,如下可以檢視manager服務以及停止,需要在我們恢復好MySQL資料失敗的節點之後,再次以後臺程序的方式啟動起來。

同時,我們可以檢視在manager節點manager服務的日誌目錄下面,有如下檔案生成,需要注意的是,其中的

app1。failover。complete

標識檔案,需要刪除之後,才可以再次啟動manager服務。否則我們的manager服務是不能啟動成功的。

root@manager:/var/log/mha/app1# ls -lstrtotal 28 4 -rw————- 1 root root 773 Mar 7 10:06 nohup。out # nohup的輸出日誌檔案24 -rw-r——r—— 1 root root 22206 Mar 7 10:06 app1。log # 主從切換過程的日誌檔案 0 -rw-r——r—— 1 root root 0 Mar 7 10:06 app1。failover。complete # 主從切換完成的標識檔案,再次啟動manager服務之前,需要將這個檔案刪除之後,才可以再次啟動manager服務。root@manager:/var/log/mha/app1# pwd/var/log/mha/app1root@manager:/var/log/mha/app1#

在manager節點上,檢視manager節點的主從切換的日誌輸出如下,由於日誌內容太長,這裡我們只列出最後幾行的日誌內容。

root@manager:/var/log/mha/app1# tail -f app1。log# 。。。省略。。。Master 172。21。0。11(172。21。0。11:3306) is down!Check MHA Manager logs at manager。mysql:/var/log/mha/app1/app1。log for details。Started automated(non-interactive) failover。Invalidated master IP address on 172。21。0。11(172。21。0。11:3306)The latest slave 172。21。0。12(172。21。0。12:3306) has all relay logs for recovery。Selected 172。21。0。12(172。21。0。12:3306) as a new master。172。21。0。12(172。21。0。12:3306): OK: Applying all logs succeeded。172。21。0。12(172。21。0。12:3306): OK: Activated master IP address。172。21。0。22(172。21。0。22:3306): This host has the latest relay log events。Generating relay diff files from the latest slave succeeded。172。21。0。22(172。21。0。22:3306): OK: Applying all logs succeeded。 Slave started, replicating from 172。21。0。12(172。21。0。12:3306)172。21。0。12(172。21。0。12:3306): Resetting slave info succeeded。Master failover to 172。21。0。12(172。21。0。12:3306) completed successfully。

slave1節點的操作

檢視備選主節點slave1上面的是否有VIP的地址,透過如下命令可以檢視VIP已經漂移到了slave1節點上了。這個漂移的動作就是我們在

master_ip_failover

指令碼中,透過

ifconfig

命令來實現的,這個指令碼會在MHA的manager服務在完整主從切換之後自動呼叫的。至於這個指令碼的具體位置和路徑,則是定義在

/etc/mha/app1。cnf

配置檔案中。

root@slave1:/# ifconfigeth0: flags=4163 mtu 1500 inet 172。21。0。12 netmask 255。255。255。0 broadcast 172。21。0。255 ether 02:42:ac:15:00:0c txqueuelen 0 (Ethernet) RX packets 433 bytes 61965 (60。5 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 395 bytes 96296 (94。0 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0eth0:1: flags=4163 mtu 1500 inet 172。21。0。10 netmask 255。255。0。0 broadcast 172。21。255。255 ether 02:42:ac:15:00:0c txqueuelen 0 (Ethernet)lo: flags=73 mtu 65536 inet 127。0。0。1 netmask 255。0。0。0 loop txqueuelen 1000 (Local Loopback) RX packets 59 bytes 8093 (7。9 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 59 bytes 8093 (7。9 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0root@slave1:/#

以上,我們就驗證了透過

master_ip_failover

在定義的指令碼來完成VIP在發生主從切換的時候漂移的過程。至於另外兩種VIP漂移的方式,實驗過程大同小異。大家可以自行式樣。

如何恢復失敗節點

在manger節點上的操作

在manager節點中的

app1。log

中,包含具體恢復失敗節點的命令提示。其實現的原理則是把失敗的主節點,以一個從節點的角色再次加入到叢集當中去。

當前啟動好失敗的主節點之後,如何把這個恢復的節點加入到叢集當中去,可以參考

app1。log

日誌檔案中的輸出內容。

root@manager:/var/log/mha/app1# cat app1。log | grep “CHANGE”Sun Mar 7 10:06:25 2021 - [info] All other slaves should start replication from here。 Statement should be: CHANGE MASTER TO MASTER_HOST=’172。21。0。12‘, MASTER_PORT=3306, MASTER_LOG_FILE=’slave1-mysql-bin。000007‘, MASTER_LOG_POS=154, MASTER_USER=’repl‘, MASTER_PASSWORD=’xxx‘;Sun Mar 7 10:06:27 2021 - [info] Executed CHANGE MASTER。root@manager:/var/log/mha/app1#

在失敗節點上的操作

根據上面在

app1。log

日誌檔案中輸出的內容,改為如下內容,在失敗的節點上,執行改變主從複製鏈路的命令之後,就可以把剛恢復的節點以從節點的角色加入到MHA考可用叢集當中。

/* 在失敗節點執行如下命令,設定新的複製鏈路 */mysql> CHANGE MASTER TO MASTER_HOST=’172。21。0。12‘, MASTER_PORT=3306, MASTER_LOG_FILE=’slave1-mysql-bin。000007‘, MASTER_LOG_POS=154, MASTER_USER=’repl‘, MASTER_PASSWORD=’repl‘;Query OK, 0 rows affected, 2 warnings (0。03 sec)/* 啟動複製鏈路 */mysql> start slave;Query OK, 0 rows affected (0。03 sec)/* 檢視主從複製後的鏈路,發現鏈路已經切換為slave1作為主節點了 */mysql> show slave status\G*************************** 1。 row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172。21。0。12 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: slave1-mysql-bin。000007 Read_Master_Log_Pos: 154 Relay_Log_File: master1-relay-bin。000002 Relay_Log_Pos: 327 Relay_Master_Log_File: slave1-mysql-bin。000007 Slave_IO_Running: Yes Slave_SQL_Running: Yes /* 。。。省略輸出內容。。。 */ Master_Server_Id: 12 Master_UUID: 3ae2324e-7bef-11eb-bc99-0242ac15000c Master_Info_File: mysql。slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 864001 row in set (0。00 sec)mysql>

在manager節點上操作

前面的步驟我們已經恢復了主從複製的鏈路,接下來就是要再次啟動我們的MHA在manager節點上的manager服務。

在啟動之前,需要把manager節點上的主從切換完成的標識檔案刪除掉,如下所示:

root@manager:/var/log/mha/app1# rm /var/log/mha/app1/app1。failover。completeroot@manager:/var/log/mha/app1#

然後再次啟動MAH監控服務,

讀服務怎麼解決

對於一個高可用的MHA叢集,對外提供寫的服務,可以透過VIP來解決應用層連線的問題。那麼對於讀的服務,多個slave節點如何對應用層提供服務?

其實,對於這樣的高可用的叢集,它是一個讀寫分離的高可用叢集。主節點提供寫的服務,從節點提供讀的服務。在應用層連線資料庫的時候,就需要配置兩個IP地址,一個用於寫,一個用於讀。這樣對應用層是有侵入性的,因為應用層要自己確定每一個SQL請求到底是該傳送到寫的IP地址,還是傳送到讀的IP地址上。

為了解決這種對應用層侵入的問題,有現有的資料庫中介軟體來解決這樣問題。MyCat就是這樣一個可以使用讀寫分離的資料庫中介軟體。我們把上面我們提到的VIP地址,和多個slave節點IP地址,分別配置在MyCat中,MyCat會根據接收到的SQL語句自動識別出這是一個寫請求還是一個讀請求,進而把對應的SQL傳送給VIP還是salve的IP。至於到底傳送到哪個salve節點上,MyCat裡有各種可以選擇的配置方式,輪休分發的策略是常用的配置方式。MyCat對外提供一個IP地址,供應用層來連線MyCat。這樣就解決了MHA高可用叢集的讀寫分離。

如果考慮到MyCat的高可用,可以在MySQL的MHA叢集上層部署兩臺MyCat,做高可用的MyCat,然後透過keepalive在兩個MyCat上啟動一個VIP,讓應用透過這個VIP來連線MyCat。最後的網路拓撲圖,如下所示:

MySQL高可用架構-MHA(下)

MHA高可用+讀寫分離

思考

MHA中的manager節點,本身就是一個單節點,該如何避免這個呢?

目前的MHA還不支援兩個MHA的manager共同管理一個MySQL叢集。從技術上講,你可以部署兩個manager去監控同一個MySQL叢集。但是,當發生故障轉移的時候兩個MHA的manager節點都在對MySQL叢集進行故障轉移,如果他們選擇的從節點不是同一個從節點將會發生什麼情況?整個MySQL叢集就有可能產生兩個master節點,發生腦裂的現象。所以我們目前還沒有一個很好的辦法對MHA的manager節點做高可用。

比較推薦的一種做法是透過監控指令碼去對MHA的manager服務是否正在運用進行監控,如果沒有執行就把這個服務再次嘗試啟動。可以把這樣的指令碼配置在Linux伺服器的crontab中。

另外一種做法就是使用類似於的supervisor這樣的後端守護程序去監控manager節點的manager服務是否正常執行,如果發現沒有在執行,supervisor會嘗試把manager程序給啟動起來。此時,我們不用擔心在MySQL叢集發生故障轉移主從切換之後MHA的manager節點在自動停止執行之後,再次被supervisor這樣的守護程序給再次拉取起來。因為此時的manager程序是啟動不了的,因為manager程序在啟動的時候,會檢查是否有上一次MySQL宕機的時候生成的標識檔案,如果沒有這樣的檔案才會啟動成功,有這樣的檔案,是不會啟動成功的。

MySQL中叢集雖然高可用了,那VIP漂移的問題怎麼解決的呢?

我們可以使用兩種方式來解決這個VIP漂移的問題。

可以使用基於MHA的Perl指令碼去做VIP的繫結和漂移工作。

可以藉助keepalive這樣的元件來完成VIP的繫結和漂移的工作,這裡需要注意一點,keepalive管理的VIP發生漂移的觸發動作是當前繫結VIP的主機上面的keepalive程序關閉退出之後,當前主機繫結的VIP才會發生漂移的動作。所以當我們的master節點如果沒有發生宕機,只是MySQL程序出現問題不能訪問的時候,我們是需要把當前的主機上面執行的keepalive程序給終止掉,才會發生VIP的漂移。所以,此時我們需要在MHA的master_ip_failover指令碼整增加殺死已經死掉的主節點的keepalive程序,或在keepalive的配置檔案中配置一個監控MySQL服務的指令碼,根據監控的結果來決定是否要殺死當前主節點的keepalive程序。

MHA中如何保證候選的master是從資料最接近宕機的master中選擇出來的呢?可以設定幾個candidate_master節點?

可以設定多個候選主節點,但是感覺意義不大。我們設定多個候選節點的目的是擔心只要一個候選節點,萬一這個候選節點掛掉了,就不能完成主從切換了。因為候選的主節點以及處於宕機的狀態,所以這個節點不能用於提供主節點的服務。

但是實驗證明,即便使我們設定兩個以上的候選節點,如果其中任意一個候選節點出現宕機的情況,此時MHA是不能完成主從切換的。會出現如下的錯誤資訊,在MHA嘗試自動完成主從切換的時候。言外之意,如果MySQL叢集中,已經存在一個候選節點宕機的情況,是不能自動完成主從切換的,需要保證叢集中所有節點都是可用狀態。

Sun Mar 7 19:46:16 2021 - [info] Replicating from 172。21。0。11(172。21。0。11:3306)Sun Mar 7 19:46:16 2021 - [info] Primary candidate for the new Master (candidate_master is set)Sun Mar 7 19:46:16 2021 - [error][/usr/share/perl5/MHA/ServerManager。pm, ln492] Server 172。21。0。33(172。21。0。33:3306) is dead, but must be alive! Check server settings。Sun Mar 7 19:46:16 2021 - [error][/usr/share/perl5/MHA/ManagerUtil。pm, ln178] Got ERROR: at /usr/share/perl5/MHA/MasterFailover。pm line 269。

如果備選主節點先於主節點宕機了,MHA會發現這個現象嗎?主從切換還能完成嗎?

MHA不會發現候選節點宕機或者任何一個從節點宕機的情況。如果發生這種現象,將不能完成主從自動切換。

總結

關於MAH的高可用叢集搭建、主從切換的實驗就到這裡了,如果你有什麼疑問可以評論區留言,我們一起討論。後續給大家分享MGR高可用叢集的搭建和實驗。