keepalived 做mysql的主从切换

keepalived 做mysql的主从切换

适用于mysql master-slave的主从架构

一、过程简介:

1、通过keepalived配置VIP高可用,keepalived均设置为BACKUP ,nopreempt非抢占模式。

2、master上监控mysql 3306端口的状态,当检测到3306 端口停止后,停止keepalived,vip自动转移到slave上。

3、slave获取到vip升级为master后,执行changemasterdb脚本,将本地mysql库升级为master库,同时将其他从库的主库修改为本库的从库。

二、主从架构图

2.1、切换前的

2.2、切换后的

三、主要配置文件

主要配置文件及作用说明

3.1 keepalived.conf.master 用于当前为master的数据库上的keepalived的配置文件

[root@t156 keepalived]# more keepalived.conf.master

! Configuration File for Keepalived

! ---------------------------------------------------------------------------

! GLOBAL

! ---------------------------------------------------------------------------

global_defs {

! this is who emails will go to on alerts

notification_email {

wanghengzhi@hw801.com

! add a few more email addresses here if you would like

}

notification_email_from wanghengzhi@hw801.com

! mail relay server

smtp_server 127.0.0.1

smtp_connect_timeout 30

! each load balancer should have a different ID

! this will be used in SMTP alerts, so you should make

! each router easily identifiable

router_id LVS_170

vrrp_mcast_group4 224.0.0.18

lvs_sync_daemon eth1 VI1_LVS_DB

script_user root

}

vrrp_script check_mysql {

script "/etc/keepalived/mysql_check.sh"

interval 10

}

vrrp_instance VI1_LVS_DB {

!state MASTER

state BACKUP

interface eth1

track_interface {

eth1

}

! interface to run LVS sync daemon on

! lvs_sync_daemon_interface eth1

!mcast_src_ip 192.168.1.156

! each virtual router id must be unique per instance name!

virtual_router_id 170

! MASTER and BACKUP state are determined by the priority

! even if you specify MASTER as the state, the state will

! be voted on by priority (so if your state is MASTER but your

! priority is lower than the router with BACKUP, you will lose

! the MASTER state)

! I make it a habit to set priorities at least 50 points apart

! note that a lower number is lesser priority - lower gets less vote

priority 100

! how often should we vote, in seconds?

advert_int 1

! send an alert when this instance changes state from MASTER to BACKUP

smtp_alert

! this authentication is for syncing between failover servers

! keepalived supports PASS, which is simple password

! authentication or AH, which is the IPSec authentication header.

! Don't use AH yet as many people have reported problems with it

authentication {

auth_type PASS

auth_pass 111111

}

! these are the IP addresses that keepalived will setup on this

! machine. Later in the config we will specify which real

! servers are behind these IPs without this block, keepalived

! will not setup and takedown any IP addresses

virtual_ipaddress {

192.168.1.170/24 dev eth1

}

nopreempt

!preempt_delay 2

track_script {

check_mysql

}

!notify_master "/etc/keepalived/changemasterdb.sh"

}

注:

state BACKUP #state 全部为BACKUP

priority 100 #当前是master的优先级高

vrrp_script check_mysql {

script "/etc/keepalived/mysql_check.sh" #vrrp_script 检测脚本

interval 10

}

nopreempt # vip 设置为非抢占模式

track_script {

check_mysql # 调用 vrrp_script检测脚本

}

3.2 keepalived.conf.slave 用于当前为slave的数据库上的keepalived的配置文件

[root@t168 keepalived]# more keepalived.conf.backup

! Configuration File for Keepalived

! ---------------------------------------------------------------------------

! GLOBAL

! ---------------------------------------------------------------------------

global_defs {

! this is who emails will go to on alerts

notification_email {

wanghengzhi@hw801.com

! add a few more email addresses here if you would like

}

notification_email_from wanghengzhi@hw801.com

! mail relay server

smtp_server 127.0.0.1

smtp_connect_timeout 30

! each load balancer should have a different ID

! this will be used in SMTP alerts, so you should make

! each router easily identifiable

router_id LVS_170

vrrp_mcast_group4 224.0.0.18

lvs_sync_daemon eth1 VI1_LVS_DB

script_user root

}

vrrp_instance VI1_LVS_DB {

state BACKUP

interface eth1

track_interface {

eth1

}

! interface to run LVS sync daemon on

! lvs_sync_daemon_interface eth1

!mcast_src_ip 192.168.1.168

! each virtual router id must be unique per instance name!

virtual_router_id 170

! MASTER and BACKUP state are determined by the priority

! even if you specify MASTER as the state, the state will

! be voted on by priority (so if your state is MASTER but your

! priority is lower than the router with BACKUP, you will lose

! the MASTER state)

! I make it a habit to set priorities at least 50 points apart

! note that a lower number is lesser priority - lower gets less vote

priority 90

! how often should we vote, in seconds?

advert_int 1

! send an alert when this instance changes state from MASTER to BACKUP

smtp_alert

! this authentication is for syncing between failover servers

! keepalived supports PASS, which is simple password

! authentication or AH, which is the IPSec authentication header.

! Don't use AH yet as many people have reported problems with it

authentication {

auth_type PASS

auth_pass 111111

}

! these are the IP addresses that keepalived will setup on this

! machine. Later in the config we will specify which real

! servers are behind these IPs without this block, keepalived

! will not setup and takedown any IP addresses

virtual_ipaddress {

192.168.1.170/24 dev eth1

}

nopreempt

notify_master "/etc/keepalived/changemasterdb.sh"

}

