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. ======Aufbau====== {{ :computer:nagios_icinga:icinga_ap_cluster.jpg |}} 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. ======OS-Installation====== 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====== UCARP ist nicht Bestandteil von CentOS 6 - es wird das [[http://fedoraproject.org/wiki/EPEL|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====== 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 [[http://elrepo.org/tiki/tiki-index.php|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; } } Es ist wichtig, dass für die Hostnames ein entsprechender Eintrag in der **/etc/hosts** vorhanden ist. Ansonsten schlägt die Initialisierung mit einer Fehlermeldung... '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 Bei DRBD ab Version 8.4 wird anstelle des letzten Kommandos das folgende Kommando benötigt: # 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 Das Volume kann immer nur von einem Server exklusiv gemountet werden. Wenn das Volume bereits gemountet wurde, erscheint auf dem zweiten Server eine Fehlermeldung: 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 ======Failover====== 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-* Nach einem Ausfall ist die Festplatte des ausgefallenen Hosts nicht mehr aktuell - ein sogenanntes "Split Brain"-Szenario: # 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 ======MySQL====== 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====== Icinga existiert zwar auch als Binärpaket im [[http://repoforge.org/|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 Bei einigen Distributionen wird beim Anlegen eines Benutzers automatisch eine gleichnamige Gruppe angelegt - in einem solchen Fall wird eine Fehlermeldung angezeigt, die getrost ignoriert werden kann: 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 =====UCARP-Skript===== 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 ======Anmerkungen====== Das hier gezeigte Konzept ist natürlich nicht perfekt. Es gibt noch einige Punkte, an denen gewisser Verbesserungsbedarf besteht: * Die Netzwerkschnittstellen zwischen den beiden Servern und von den Servern nach "Außen" sind nicht redundant. Diese Verbindungen könnten mit einer weiteren Schnittstellen zu einem "Bond" zusammengefasst werden um Ausfälle abzufangen. * Mandatory Access Control mittels SELinux wurde deaktiviert - hier müssen insbesondere für Icinga Ausnahmen definiert werden, damit Icinga und die Nagios-Plugins überhaupt verwendet werden können * Die Firewall wurde deaktiviert, es werden unter anderem die folgenden Ports benötigt: * TCP-Port 22 für SSH-Remoteadministration * TCP-Ports 80 und 443 von "Außen" für Apache-Webserver (//unverschlüsselt und verschlüsselt//) * TCP-Ports 7788 bis 7799 auf der Netzwerkschnittstelle zwischen beiden Servern für DRBD-Synchronisation Wichtig ist auch, dass die Anwendungen, die von beiden Servern gleichzeitig verwendet werden, immer den selben Versionsstand aufweisen müssen. Insbesondere der Mischbetrieb von verschiedenen MySQL- und Icinga-Versionen kann sich als problematisch herausstellen. ======Internetverweise====== * Nagios-Cluster mit ucarp und drbd [[http://kublik.net/blog/nagios_cluster]] * Installation und Administration von drbd [[http://kublik.net/blog/drbd]] * Installation und Administration von ucarp [[http://kublik.net/blog/ucarp]] * Hinweis im DRBD-Forum bezüglich DRBD und CentOS 6: [[http://lists.linbit.com/pipermail/drbd-user/2011-December/017378.html]] * IP-Failover mit ucarp unter CentOS: [[http://iarlyy.wordpress.com/2010/03/11/ip-failover-with-ucarp-centos]] * EPEL-Repository: [[http://fedoraproject.org/wiki/EPEL]] * ELRepo: [[http://elrepo.org/tiki/tiki-index.php]] * RPMForge-Repository: [[http://repoforge.org]]