Monitoring ist neben Backup das wichtigste Gut in der professionellen IT, weswegen Monitoringsystem in der Regel hochverfügbar sein müssen. Ein Ansatz ist die Implementation eines Active/Passive-Clusters, auf welchem Icinga läuft. Die aktive Node übernimmt in der Regel die Aufgaben des Monitorings, während die passive Node inaktiv ist. Bei einem Ausfall der aktiven Node übernimmt die passive Node die Aufgabe - sie wird aktiv. Damit in einem solchen Fall nicht immer die andere IP verwendet werden muss, wird mithilfe von ucarp eine virtuelle IP für beide Server verwendet. Sofern eine Node nicht zur Verfügung steht, wird automatisch der Verkehr zur IP des anderen Servers durchgereicht.
Der Aufbau besteht aus zwei Servern, hier TVM-HAM01 und TVM-HAM002, die über eine Netzwerkschnittstelle (hier jeweils eth0) und eine einzigartige IP, hier 192.168.1.41 und 192.168.1.42, nach „Außen“ verfügen. Zwischen den beiden Servern besteht eine direkte Netzwerk-Verbindung (hier jeweils eth1), die zur Synchronisation eines gemeinsamen Speichers verwendet wird. Dieses „distributed replicated block device“ ist auf beiden Nodes immer synchron, da ein Schreibvorgang erst dann abgeschlossen ist, wenn er auch auf der zweiten Resource ausgeführt wurde. Diese Resource beinhaltet Daten, über die beide Systene verfügen müssen. Insbesondere die Nagios-/Icinga-Konfiguration sowie die temporären Icinga-Daten müssen hier liegen. Zusätzliche optionale Daten, wie z.B. eine Dokuwiki-Installation können hier ebenfalls Platz finden.
Die Netzwertkschnittstellen der einzelnen Server sollten idealerweise statisch konfiguriert werden:
tvm-ham01 | tvm-ham02 |
---|---|
# cat /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE="eth0" BOOTPROTO="none" HWADDR="..." NM_CONTROLLED="no" ONBOOT="yes" TYPE="Ethernet" IPADDR="192.168.1.41" NETMASK="255.255.255.0" GATEWAY="192.168.1.1" | # cat /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE="eth0" BOOTPROTO="none" HWADDR="..." NM_CONTROLLED="no" ONBOOT="yes" TYPE="Ethernet" IPADDR="192.168.1.42" NETMASK="255.255.255.0" GATEWAY="192.168.1.1" |
# cat /etc/sysconfig/network-scripts/ifcfg-eth1 DEVICE="eth1" BOOTPROTO="none" HWADDR="..." NM_CONTROLLED="no" ONBOOT="yes" TYPE="Ethernet" IPADDR="10.0.0.1" NETMASK="255.255.255.252" | # cat /etc/sysconfig/network-scripts/ifcfg-eth1 DEVICE="eth1" BOOTPROTO="none" HWADDR="..." NM_CONTROLLED="no" ONBOOT="yes" TYPE="Ethernet" IPADDR="10.0.0.2" NETMASK="255.255.255.252" |
# cat /etc/resolv.conf nameserver 192.168.1.1 | # cat /etc/resolv.conf nameserver 192.168.1.1 |
Anschließend werden der Apache2-Webserver sowie PHP 5 installiert und aktiviert. In diesem Beispiel wird die Firewall der Einfachheit halber deaktiviert.
# yum install httpd php # chkconfig httpd on # chkconfig iptables off # chkconfig ip6tables off # service httpd start # service iptables stop # service ip6tables stop
UCARP ist nicht Bestandteil von CentOS 6 - es wird das EPEL-Repository benötigt:
# rpm -ivh http://ftp-stud.hs-esslingen.de/pub/epel/6/i386/epel-release-6-7.noarch.rpm # yum update
Nun kann ucarp installiert werden. Die angelegte Beispiel-Konfigurationsdatei wird angepasst:
# yum install ucarp # cp /etc/ucarp/vip-001.conf.example /etc/ucarp/vip-001.conf # vim /etc/ucarp/vip-001.conf
In der Konfigurtionsdatei wird neben der virtuellen IP-Adresse das lokale Interface und deren IP-Adresse konifguriert. Außerdem wird ein Passwort definiert, das zur Kommunikation über ucarp zwischen den Servern verwendet wird:
tvm-ham01 | tvm-ham02 |
---|---|
VIP_ADDRESS="192.168.1.43" ID=001 BIND_INTERFACE="eth0" SOURCE_ADDRESS="192.168.1.41" PASSWORD="CHANGEME" | VIP_ADDRESS="192.168.1.43" ID=001 BIND_INTERFACE="eth0" SOURCE_ADDRESS="192.168.1.42" PASSWORD="CHANGEME" |
Der Dienst wird dahingehend konfiguriert, dass er bei jedem Boot automatisch gestartet wird - initial wird der Dienst das erste Mal händisch gestartet:
# chkconfig ucarp on # service ucarp start
Nun funktioniert die virtuelle IP-Adresse bereits. Um die Funktion von ucarp zu überprüfen, wird auf jeden der beiden Server eine Start-Webseite mit dem Hostname angelegt:
tvm-ham01 | tvm-ham02 |
---|---|
# echo "tvm-ham01" > /var/www/html/index.html | # echo "tvm-ham02" > /var/www/html/index.html |
Beim Aufruf der virtuellen IP-Adresse erscheint nun eine Seite mit dem Namen de aktuell aktiven Systems.
ucarp lässt sich rudimentär durch das Senden zweier Signale steuern - nach erfolgter Aktion schreibt ucarp in das Syslog:
USR1 | USR2 |
---|---|
Status des Knoten herausfinden | Master zum Backup degradieren (falls Masternode) |
kill -SIGUSR1 PID-VON-UCARP | kill -SIGUSR2 PID-VON-UCARP |
tvm-ham01# tail -n 1 /var/log/messages tvm-ham01 ucarp[2363]: [INFO] MASTER on eth0 id 1 tvm-ham02# tail -n 1 /var/log/messages tvm-ham02 ucarp[2697]: [INFO] BACKUP on eth0 id 1 | tvm-ham01# tail /var/log/messages tvm-ham01 ucarp[2363]: [WARNING] Switching to state: BACKUP tvm-ham01 ucarp[2363]: [WARNING] Spawning [/usr/libexec/ucarp/vip-down eth0 192.168.1.43] |
Die PID von ucarp kann man ganz einfach über einen der folgenden Aufrufe herausfinden:
# ps aux|grep ucarp|grep -v grep|tr -s ' '|cut -d ' ' -f 2 1517 # service ucarp status ucarp (pid 1517) is running...
In Fehlerfall switcht UCARP automatisch binnen weniger Sekunden auf den nächsten Server. Mit dem Herunterfahren der Netzwerkschnittstellen auf dem Master-Server kann man einen solchen Ausfall mal schnell demonstrieren:
# ifconfig eth0 down; ifconfig eth1 down
DRBD ist fester Bestandteil des Linux-Kernels - ab Version 2.6.33. Dummerweise kommt CentOS 6 mit 2.6.32 - DRBD ist hier nicht integriert und auch nicht mehr Bestandteil des „CentOS Extra“-Repositories, wie unter CentOS 5.
Es gibt im ELRepo jedoch die notwendigen Utilities und Kernelmodule:
# rpm --import http://elrepo.org/RPM-GPG-KEY-elrepo.org # rpm -ivh http://elrepo.org/elrepo-release-6-4.el6.elrepo.noarch.rpm # yum update # yum install kmod-drbd84 drbd84-utils
Auf den beiden Servern befindet sich eine zweite Festplatte (/dev/sdb), die zwischen den beiden Systemen synchron gehalten werden soll. Zuerst wird die Festplatte auf beiden Systemen partioniert:
# fdisk /dev/sdb << EOF n p 1 w EOF
Die Konfigurationsdatei von DRBD (etc/drbd.conf) wird auf beiden Systemen angepasst:
# vim /etc/drbd.conf ... resource drbd1 { protocol C; syncer { rate 70M; al-extents 257; } on tvm-ham01.localdomain.loc { device /dev/drbd1; disk /dev/sdb1; address 10.0.0.1:7789; meta-disk internal; } on tvm-ham02.localdomain.loc { device /dev/drbd1; disk /dev/sdb1; address 10.0.0.2:7789; meta-disk internal; } }
'drbd1' ignored, since this host (tvm-ham01.localdomain.loc) is not mentioned with an 'on' keyword.
…ab.
# cat /etc/hosts ... 10.0.0.1 tvm-ham01 tvm-ham01.localdomain.loc 10.0.0.2 tvm-ham02 tvm-ham02.localdomain.loc
Auf beiden Systemen werden jetzt die Metadaten des Volumes initialisiert und das Volume anschließend gestartet - danach wird das Volume vom ersten Server aus initial angelegt:
# drbdadm create-md drbd1 # drbdadm up drbd1 Command 'drbdmeta 1 v08 /dev/sdb1 internal apply-al' terminated with exit code 20 # drbdadm --overwrite-data-of-peer primary drbd1
# drbdadm primary --force drbd1
Nachdem auf dem zweiten Server das Gerät ebenso angelegt wurde, ist der Status der Initialisierung einsehbar:
# drbdadm create-md drbd1 # drbdadm up drbd1 # drbdadm secondary drbd1 # cat /proc/drbd ... 1: cs:SyncSource ro:Primary/Secondary ds:UpToDate/Inconsistent C r----- ns:3816448 nr:0 dw:0 dr:3817112 al:0 bm:232 lo:6 pe:1 ua:6 ap:0 ep:1 wo:b oos:1422976 [=============>......] sync'ed: 72.9% (1388/5112)M finish: 0:00:28 speed: 50,460 (52,264) K/sec
Nachdem die Initialisierung abgeschlossen ist, kann die gespiegelte Festplatte (/dev/drbd1) mit einem Dateisystem versehen werden:
# mkfs.ext4 /dev/drbd1
mount: block device /dev/drbd1 is write-protected, mounting read-only mount: Wrong medium type
Damit das Dateisystem auf dem zweiten Server gemountet werden kann, muss das Volume erst ausgebunden und anschließend heruntergestuft werden:
umount /shared # drbdadm secondary drbd1
Der zweite Server kann jetzt das Volume an sich reißen und es einbinden:
# drbdadm primary drbd1 # mount /shared
Damit jetzt im Fehlerfall auch automatisch die DRBD-Festplatte ausgehängt und auf dem anderen System eingehängt wird, werden zwei ucarp-Skripte auf Basis der mitgelieferten Beispiele erstellt:
# cp /usr/libexec/ucarp/vip-down /etc/vip-down # cp /usr/libexec/ucarp/vip-up /etc/vip-up # vim /etc/vip-up #!/bin/sh exec 2>/dev/null /sbin/ip address add "$2"/32 dev "$1" /sbin/drbdadm primary drbd1 /bin/mount /dev/drbd1 /shared # vim /etc/vip-down #!/bin/sh exec 2>/dev/null /sbin/ip address del "$2"/32 dev "$1" /bin/umount /shared /sbin/drbdadm secondary drbd1 # chmod +x /etc/vip-*
# cat /proc/drbd ... 1: cs:StandAlone ro:Primary/Unknown ds:UpToDate/DUnknown r----- ns:12 nr:5453032 dw:5453044 dr:1370 al:1 bm:320 lo:0 pe:0 ua:0 ap:0 ep:1 wo:b oos:0
Dem anderen Server wird mitgeteilt, dass er jetzt der Master ist:
# drbdadm connect drbd1
Auf dem fehlerhaften Servern werden die Änderungen bezogen:
# drbdadm secondary drbd1 # drbdadm --discard-my-data connect drbd1
Zunächst wird MySQL aus dem CentOS-Repository installiert. Für eventuelle spätere NagVis-Installationen wird gleich das MySQL-Modul für PHP installiert:
# yum install mysql-server php-mysql # service mysqld start # mysqladmin -u root password NEUES-PASSWORT
Anschließend wird die Konfigurationsdatei von MySQL gesichert und dahingehend angepasst, dass es seine Daten von der gemeinsamen Festplatte bezieht - auf der gemeinsamen Festplatte wird vorher ein Ordner für die MySQL-Datendateien angelegt. Vor der gesamten Aktion muss die Datenbank natürlich heruntergefahren werden:
# service mysqld stop # mkdir -p /shared/mysql/data # cp /etc/my.cnf /shared/mysql # cp -R /var/lib/mysql /shared/mysql/data # vim /shared/mysql/my.cnf ... datadir=/shared/mysql/data log-bin=mysql-bin ... # mv /etc/my.cnf /etc/my.cnf.initial # ln -s /shared/mysql/my.cnf /etc/my.cnf # service mysqld start # mysql -u root -p
Auf den anderen Server muss nur die Konfigurationsdatei „umgebogen“ werden:
# service mysqld stop # mv /etc/my.cnf /etc/my.cnf.initial # ln -s /shared/mysql/my.cnf /etc/my.cnf
Icinga existiert zwar auch als Binärpaket im RPMForge-Repository, ich jedoch empfehle die händische Übersetzung. Das hat mehrere Gründe: zum Einen ist Aktualisierungspolitik (meiner Meinung nach) relativ restriktiv, was dazu führt, dass es lange dauert, bis neue Versionen von Icinga in das Repository aufgenommen werden - zum Anderen gestaltet sich die Anpassung an die redundante Verwendung der Anwendung durch zwei Server bedeutend komplexer als bei einer händischen Übersetzung.
Die hier beschriebene Übersetzung und Installation erfolgt auf der aktiven Instanz des Clusters.
Zuerst werden einige Entwicklungstools installiert, Icinga und die Nagios-Plugins aus dem Internet bezogen und entpackt, damit einer Kompilation nichts mehr im Wege steht:
# yum install make wget gcc gcc-c++ httpd gd gd-devel glibc glibc-common libjpeg libjpeg-devel libpng libpng-devel openldap-devel php net-snmp net-snmp-devel net-snmp-utils openssl openssl-devel libdbi libdbi-devel libdbi-drivers libdbi-dbd-mysql # mkdir /usr/src/icinga # cd /usr/src/icinga # wget http://downloads.sourceforge.net/sourceforge/icinga/icinga-1.7.1.tar.gz # wget http://downloads.sourceforge.net/sourceforge/nagiosplug/nagios-plugins-1.4.15.tar.gz # tar xfz icinga-1.7.1.tar.gz # tar xfz nagios-plugins-1.4.15.tar.gz
Zuerst wird Icinga übersetzt. Hierfür müssen vorab noch ein Benutzer und zwei Gruppen angelegt werden. Eine Gruppe ist für den Icinga-Dienst, die andere Gruppe berechtigt zusätzliche (Service-)Benutzer dazu, Icinga zu steuern. Zu dieser Gruppe wird der Apache-Benutzer hinzugefügt, damit Icinga über das Webfrontend gesteuert werden kann:
# useradd -m icinga # passwd icinga ... # groupadd icinga # groupadd icinga-cmd # usermod -a -G icinga-cmd icinga # usermod -a -G icinga-cmd apache
groupadd: Gruppe icinga bereits vorhanden
Die Konfiguration für die eigentliche Übersetzung erfolgt nun mittels:
# cd icinga-1.7.1 # ./configure --with-command-group=icinga-cmd --enable-idoutils --enable-ssl --prefix=/usr/local/icinga/ --exec-prefix=/usr/local/icinga/ --sysconfdir=/shared/icinga/etc/ --localstatedir=/shared/icinga/var/ --libexecdir=/shared/icinga/libexec/ ... *** Configuration summary for icinga-core 1.7.1 06-18-2012 ***: General Options: ------------------------- Icinga executable: icinga Icinga user/group: icinga,icinga Command user/group: icinga,icinga-cmd Apache user/group: apache,apache Embedded Perl: no Event Broker: yes ido2db lockfile: /shared/icinga/var/ido2db.lock ido sockfile: /shared/icinga/var/ido.sock idomod tempfile: /shared/icinga/var/idomod.tmp Build IDOUtils: libdbi, instance_name=default Install ${prefix}: /usr/local/icinga Lock file: /shared/icinga/var/icinga.lock Temp file: /tmp/icinga.tmp Chk file: /shared/icinga/var/icinga.chk HTTP auth file: /shared/icinga/etc/htpasswd.users Lib directory: ${exec_prefix}/lib Bin directory: ${exec_prefix}/bin Plugin directory: /shared/icinga/libexec Eventhandler directory: /shared/icinga/libexec/eventhandlers Log directory: /shared/icinga/var Check result directory: /shared/icinga/var/spool/checkresults Temp directory: /tmp State directory: /shared/icinga/var Ext Cmd file directory: /shared/icinga/var/rw Init directory: /etc/rc.d/init.d Apache conf.d directory: /etc/httpd/conf.d Mail program: /bin/mail Host OS: linux-gnu Environment Prefix: ICINGA_
Wichtig ist, dass die Werte mit den oben genannten übereinstimmen. Ein kleiner Vertipper kann hier schon zu sehr unschönen Ergebnissen später führen.
Nach der Konfiguration wird der Compiler angeworfen:
# make all # make fullinstall # make install-config
Nach der Übersetzung und Installation werden zwei Beispielkonfigurationsdateien umbenannt. In aller Regel sind diese weitesgehend korrekt - lediglich die Zugangsdaten für die MySQL-Datenbank müssen noch angepasst werden. Dies geschiet in der Konfigurationsdatei von ido2db - dem Dienst, der nachher die Ereignisse in die Datenbank einträgt:
# cd /shared/icinga/etc # cp idomod.cfg-sample idomod.cfg # cp ido2db.cfg-sample ido2db.cfg # vim ido2db.cfg ... db_servertype=mysql db_port=3306 db_user=icinga db_pass=icinga ... # cp modules/idoutils.cfg-sample modules/idoutils.cfg
Anschließend muss auf der installierten MySQL-Datenbank noch ein Icinga-Benutzer mit einer eigenen Datenbank angelegt werden. Auf dieser Datenbank wird das Standardschema für ido2db installiert:
# mysql -u root -p mysql> CREATE DATABASE icinga; mysql> GRANT USAGE ON icinga.* TO 'icinga'@'localhost' IDENTIFIED BY 'icinga' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0; mysql> GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE VIEW, INDEX ON icinga.* TO 'icinga'@'localhost'; mysql> FLUSH PRIVILEGES; mysql> quit # cd /usr/src/icinga/icinga-1.7.1/module/idoutils/db/mysql # mysql -u root -p icinga < mysql.sql
Das letzte fehlende Stück ist die klassische Weboberfläche von Icinga - die HTML- und CGI-Dateien müssen noch installiert werden:
# cd /usr/src/icinga/icinga-1.7.1 # make cgis # make install-cgis # make install-html # make install-webconf
Damit die Weboberfläche auch verwendet werden kann, muss ein Standardbenutzer icingaadmin für Icinga eingerichtet werden. Anschließend muss der Apache-Webserver seine Konfiguration neu einlesen:
# htpasswd -c /shared/icinga/etc/htpasswd.users icingaadmin # service httpd reload
Damit Icinga auch etwas überwachen kann, fehlen noch die Nagios-Plugins. Mithilfe dieser Plugins wird Icinga zur „eierlegenden Überwachungs-Sau“:
# cd /usr/src/icinga/nagios-plugins-1.4.15 # ./configure --prefix=/shared/icinga --with-cgiurl=/icinga/cgi-bin --with-htmurl=/icinga --with-nagios-user=icinga --with-nagios-group=icinga # make # make install
Zu aller letzt werden die neu installierten Dienste für den automatischen Start vorbereitet, konfiguriert und anschließend gestartet:
# chkconfig --add ido2db # chkconfig --add icinga # chkconfig ido2db on # chkconfig icinga on # service ido2db start # service icinga start
Ein Teil des Icinga-Systems befindet sich auf der gemeinsamen Festplatte. Auf dem passiven System muss die Übersetzung jedoch nochmals bei deaktivierter gemeinsamer Festplatte ausgeführt werden, damit die restlichen Dateien ebenfalls vorhanden sind. Anschließend können die Inhalte unterhalb von /shared theoretisch entfernt werden, da bei der Aktivierung der Node ohnehin die DRBD-Festplatte gemountet und somit der ursprüngliche Inhalt unterhalb von /shared ausgeblendet wird:
# rm -r /shared
Die vorhin angelegten ucarp-Skripte müssen noch angepasst werden, damit im Fall eines Ausfalls auch die MySQL-, ido2db- und Icinga-Dienste gestartet werden:
# vim /etc/vip-down #!/bin/sh exec 2>/dev/null /etc/init.d/icinga stop /etc/init.d/ido2db stop /etc/init.d/mysqld stop /sbin/ip address del "$2"/32 dev "$1" /bin/umount /shared /sbin/drbdadm secondary drbd1
# vim /etc/vip-up #!/bin/sh exec 2>/dev/null /sbin/ip address add "$2"/32 dev "$1" /sbin/drbdadm primary drbd1 /bin/mount /dev/drbd1 /shared /etc/init.d/mysqld start /etc/init.d/ido2db start /etc/init.d/icinga start
Diese Aktion muss auf beiden Clusternodes ausgeführt werden und bedarf einen Neustart des ucarp-Dienstes:
# service ucarp restart
Das hier gezeigte Konzept ist natürlich nicht perfekt. Es gibt noch einige Punkte, an denen gewisser Verbesserungsbedarf besteht: