Benutzer-Werkzeuge

Webseiten-Werkzeuge


keepalived

====== KeepaliveD ====== In diesem Beispiel soll KeepaliveD einen MySQL-/MariaDB-Cluster überwachen. Diese Konfiguration wurde auf einem ''Ubuntu 16.04.3 LTS (xenial)'' durchgeführt und getestet. ===== Installation KeepaliveD ===== > apt install keepalived ==== Konfiguration ==== Im nächsten Schritt wird der automatische Start beim Booten aktiviert. > update-rc.d keepalived defaults ==== sysctl.conf ==== Virtuelle IP erlauben Um zu erlauben, dass IPs auch auf nicht lokale Schnittstellen zugewiesen werden dürfen, ist ein Eintrag in der /etc/systemctl.conf nötig. > echo "net.ipv4.ip_nonlocal_bind = 1" >> /etc/sysctl.conf > sysctl -p ==== keepalived.conf ==== Die zentrale Konfiguration von KeepaliveD unter Linux erfolgt über die Datei ''/etc/keepalived/keepalived.conf''. === Erreichbarkeit / Service prüfen === Zur Konfiguration sollte man sich zunächst Gedanken machen, wie man Nichterreichbarkeit definiert. Die einfachste Methode ist hierbei der Ping. \\ Allerdings kann ein Server durchaus per Ping erreichbar sein, auch wenn der Dienst (MySQL, Apache, Samba, Mail, DNS, Proxy, etc.) nicht reagiert. Daher hat sich das Prüfen des entsprechenden Dienstes etabliert. Die Signalnummer 0 von ''kill'' bzw. ''killall'' hat keinen symbolischen Namen und dient lediglich der Abfrage ob ein Prozess läuft oder nicht. \\ Der erste Teil der Konfigurationsdatei besteht daher aus dem folgenden Block. <code> vrrp_script chk_dienst { #script "killall -0 mysqld" # einfachste Form einen Dienst zu prüfen - funktioniert in Verbindung mit KeepaliveD nicht zuverlässig script "mysqlshow --defaults-file=/root/.my.cnf >/dev/null" interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK } </code> Eine Zuweisung von Master/Slave erfolgt dabei über die Priorität. Hierbei gilt ''höher = wichtiger''. \\ Aus diesem Grund setzen wir ''101'' auf dem Master und ''100'' auf dem Backup Server. \\ __Wenn die ''priority'' auf beiden Knoten gleich ist (z.B. ''100''), dann muss ''state EQUAL'' verwendet werden.__ Diese Abfrage lässt sich natürlich beliebig anpassen. So könnte man beispielsweise ein eigenes Bash-Skript für komplexere Abfragen konstruieren. Wichtig ist hierbei nur der Rückgabewert: 0 = true - Erreichbar 1 = false - Nicht erreichbar ==== Cluster-IP-Management mit KeepaliveD auf einem MySQL-Cluster ==== Zum Beispiel (für Galera/WSREP): <file c /root/bin/check_db.sh> #!/bin/bash # # DB-Check # # /root/bin/check_db.sh $(hostname -s) 3306 root geheimespasswort # unset node_response mysql_host="${1}"; mysql_port="${2}"; mysql_user="${3}"; mysql_pass="${4}"; OPTION="/tmp/check_db.cfg" touch ${OPTION} chmod 0600 ${OPTION} echo " [client] host = localhost port = ${mysql_port} user = ${mysql_user} password = "${mysql_pass}" " > ${OPTION} node_response=$(echo "SHOW GLOBAL VARIABLES LIKE 'hostname';" | mysql --defaults-file=${OPTION} -N | awk '{ print $2 }'); wsrep_state=$(echo "SHOW STATUS LIKE 'wsrep_local_state_comment';" | mysql --defaults-file=${OPTION} -N | awk '{ print $2 }'); rm -f ${OPTION} echo "${mysql_host} ? ${node_response} / ${wsrep_state}" > /tmp/${mysql_host}ma.txt if [ "${wsrep_state}" == "Synced" ] then if [ "${node_response}" == "${mysql_host}" ] then # echo "Hostname matched" exit 0; else # echo "Hostname not matched" exit 1; fi else # echo "Knoten ist nicht synchron" exit 1; fi </file> Zum Beispiel (für GTID mit Kanal): <file bash /root/bin/check_db.sh> #!/bin/bash #==============================================================================# # # DB-Bakup-IP-Check # # /root/bin/check_db.sh $(hostname -s) 3306 root geheimespasswort # #==============================================================================# VERSION="v2019021900" if [ "x${3}" = x ] ; then echo "${0} [Hostname] [Port] [User]" echo "${0} [Hostname] [Port] [User] [Passwort]" echo "${0} \$(hostname -s) 3306 dbuser geheimespasswort" exit 10 else mysql_host="${1}" fi #==============================================================================# #------------------------------------------------------------------------------# ROOT_FILE="/root/.my.cnf" ROOT_U="$(cat "${ROOT_FILE}" | awk '/^user/{print $3}')" ROOT_P="$(cat "${ROOT_FILE}" | awk '/^password/{print $3}')" STECKER="/var/run/mysqld/mysqld.sock" #STECKER="/var/run/mysqld/mysqld_${2}.sock" #OPTION="/tmp/check_db_${2}_$(head -c 100 /dev/urandom | base64 | tr -d '\n' | tr -cd '[:alnum:]' | cut -b-12).cfg" #touch ${OPTION} #chmod 0600 ${OPTION} #echo " #[client] #host = localhost #port = ${2} #user = ${3} #password = ${4} #socket = ${STECKER} #" > ${OPTION} rm_defaults-file() { echo "OPTION='${OPTION}'" >/dev/null # rm -f ${OPTION} } #==============================================================================# #------------------------------------------------------------------------------# ### erst muss die Master-IP hoch gefahren sein ### nur für Backup-IP aktivieren #sleep 3 #------------------------------------------------------------------------------# ### schauen, ob die Master-IP hier hochgefahren ist ### nur für Backup-IP aktivieren #ma_ip="$(host vadb${2}ma.oqrm.datenbank | awk '{print $NF}')" #MASTER_AN="$(ip a | fgrep " ${ma_ip}/")" # #if [ "x${MASTER_AN}" != "x" ] ; then # #echo "dieses ist der Master: ${MASTER_AN}" # rm_defaults-file # exit 1 #fi #------------------------------------------------------------------------------# ### wenn dieses DBMS nicht läuft, dann darf die IP hier nicht aktiviert werden #echo "echo \"SHOW SLAVE STATUS \G;\" | mysql -S ${STECKER}" #echo "SHOW SLAVE STATUS \G;" | mysql -S ${STECKER} AUSGABE="$(echo "SHOW SLAVE STATUS \G;" | mysql -S ${STECKER} 2>/dev/null || echo Aus)" #echo "AUSGABE='${AUSGABE}'" ### immer aktiviert if [ "${AUSGABE}" = "Aus" ] ; then echo "DB is not Running" rm_defaults-file exit 1 else CHANNEL_NAMEN="$(echo "${AUSGABE}" | awk '/Channel_Name:/{print $NF}')" #CHANNEL_NAMEN='mtversanddb01 mtversanddb03' #CHANNEL_NAMEN='mtversanddb02' fi #------------------------------------------------------------------------------# ### Kontrolliert, ob dieses MySQL-DBMS auf dem richtigen Host läuft #node_response="$(echo "SHOW GLOBAL VARIABLES LIKE 'hostname';" | mysql --defaults-file=${OPTION} -N 2>/dev/null | awk '{ print $2 }')" node_response="$(echo "SHOW GLOBAL VARIABLES LIKE 'hostname';" | mysql -S ${STECKER} -N 2>/dev/null | awk '{ print $2 }')" #echo "node_response='${node_response}'" #echo "${mysql_host} ? ${node_response}" if [ "${node_response}" != "${mysql_host}" ] ; then echo "Hostname not matched: ${node_response}/${mysql_host}" rm_defaults-file exit 1; fi #------------------------------------------------------------------------------# ### jeder Kanal muss separat überprüft werden ### Fehler sind hier nur relevant, wenn kein Kanal vernünftig läuft #echo "CHANNEL_NAMEN: '${CHANNEL_NAMEN}'" STATUS_GUT="$(for KANAL in ${CHANNEL_NAMEN} do #----------------------------------------------------------------------# ### Den Status aus diesem Kanal auslesen SLAVE_STATUS="$(echo "SHOW SLAVE STATUS FOR CHANNEL '${KANAL}' \G;" | mysql -S ${STECKER} -t 2>/dev/null)" MASTER_HOST="$(echo "${SLAVE_STATUS}" | fgrep "Master_Host:" | awk '{print $NF}')" if [ "${MASTER_HOST}" != "${node_response}" ] ; then MASTER_PORT="$(echo "${SLAVE_STATUS}" | fgrep "Master_Port:" | awk '{print $NF}')" SLAVE_IO_RUNNING="$(echo "${SLAVE_STATUS}" | fgrep "Slave_IO_Running:" | awk '{print $NF}')" SLAVE_SQL_RUNNING="$(echo "${SLAVE_STATUS}" | fgrep "Slave_SQL_Running:" | awk '{print $NF}')" SECONDS_BEHIND_MASTER="$(echo "${SLAVE_STATUS}" | fgrep "Seconds_Behind_Master:" | awk '{print $NF}')" #--------------------------------------------------------------# ### Kontrolle ob auf den richtigen Port verbunden wird unset MPORT if [ "${2}" -eq "${MASTER_PORT}" ] ; then echo "Master_Port: ${MASTER_PORT}" > /tmp/${2}ma.txt MPORT="Port" fi #--------------------------------------------------------------# ### Kontrolliert, ob dieser Knoten mit dem Cluster verbunden ist unset RUNNING IO_RUNNING="$(echo "${SLAVE_IO_RUNNING}" | fgrep Yes)" if [ "${IO_RUNNING}" = "Yes" ] ; then SQL_RUNNING="$(echo "${SLAVE_SQL_RUNNING}" | fgrep Yes)" if [ "${SQL_RUNNING}" = "Yes" ] ; then echo "Running: ${IO_RUNNING}/${SQL_RUNNING}" >> /tmp/${2}ma.txt RUNNING="Running" fi fi #--------------------------------------------------------------# #-# Diese Bedingung muss auf dem Backup-Slave nicht zwingend erfüllt sein ### Kontrolliert, ob dieser Knoten mit dem Cluster in Sync ist unset SEKUNDEN if [ "${SECONDS_BEHIND_MASTER}" = "NULL" ] ; then echo "Seconds_Behind_Master: ${SECONDS_BEHIND_MASTER}" >> /tmp/${2}ma.txt elif [ "${SECONDS_BEHIND_MASTER}" -eq "0" ] ; then echo "Seconds_Behind_Master: ${SECONDS_BEHIND_MASTER}" >> /tmp/${2}ma.txt SEKUNDEN="Seconds" fi echo "${MPORT} ${RUNNING} ${SEKUNDEN}" #--------------------------------------------------------------# fi #----------------------------------------------------------------------# done | fgrep "Port Running")" #done | fgrep "Port Running Seconds")" rm_defaults-file if [ "x${STATUS_GUT}" = "x" ] ; then echo "${2}ma $(date +'%F %T')" >> /tmp/KO.txt exit 1 fi </file> Zum Beispiel (für alte MySQL5-Repli): <file c /root/bin/check_db.sh> #!/bin/bash #==============================================================================# # # DB-Check # # /root/bin/check_db.sh $(hostname -s) 3306 root geheimespasswort # #==============================================================================# VERSION="v2019013001" if [ "x${3}" = x ] ; then echo "${0} [Hostname] [Port] [User]" echo "${0} [Hostname] [Port] [User] [Passwort]" echo "${0} \$(hostname -s) 3306 dbuser geheimespasswort" exit 10 else mysql_host="${1}" fi #==============================================================================# unset node_response unset AUSGABE #------------------------------------------------------------------------------# OPTION="/tmp/check_db_$(head -c 100 /dev/urandom | base64 | tr -d '\n' | tr -cd '[:alnum:]' | cut -b-12).cfg" touch ${OPTION} chmod 0600 ${OPTION} echo " [client] host = localhost port = ${2} user = ${3} password = ${4} socket = /var/run/mysqld/mysqld_${2}.sock " > ${OPTION} #==============================================================================# #------------------------------------------------------------------------------# ### Informationen werden eingeholt node_response="$(echo "SHOW GLOBAL VARIABLES LIKE 'hostname';" | mysql --defaults-file=${OPTION} -N 2>/dev/null | awk '{ print $2 }')" AUSGABE="$(echo "SHOW SLAVE STATUS \G;" | mysql --defaults-file=${OPTION} -t 2>/dev/null)" rm -f ${OPTION} #------------------------------------------------------------------------------# ### Kontrolliert, ob dieses MySQL-DBMS auf dem richtigen Host läuft #echo "${mysql_host} ? ${node_response}" if [ "${node_response}" != "${mysql_host}" ] ; then echo "Hostname not matched: ${node_response}/${mysql_host}" exit 1; fi #------------------------------------------------------------------------------# ### Kontrolliert, ob dieser Knoten mit dem Cluster in Sync ist RUNNING="$(echo "${AUSGABE}" | fgrep '_Running:' | awk '{print $NF}' | fgrep -v Yes)" SECONDS="$(echo "${AUSGABE}" | fgrep 'Seconds_Behind_Master:' | awk '{z=$NF;s+=z}END{print s}')" if [ "x${RUNNING}" != "x" ] ; then echo "Slave not Running" exit 1 fi if [ "${SECONDS}" -gt "0" ] ; then echo "Seconds Behind Master greater then zero: ${SECONDS}" exit 1 fi </file> > chmod 0640 /etc/keepalived/keepalived.conf === Auf allen Datenbank-Knoten === __Wenn die ''priority'' auf beiden Knoten gleich ist (z.B. ''100''), dann muss ''state EQUAL'' verwendet werden.__ diese Konfigurationsdatei kann auf allen Knoten gleich sein; der einzige Unterschied ist die Variable ''interface''; es ist wichtig, dass der Wert dieser Variablen auf jedem einzelnen Knoten richtig gesetzt ist: interface eth1 # Knoten01 interface ens2 # Knoten02 <file c /etc/keepalived/keepalived.conf> vrrp_script chk_dienst { #script "mysqlshow --defaults-file=/root/.my.cnf >/dev/null" # MySQL-Dienst per login überprüfen script "/root/bin/check_db.sh $(hostname -s) 3306 rouser Pioph7E3" # MySQL-DB prüfen interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK } vrrp_instance meine_mysql_db { interface eth1 # Zu überwachendes Interface state EQUAL # auf allen Knoten gleich priority 100 # auf allen Knoten gleich #state MASTER #priority 101 # 101 - Master, 100 - Backup virtual_router_id 151 # ID der Route virtual_ipaddress { 10.10.10.10 # Die virtuelle IP Adresse } track_script { chk_dienst } } </file> //** Keepalived starten **// Die Konfiguration ist nun abgeschlossen. Um Keepalived zu starten und damit auch die virtuelle IP im Netzwerk verfügbar zu machen, genügt es den Dienst neu zu starten. > service keepalived restart > service keepalived status === KeepaliveD soll seinen Status ausgeben === <file c /etc/keepalived/keepalived.conf> vrrp_script chk_dienst { script "/root/bin/check_db.sh $(hostname -s) 3306 rouser Pioph7E3" # MySQL-DB prüfen interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK } vrrp_instance meine_mysql_db { interface eth1 # Zu überwachendes Interface state BACKUP priority 99 # 97+98 - Backup, 99 - Master virtual_router_id 151 # ID der Route virtual_ipaddress { 10.10.10.10 # Die virtuelle IP Adresse } track_script { chk_dienst } unicast_src_ip 10.10.10.99 # eigene IP (Knoten 3) unicast_peer { 10.10.10.97 # IP von Knoten 1 10.10.10.98 # IP von Knoten 2 } authentication { auth_type PASS auth_pass geheim_1234 } # for ANY state transition. # "notify" script is called AFTER the # notify_* script(s) and is executed # with 3 arguments provided by keepalived # (ie dont include parameters in the notify line). # arguments # $1 = "GROUP"|"INSTANCE" # $2 = name of group or instance # $3 = target state of transition # ("MASTER"|"BACKUP"|"FAULT") notify /root/bin/keepalived_notify.sh } </file> <file bash /root/bin/keepalived_notify.sh> #!/bin/bash # /root/bin/keepalived_notify.sh echo $1 $2 is in $3 state > /var/run/keepalive.$1.$2.state </file> > cat /var/run/keepalive.INSTANCE.meine_mysql_db.state INSTANCE meine_mysql_db is in MASTER state

keepalived.txt · Zuletzt geändert: 2019/11/01 12:05 von manfred