====== KeepaliveD ====== [[https://docs.linuxfabrik.ch/software/keepalived.html]] 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. 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 fall 2 rise 2 } 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. \\ 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 + Backup-IP === //Am Beispiel des Zentralisierungs-Clusters.// #!/bin/bash # Monitoring information for Check_MK echo $1 $2 is in $3 state > /var/run/keepalive_Instance_MAIP_3306.state #!/bin/bash # Monitoring information for Check_MK echo $1 $2 is in $3 state > /var/run/keepalive_Instance_BUIP_3306.state # http://www.keepalived.org/doc/configuration_synopsis.html global_defs { no_email_faults vrrp_no_swap } # Master vrrp_script chk_dienst_ma { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance MAIP_3306 { interface bond0.24 state BACKUP virtual_router_id 101 priority 98 advert_int 1 track_script { chk_dienst_ma } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.20 } unicast_src_ip 10.10.2.21 unicast_peer { #10.10.2.21 10.10.2.22 10.10.2.23 } notify "/usr/local/bin/notify_Instance_MAIP_3306.sh" } # # Backup vrrp_script chk_dienst_bu { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance BUIP_3306 { interface bond0.24 state BACKUP virtual_router_id 102 priority 2 advert_int 2 track_script { chk_dienst_bu } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.24 } unicast_src_ip 10.10.2.21 unicast_peer { #10.10.2.21 10.10.2.22 10.10.2.23 } notify "/usr/local/bin/notify_Instance_BUIP_3306.sh" } # http://www.keepalived.org/doc/configuration_synopsis.html global_defs { no_email_faults vrrp_no_swap } # Master vrrp_script chk_dienst_ma { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance MAIP_3306 { interface bond0.24 state BACKUP virtual_router_id 101 priority 97 advert_int 1 track_script { chk_dienst_ma } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.20 } unicast_src_ip 10.10.2.22 unicast_peer { 10.10.2.21 #10.10.2.22 10.10.2.23 } notify "/usr/local/bin/notify_Instance_MAIP_3306.sh" } # # Backup vrrp_script chk_dienst_bu { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance BUIP_3306 { interface bond0.24 state BACKUP virtual_router_id 102 priority 3 advert_int 2 track_script { chk_dienst_bu } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.24 } unicast_src_ip 10.10.2.22 unicast_peer { 10.10.2.21 #10.10.2.22 10.10.2.23 } notify "/usr/local/bin/notify_Instance_BUIP_3306.sh" } # http://www.keepalived.org/doc/configuration_synopsis.html global_defs { no_email_faults vrrp_no_swap } # Master vrrp_script chk_dienst_ma { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance MAIP_3306 { interface bond0.24 state BACKUP virtual_router_id 101 priority 96 advert_int 1 track_script { chk_dienst_ma } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.20 } unicast_src_ip 10.130.2.23 unicast_peer { 10.10.2.21 10.10.2.22 #10.10.2.23 } notify "/usr/local/bin/notify_Instance_MAIP_3306.sh" } # # Backup vrrp_script chk_dienst_bu { script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheimes-passwort" # MySQL-DB prüfen interval 2 weight 2 fall 2 rise 2 } vrrp_instance BUIP_3306 { interface bond0.24 state BACKUP virtual_router_id 102 priority 4 advert_int 2 track_script { chk_dienst_bu } authentication { auth_type PASS auth_pass geheimes-passwort } virtual_ipaddress { 10.10.2.24 } unicast_src_ip 10.10.2.23 unicast_peer { 10.10.2.21 10.10.2.22 #10.10.2.23 } notify "/usr/local/bin/notify_Instance_BUIP_3306.sh" } ==== Cluster-IP-Management mit KeepaliveD auf einem MySQL-Cluster ==== Zum Beispiel (für Galera/WSREP): #!/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_local_state}" = "4" ] 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 Zum Beispiel (für GTID mit Kanal): #!/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" #==============================================================================# #------------------------------------------------------------------------------# ### erst muss die Master-IP hoch gefahren sein ### nur für Backup-IP aktivieren #sleep 3 #------------------------------------------------------------------------------# ### wenn dieses DBMS nicht läuft, dann darf die IP hier nicht aktiviert werden AUSGABE="$(echo "SHOW SLAVE STATUS \G;" | mysql -S ${STECKER} 2>/dev/null || echo Aus)" ### 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}')" fi #------------------------------------------------------------------------------# ### Kontrolliert, ob dieses MySQL-DBMS auf dem richtigen Host läuft node_response="$(echo "SHOW GLOBAL VARIABLES LIKE 'hostname';" | mysql -S ${STECKER} -N 2>/dev/null | awk '{ print $2 }')" 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 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")" rm_defaults-file if [ "x${STATUS_GUT}" = "x" ] ; then echo "${2}ma $(date +'%F %T')" >> /tmp/KO.txt exit 1 fi === Auf jeden Datenbank-Knoten muß eine individuelle KeepaliveD-CFG abgelegt werden === 1. Knoten: vrrp_script chk_dienst { #script "/usr/bin/pgrep mysqld" # MySQL-DB prüfen #script "killall -0 /usr/sbin/mysqld" # MySQL-DB prüfen script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheim" # MySQL-DB prüfen interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK fall 2 rise 2 } vrrp_instance meine_mysql_db { interface eth0 # Zu überwachendes Interface state BACKUP priority 100 virtual_router_id 123 # ID der Route virtual_ipaddress { 10.10.10.10 # Die virtuelle IP Adresse } track_script { chk_dienst } unicast_src_ip 10.10.10.97 # eigene IP (Knoten 1) unicast_peer { 10.10.10.98 # IP von Knoten 2 10.10.10.99 # IP von Knoten 3 } 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 } 2. Knoten: vrrp_script chk_dienst { #script "/usr/bin/pgrep mysqld" # MySQL-DB prüfen #script "killall -0 /usr/sbin/mysqld" # MySQL-DB prüfen script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheim" # MySQL-DB prüfen interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK fall 2 rise 2 } vrrp_instance meine_mysql_db { interface eth0 # Zu überwachendes Interface state BACKUP priority 100 virtual_router_id 123 # ID der Route virtual_ipaddress { 10.10.10.10 # Die virtuelle IP Adresse } track_script { chk_dienst } unicast_src_ip 10.10.10.98 # eigene IP (Knoten 2) unicast_peer { 10.10.10.97 # IP von Knoten 1 10.10.10.99 # IP von Knoten 3 } 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 } 3. Knoten: vrrp_script chk_dienst { #script "/usr/bin/pgrep mysqld" # MySQL-DB prüfen #script "killall -0 /usr/sbin/mysqld" # MySQL-DB prüfen script "/root/bin/check_db.sh $(hostname -s) 3306 checkuser geheim" # MySQL-DB prüfen interval 2 # Alle 2 Sekunden prüfen weight 2 # 2 Punkte hinzufügen wenn OK fall 2 rise 2 } vrrp_instance meine_mysql_db { interface eth0 # Zu überwachendes Interface state BACKUP priority 100 virtual_router_id 123 # 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 } #!/bin/bash # /root/bin/keepalived_notify.sh echo $1 $2 is in $3 state > /var/run/keepalive.$1.$2.state > cat /var/run/keepalive.INSTANCE.meine_mysql_db.state INSTANCE meine_mysql_db is in MASTER state > chmod 0640 /etc/keepalived/keepalived.conf //** 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