注:

state BACKUP #state 全部为BACKUP

priority 90 #当前是master的优先级高

nopreempt # vip 设置为非抢占模式

notify_master "/etc/keepalived/changemasterdb.sh" # 切换为master后执行的脚本

3.3 mysql_check.sh 用于主库上mysql 3306端口的检测

[root@t168 keepalived]# more mysql_check.sh

#!/bin/bash

set -o nounset

#@Author : wanghz

#@Time : 2021/9/1 12:05

# define restricted path

PATH="/bin:/usr/bin:/sbin:/usr/sbin"

# adirname - return absolute dirname of given file

adirname() { odir=`pwd`; cd `dirname $1`; pwd; cd "${odir}"; }

PSW="123456"

PORT='3306'

MYSQL_BIN=`which mysql`

MYSQL_MASTER_BIN="${MYSQL_BIN} -uroot -p${PSW} -S /tmp/mysql${PORT}.sock"

count=1

while true

do

${MYSQL_MASTER_BIN} -e "show status\G;" >/dev/null 2>&1

Status=$?

ps aux|grep mysqld|grep -v grep >/dev/null 2>&1

Grep=$?

if [ ${Status} = 0 ] && [ ${Grep} = 0 ]

then

exit 0

else

if [ ${Status} = 1 ] && [ ${Grep} = 0 ]

then

exit 0

else

if [ ${count} -gt 5 ]

then

echo ${count}

break

fi

let count+=1

continue

fi

fi

done

`which service` keepalived stop

3.4 changemasterdb.sh 用于到vip 切换到从库上,进行的一系列操作

[root@t168 keepalived]# more changemasterdb.sh

#!/bin/bash

set -o nounset

#数据库的端口

PORTS=( 3306 3308 )

PSW="123456"

REPL_USER="repl"

REPL_USER_PSW="repl123456"

#ANSIBLE_HOST_NAMES=( "168" ) #同 SLAVE_HOST_IP ,需要在/etc/ansible/hosts里配置

ANSIBLE_HOST_NAMES=( "159" ) #同 SLAVE_HOST_IP ,需要在/etc/ansible/hosts里配置

#[root@t168 ~]# more /etc/ansible/hosts

#[159]

#192.168.1.159

##############################################

# #

# 配置修改开始 #

# #

##############################################

LOCAL_HOST_IP="192.168.1.168" ##升级为主库的现有IP地址

MASTER_HOST_IP='192.168.1.170' ## 主库的IP地址

SLAVE_HOST_IP="192.168.1.157" ##原另一个从库的IP

MYSQL_BIN=`which mysql`

MYSQL_MASTER_BIN='${MYSQL_BIN} -uroot -p${PSW}'

##############################################

# #

# 配置修改结束 #

# #

##############################################

# define restricted path

PATH="/usr/local/mysql/bin:/bin:/usr/bin:/sbin:/usr/sbin"

# adirname - return absolute dirname of given file

adirname() { odir=`pwd`; cd `dirname $1`; pwd; cd "${odir}"; }

MYNAM=`basename "$0"`

MYDIR=`adirname "$0"`

MYLOG_PATH="${MYDIR}/logs"

MYLOG="${MYLOG_PATH}/${MYNAM}_`date +%F`.log"

for D in ${MYLOG_PATH}

do

if [ ! -d ${D} ] ; then

mkdir -p ${D}

echo -e "Mkdir ${D}" >> ${MYLOG}

fi

done

# ---------

# functions

# ---------

#日志函数

function L(){

message="$(date -d today +"%Y-%m-%d %H:%M:%S") - $1"

echo -e "\033[34m $message \033[0m" && echo $message >> ${MYLOG}

}

#主库挂,从库升级为主库

#1、keepalived 切vip到从库上

#2、脚本停止slave同步

#3、重看当前从库的pos和bin-logs记录

#4、修改从库为读写状态

#5、修改另一个从库来连接新的主库并查看同步状态

function CheckMasterIP(){

#1、keepalived 切vip到主库上。keepalived为非抢占模式,切回来不自动切回去。

#获取本地是否有vip

VIP=`/bin/hostname -I|grep "${MASTER_HOST_IP}"`

if [ "x${VIP}" == "x" ]; then

L "vip${MASTER_HOST_IP}没有切到这个服务器上。"

exit 1

fi

L "vip${MASTER_HOST_IP}切换到该服务器上成功"

return 0

}

