zoukankan      html  css  js  c++  java
  • 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 statusG;" >/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 "33[34m  $message 33[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 statusG"|grep "Slave_SQL_Running"|grep -v "State"|awk '{print $NF}'`
        L "${SLAVESTATUS} ${MYSQL_MASTER_BIN} -e "show slave statusG"|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 statusG"|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 statusG"|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 statusG""|grep "Master_Host"|awk '{print $NF}'`
            L "ansible ${ANSIBLE_HOST_NAME} -m shell -a "${MYSQL_MASTER_BIN} -e "show slave statusG""|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库上同步

  • 相关阅读:
    iOS项目之wifi局域网传输文件到iPhone的简单实现
    iOS项目之苹果审核被拒
    iOS项目之模拟请求数据
    nvm-window常用命令
    初探浏览器渲染原理
    node + mongodb 简单实现自己的查询接口
    快速理解_.debounce方法
    tr标签使用hover的box-shadow效果不生效
    一个简单的Node命令行程序:文件浏览
    打造丝般顺滑的 H5 翻页库(传送门)
  • 原文地址:https://www.cnblogs.com/xzlive/p/15219632.html
Copyright © 2011-2022 走看看