function StopSlave(){

#2、停止当前slave同步

${MYSQL_MASTER_BIN} -e "stop slave"

${MYSQL_MASTER_BIN} -e "reset slave"

L "${MYSQL_MASTER_BIN} -e \"stop slave\""

SLAVESTATUS=`${MYSQL_MASTER_BIN} -e "show slave status\G"|grep "Slave_SQL_Running"|grep -v "State"|awk '{print $NF}'`

L "${SLAVESTATUS} ${MYSQL_MASTER_BIN} -e \"show slave status\G\"|grep \"Slave_SQL_Running\"|grep -v \"State\"|awk '{print \$NF}'"

if [ "${SLAVESTATUS}" != "No" ];then

L "当前db${IPORT}停止slave失败。"

exit 1

fi

L "停止当前DB${IPORT}的slave同步成功"

return 0

}

function GetNewMasterPosNum(){

#3、重看当前升级为主库的posnum记录

#posnum

Pos=`${MYSQL_MASTER_BIN} -e "show master status\G"|grep "Position"|awk -F " " '{print $2}'`

L "新的master库${IPORT}的pos是${Pos}"

return ${Pos}

}

function AlterReadOnlyStatus(){

#4、修改当前库为读写状态

${MYSQL_MASTER_BIN} -e "set global read_only=0"

#修改网卡配置文件,但不重启(防止该服务器重启后临时IP地址丢失)

sed -i "s/read_only = 1/read_only = 0/g" /etc/my${IPORT}.cnf

L "sed -i \"s/read_only = 1/read_only = 0/g\" /etc/my${IPORT}.cnf"

ReadOnlyStatus=`${MYSQL_MASTER_BIN} -e "show variables like \"read_only\""|awk '{print $NF}'|sed -n '$p'`

ReadOnlyConfig=`grep "read_only" /etc/my${IPORT}.cnf |awk '{print $NF}'`

#echo "${ReadOnlyStatus}"

if [ "${ReadOnlyStatus}" != "OFF" ] && [ "${ReadOnlyConfig}" != "0" ];then

L "修改当前数据库${IPORT}的读写状态失败。"

exit 1

fi

L "修改当前数据库${IPORT}的读写状态成功。"

return 0

}

function AnsibleSlaveConnect(){

#5、修改另一个从库来连接新的主库并查看同步状态

for ANSIBLE_HOST_NAME in ${ANSIBLE_HOST_NAMES[@]}

do

ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"stop slave\""

L "ansible ${ANSIBLE_HOST_NAME} -m shell -a \"${MYSQL_MASTER_BIN} -e \"stop slave\"\""

L "另一个从库${IPORT}停止原主从同步成功"

ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"reset slave\""

L "另一个从库${IPORT}重置原主从同步成功"

ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"set global read_only=1\""

L "获取新主库${IPORT}的bin-logs记录"

LogFile=`${MYSQL_MASTER_BIN} -e "show master status\G"|egrep "File"|awk -F " " '{print $2}'`

L "获取新主库${IPORT}的Pos记录"

GetNewMasterPosNum

ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"change master to master_host='${MASTER_HOST_IP}',master_port=${IPORT},master_user='${REPL_USER}',master_password='${REPL_USER_PSW}',MASTER_LOG_FILE='${LogFile}',MASTER_LOG_POS=${Pos}\""

L "另一个从库${IPORT}指定新的主库"

L "ansible ${ANSIBLE_HOST_NAME} -m shell -a \"${MYSQL_MASTER_BIN} -e \"change master to master_host='${MASTER_HOST_IP}',master_port=${IPORT},master_user='${REPL_USER}',master_password='${REPL_USER_PSW}',MASTER_LOG_FILE=${LogFile},MASTER_LOG_POS=${Pos}\"\""

ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"start slave\""

L "另一个从库${IPORT}开启同步"

SlaveRsyncIp=`ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"show slave status\G\""|grep "Master_Host"|awk '{print $NF}'`

L "ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e \"show slave status\G\""|grep "Master_Host"|awk '{print \$NF}'"

L "SlaveRsyncIp ${SlaveRsyncIp} ,${MASTER_HOST_IP}"

if [ "${SlaveRsyncIp}" != "${MASTER_HOST_IP}" ];then

L "同步主库${IPORT}的IP错误"

exit 1

fi

L "另一个从库${IPORT}配置完成,并开始同步"

return 0

done

}

##############################################

# #

# 始设置本机为master db #

# #

##############################################

function MasterDB(){

for IPORT in ${PORTS[@]}

do

MYSQL_MASTER_BIN="`which mysql` -uroot -p${PSW} -P${IPORT} -S /tmp/mysql${IPORT}.sock"

CheckMasterIP

if [ $? != 0 ];then

L "增加临时IP或修改IP配置文件失败."

exit 1

fi

StopSlave

if [ $? != 0 ];then

L "停止${IPORT}的slave同步失败."

exit 1

fi

AlterReadOnlyStatus

if [ $? != 0 ];then

L "设置master${IPORT}库为读写库失败."

exit 1

fi

AnsibleSlaveConnect

if [ $? != 0 ];then

L "修改另一从库${IPORT}的master库为新的master库失败."

exit 1

fi

L "${IPORT}设置为新的主库完成!"

done

}

##############################################

# #

# 开始执行脚本 #

# #

##############################################

MasterDB

至此,实现了mysql vip的自动切换,同时其他从库从新的master库上同步

相关推荐