Monit : monitorer son serveur LAMP

Monit est un logiciel de monitoring dédié à la surveillance locale d’un serveur et de ses services.  Il peut effectuer des tests précis et effectuer des actions en retour, comme le redémarrage d’un logiciel si la charge CPU est trop importante. Bref, c’est simple et çà marche.

Je vous colle la configuration pour une installation LAMP sous Debian, regroupant les tests de disponibilité réseau SSH / Apache / MySQL, la vérification de l’espace disque, contrôle de la charge système et de l’utilisation CPU.

set logfile /var/log/monit.log
set mailserver localhost
set mail-format { from: monit@my.domain }
set alert admin@my.domain
set eventqueue
 basedir /var/monit
 slots 100

set httpd port 2812 and
 ssl enable
 pemfile /etc/monit/monit.pem
 allow admin:pa$$word

check system localhost
 if loadavg (1min) > 4 then alert
 if loadavg (5min) > 2 then alert
 if memory usage > 75% then alert
 if cpu usage (user) > 70% then alert
 if cpu usage (system) > 30% then alert
 if cpu usage (wait) > 20% then alert

check device rootfs with path /
 if space usage > 90% then alert

check directory bin with path /bin
 if failed permission 755 then unmonitor
 if failed uid 0 then unmonitor
 if failed gid 0 then unmonitor

check process cron with pidfile /var/run/crond.pid
 group system
 start program = "/etc/init.d/cron start"
 stop  program = "/etc/init.d/cron stop"
 if 5 restarts within 5 cycles then timeout
 depends on cron_rc
check file cron_rc with path /etc/init.d/cron
 group system
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor

check process syslog with pidfile /var/run/rsyslogd.pid
 group system
 start program = "/etc/init.d/rsyslog start"
 stop program = "/etc/init.d/rsyslog stop"
 if 5 restarts within 5 cycles then timeout
check file syslog_file with path /var/log/syslog

check process sshd with pidfile /var/run/sshd.pid
 group system
 start program  "/etc/init.d/ssh start"
 stop program  "/etc/init.d/ssh stop"
 if failed port 22 protocol ssh then restart
 if 5 restarts within 5 cycles then timeout
check file ssh_bin with path /usr/sbin/sshd
 group system
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor
check file ssh_rc with path /etc/init.d/ssh
 group system
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor

check process apache with pidfile /var/run/apache2.pid
 group web
 start program = "/etc/init.d/apache2 start"
 stop program  = "/etc/init.d/apache2 stop"
 if failed host 127.0.0.1 port 80 protocol http and request "/index.html" then restart
 if cpu is greater than 60% for 2 cycles then alert
 if cpu > 80% for 5 cycles then restart
 if children > 250 then restart
 if 3 restarts within 5 cycles then timeout
check file apache_bin with path /usr/sbin/apache2
 group apache
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor
check file apache_rc with path /etc/init.d/apache2
 group apache
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor

check process mysql with pidfile /var/run/mysqld/mysqld.pid
 group database
 start program = "/etc/init.d/mysql start"
 stop program = "/etc/init.d/mysql stop"
 if failed host 127.0.0.1 port 3306 protocol mysql then restart
 if 5 restarts within 5 cycles then timeout
 depends on mysql_bin
 depends on mysql_rc
check file mysql_bin with path /usr/sbin/mysqld
 group database
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor
check file mysql_rc with path /etc/init.d/mysql
 group database
 if failed checksum then unmonitor
 if failed permission 755 then unmonitor
 if failed uid root then unmonitor
 if failed gid root then unmonitor

Un petit screenshot pour présenter le résultat :

Glassfish 3 : créer un pool de connexions JDBC

Cet exemple couvre la création d’un pool de connexions JDBC pour un serveur MySQL,

En premier lieu, ne pas oublier d’installer le driver JDBC de MySQL dans le domaine d’exécution :

$ cd $GLASSFISH_HOME
$ ./bin/asadmin stop-domain
$ cp ~/mysql-connector-java-5.1.13-bin.jar glassfish/domains/domain1/lib/
$ ./bin/asadmin start-domain

La création du pool de connexions (en veillant aux caractères d’échappement dans l’URL) :

$ ./bin/asadmin create-jdbc-connection-pool --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlDataSource --restype javax.sql.DataSource --property User=myuser:Password=mypwd:URL=jdbc\\:mysql\\://127.0.0.1/mydb mydb_pool
Command create-jdbc-connection-pool executed successfully.

Un test rapide pour valider la connexion au serveur MySQL :

$ ./bin/asadmin ping-connection-pool mydb_pool
Command ping-connection-pool executed successfully.

Reste à créer la ressource jdbc/mydb à utiliser dans l’application Java :

$ ./bin/asadmin create-jdbc-resource --connectionpoolid=mydb_pool jdbc/mydb
Command create-jdbc-resource executed successfully.

Roundcubemail : plugin rc-vacation 0.4

La version 0.4 du plugin rc-vacation est disponible. Au programme, l’inclusion d’un driver pour maildrop. Niveau interface, il est possible de configurer une date d’activation et d’expiration du message d’absence.  L’installation du plugin jquery-ui est d’ailleurs conseillée afin de bénéficier des sélecteurs de date jQuery. Plusieurs adresses de redirection sont acceptées. Suite aux nombreuses demandes, des exemples de configuration sont fournis, notamment les schémas SQL.

Dépôt Git : http://github.com/bhuisgen/rc-vacation
Tarball : rc-vacation-0.4.tar.gz

nginx-etag-module : module de gestion des etags pour Nginx

Le protocole HTTP définit un mécanisme de cache par validation des ressources Web : les Entity Tags. Son intérêt est d’autoriser les requêtes conditionnelles afin d’optimiser la gestion de la bande passante des clients et des serveurs Web. Ainsi, à chaque requête, le serveur calcule et ajoute un Etag, matérialisé par un identifiant unique associé à la ressource et à sa version.

En pratique, la première requête envoie le statut HTTP 200 – OK et l’etag calculé par le serveur. Si le client demande à nouveau la ressource et s’il soumet en information l’etag précédent, le serveur est capable de déterminer sa validité. Si les etags matchent, la ressource est à jour côté client et le serveur renvoie un statut HTTP 304 – Content Not Modified sans les données. Les Etags sont donc un moyen simple et fiable de gestion cache des ressources.

Je vais vous présenter le module que j’ai développé pour effectuer ce job sous Nginx : ngx-http-etag-module. Le calcul de l’etag est indépendant du filesystem afin de permettre une utilisation en cluster. A cela, j’y ai couplé une option permettant de forcer le calcul sur un fichier précis, quelque soit la ressource statique demandée. C’est cette raison qui m’a incité à développer ce module, car elle me permet de conserver le cache des proxys frontaux suite à une mise à jour des fichiers. Seul un touch sur le fichier permet d’invalider l’ensemble du cache et propager les modifications.

Installation

$ git clone git://github.com/bhuisgen/nginx-etag-module.git ./nginx-etag-module
Initialized empty Git repository in /private/tmp/nginx-etag-module/.git/
remote: Counting objects: 21, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 21 (delta 6), reused 0 (delta 0)
Receiving objects: 100% (21/21), 4.62 KiB, done.
Resolving deltas: 100% (6/6), done.
$ cd nginx-etag-module/

Le code de Nginx doit également être récupéré, compilation statique oblige :

$ wget http://nginx.org/download/nginx-0.7.67.tar.gz
--2010-09-03 20:35:46--  http://nginx.org/download/nginx-0.7.67.tar.gz
Résolution de nginx.org (nginx.org)... 81.19.68.137
Connexion vers nginx.org (nginx.org)|81.19.68.137|:80...connecté.
requête HTTP transmise, en attente de la réponse...200 OK
Longueur: 608462 (594K) [application/octet-stream]
Sauvegarde en : «nginx-0.7.67.tar.gz»
100%[===================================================================
=======================>] 608.462     64,9K/s   ds 8,5s
2010-09-03 20:35:54 (70,0 KB/s) - «nginx-0.7.67.tar.gz» sauvegardé [608462/608462]
$ tar xzf nginx-0.7.67.tar.gz
$ cd nginx-0.7.67
$ ./configure --add-module=../

Le script configure doit détecter la présence du module :

adding module in ../
 + ngx_http_etag_module was configured

Comme d’habitude, compilation puis installation :

$ make
$ sudo make install

Configuration

Pour activer la prise en charge des etags :

server {
   listen       8000;
   server_name  localhost;
   root /usr/local/www/html;
   index index.php;

   location / {
      etag on;
   }
   location ~ \.php$ {
      include        fragments/php.conf;
   }
}

Pour rediriger le calcul des etags sur un unique fichier :

server {
   listen       8000;
   server_name  localhost;
   root /usr/local/www/html;
   index index.php;

   location / {
      etag on;
      etag_file /opt/local/var/www/etag_file
   }
   location ~ \.php$ {
      include        fragments/php.conf;
   }
}

Exemple :

Mac OS X : normalisation UTF-8 des noms de fichiers

L’encodage UTF-8 n’a pas dit son dernier mot ! La normalisation UTF-8 des noms de fichiers/répertoires est différente entre les systèmes d’exploitation. Mac OS X utilise ainsi la forme D (NFD) alors que les autres systèmes dont Linux utilisent la forme C (NFC).

Bref, la commande magique s’appelle convmv et elle permet de convertir la normalisation NFC en NFD (et inversement) suite à une copie de fichiers entre les deux systèmes.

Exemple d’utilisation :

# convmv -r -i -f utf8 -t utf8 --nfc --notest .

Pour convertir de Linux vers Mac OS, cela donne donc :

# convmv -r -i -f utf8 -t utf8 --nfd --notest .

Pour effectuer un test et confirmer le format de conversion, vous pouvez omettre l’option –notest.

Man page : http://www.j3e.de/linux/convmv/man/
Download : http://www.j3e.de/linux/convmv/

Plus d’infos sur la normalisation UTF-8 : http://www.unicode.org/reports/tr15/

Zabbix : script de notification d’alertes XMPP / Jabber

Zabbix intégre le support de notifications par protocole XMPP / Jabber. Pour autant, en fonction du serveur XMPP, les notifications peuvent ne pas fonctionner. Ceci a été mon cas, probablement car le serveur jabber utilisé n’accepte que les connexions sécurisées. Ne pouvant pas le changer (la gestion par LDAP étant nécessaire), j’ai donc dû testé différentes librairies Perl, Python, etc pour en arriver à celle qui marche : xmpphp. Je vous fait part de cette configuration et des scripts nécessaires.

L’installation des scripts se fait dans le répertoire spécifié par l’option de configuration AlertScriptsPath de Zabbix. Ici, il s’agit de mon côté /etc/zabbix/alertscripts/. Je débute par xmpphp :

# cd /tmp/
# wget http://xmpphp.googlecode.com/files/xmpphp-0.1rc2-r77.tgz
# tar xzf xmpphp-0.1rc2-r77.tgz
# mv xmpphp-0.1rc2-r77/XMPPHP /etc/zabbix/alertscripts/

Puis les deux scripts à mettre en place, le script PHP et le script shell appelé par Zabbix (celui-ci ne peut appeler que des scripts shell) :

# more /etc/zabbix/alertscripts/sendxmpp.php
<?php
include 'XMPPHP/XMPP.php';

if ($_SERVER[argc] < 3)
 die ("Not enough arguments.");

$sendto = $_SERVER[argv][1];
$subject = $_SERVER[argv][2];
$message = $_SERVER[argv][3];

// TODO: change your server settings and IM sender
$conn = new XMPPHP_XMPP('jabber.my.domain', 5222, 'zabbix', 'mot_de_passe',
 'xmpphp', 'my.domain', $printlog=false, $loglevel=XMPPHP_Log::LEVEL_INFO);

try
{
 $conn->connect();
 $conn->processUntil('session_start');
 $conn->presence();
 $conn->message($sendto, $message);
 $conn->disconnect();
}
catch(XMPPHP_Exception $e)
{
 die($e->getMessage());
}
# more /etc/zabbix/alertscripts/sendxmpp.sh
#!/bin/sh
/usr/bin/php /etc/zabbix/alertscripts/sendxmpp.php "$1" "$2" "$3"

Au niveau de la configuration du serveur Zabbix, un nouveau média doit être créé. Il doit être de type script et pointer sur /etc/zabbix/alertscripts/sendxmpp.sh :


Je vous laisse le soin de l’associer à une action. Voici ce que j’obtiens comme notification sur mon poste avec Growl :

FreeBSD : activer le ping dans vos jails

Par défaut, les jails FreeBSD ne peuvent pas créer de sockets de type RAW. Celles-ci sont pourtant utilisées par certains logiciels (sniffeur réseau et cie) à commencer par la commande rudimentaire ping.

Pour pouvoir autoriser la création de telles sockets, il faut modifier un paramètre système :

# sysctl security.jail.allow_raw_sockets=1
security.jail.allow_raw_sockets: 0 -> 1

Les jails devront être redémarrés pour que cela soit effectif. Pour affiner la configuration et  limiter l’impact de sécurité octroyé, il reste votre parefeu pour limiter les flux entrants/sortants des jails concernées.

FreeBSD : configuration réseau des jails

Lorsqu’une seule IP publique est disponible, la configuration réseau des jails doit s’appuyer sur un réseau privé où chaque jail possède son IP tout comme l’hôte réél (eh oui). Ainsi, une nouvelle interface loopback doit être créée, en y attribuant l’adresse IP dédiée à l’hôte :

# ifconfig lo1 create
# ifconfig lo1 inet 172.16.0.254 netmask 255.255.255.0

Ici, l’interface loopback lo1 est créée et l’hôte réel y est associée par l’IP 172.16.0.254. Ensuite, les alias réseaux peuvent être ajoutés de cette façon :

# ifconfig lo1 alias 172.16.0.1 netmask 255.255.255.0

La première jail aura donc l’IP 172.16.0.1. Cette commande est évidemment à répéter pour chaque jail, en y affectant une autre IP. Une fois l’ensemble des alias créés, les jails pourront enfin être démarrées.

Pour recréer la configuration réseau au démarrage, il faut modifier rc.conf :

# network_interfaces="lo0 re0 lo1"
# ifconfig_lo1="inet 172.16.0.254 netmask 255.255.255.0 broadcast 172.16.0.255"
# ifconfig_lo1_alias0="inet 172.16.0.1 netmask 255.255.255.0 broadcast 172.16.0.255"

Puis, créer le fichier start_if.lo1 :

# echo '/sbin/ifconfig lo1 create' > /etc/start_if.lo1

Au niveau de votre parefeu, il faudra évidemment effectuer les redirections de port sur l’IP de la jail pour rendre le service accessible. Par exemple avec PF :

rdr pass on $ext_if proto tcp from any to port 80 -> 172.16.0.1 port 80

Eclipse : utiliser votre script Ant depuis l’IDE

Pour utiliser votre script Ant depuis l’interface d’Eclipse, vous pouvez ajouter un Ant Builder à votre projet afin de remplacer le builder par défaut Java Builder. Une fois votre build.xml écrit, il faut configurer le builder Ant afin de faire correspondre les cibles aux actions de l’interface graphique : Build Project, Clean, Build Automatically

Pour ce faire, éditez les propriétés de votre projet et sélectionner la rubrique Builders. Ajoutez un builder de type Ant Builder et choisissez un nom lié au projet en cours (afin de pouvoir travailler sur plusieurs projets par la suite).

  • La première étape de configuration consiste à sélectionner votre fichier build.xml et son répertoire de travail.

  • Le second onglet permet de configurer le rafraîchissement du projet et de la vue Project Explorer, suite aux nouveaux fichiers  générés durant le build.

  • Les différents évènements de l’interface sont configurés dans l’onglet Targets :
    • After clean correspond au premier évènement de build lancé suite à un nettoyage par Build / Clean… La cible normale de compilation convient à cet usage.
    • Manual Build est levé par Build / Build Project. Si aucun évènement ne s’est produit suite au nettoyage, ce ne sera pas cet évènement qui sera levé mais After clean.
    • Auto-build correspond à l’évènement lancé par l’IDE lorsqu’une modification est détectée dans les sources (ou comment ruiner son disque dur rapidement). Vous pouvez le désactiver en ne spécifiant aucune cible ou le faire par l’interface en décochant Build / Build Automatically.
    • Clean correspond à la cible exécutée par Build / Clean…

Vous pouvez spécifier plusieurs cibles par évènement. Elles seront toutes exécutées par  ordre. Si  vos cibles ont des dépendances, elles seront également exécutées. Dans un tel cas, seule la dernière cible de la chaîne est nécessaire. Si aucune cible n’est sélectionnée, l’interface d’Eclipse ne répondra pas.

  • Dernier point, il faut désactiver le builder d’Eclipse Java Builder et confirmer l’avertissement.

Si vous souhaitez avoir un accès rapide à vos cibles secondaires, vous pouvez faire appel à la vue Ant et y ajouter vos scripts.

PHP : gestion FastCGI avec PHP-FPM

Le port de PHP 5.3.3 est disponible ! Au programme de cette nouvelle version, l’intégration de PHP-FPM, un gestionnaire de processus PHP/FastCGI.

PHP-FPM permet de gérer plusieurs pools de processus, chacun possédant ses propres règles : gestion statique/dynamique (pour s’adapter à la charge du serveur), limites mini/maxi de processus actifs/en attente, type d’écoute par socket Unix ou TCP/IP (pour déporter l’exécution sur un serveur distinct), nombre maxi de requêtes par processus avant extinction. Le redémarrage complet du pool est également supporté en cas de crash suite à une fuite mémoire. Le recours à un outil équivalent mais limité en option, tel que spawn-fcgi n’est donc plus nécessaire (référez-vous à mes articles à son sujet : ici et ici). J’oubliais l’utilisation d’un cache de code PHP tel que APC ou Xcache est possible vu que les processus php-fpm ne sont pas détruits après chaque requête.

Je vous fais part des étapes nécessaires à sa mise en place, effectuées ici sous FreeBSD 8.1 & Nginx.

  • Configuration de PHP

Il faut s’assurer d’activer le flag de compilation de PHP-FPM :

# cd /usr/ports/lang/php5
# make config
# make install

On oublie pas l’extension de sécurité suhosin et le cache APC :

# cd /usr/ports/security/php-suhosin
# make install
# cd /usr/ports/www/pecl-APC
# make install

Une petite vérification de routine :

# php -v
PHP 5.3.3 with Suhosin-Patch (cli) (built: Aug  9 2010 21:34:21)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
 with Suhosin v0.9.32.1, Copyright (c) 2007-2010, by SektionEins GmbH
  • Configuration de PHP-FPM

Le module dispose de son propre fichier de configuration /usr/local/etc/php-fpm.conf :

[global]
pid = /var/run/php-fpm.pid
;error_log = /var/log/php-fpm.log
[www]
# socket
listen = /tmp/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0666
# user ID/GID
user = www
group = www
# pool
pm = dynamic
pm.max_children = 20
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 8

Est configuré un pool de processus dynamique avec un maximum de 20 processus PHP, avec écoute par socket UNIX, le tout limité au compte utilisateur du serveur web www. Le démon peut à présent être lancé :

# echo "php_fpm_enable="YES" >> /etc/rc.conf
# /usr/local/etc/rc.d/php-fpm start
Starting php_fpm.

Le pool PHP devrait être en exécution :

# su -m www
www# ps aux|grep php
www  68220  0.6  2.3 110152 48216  ??  SJ   11:56PM   0:00.95 /usr/local/sbin/php-fpm
www  68219  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68221  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68222  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68223  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68224  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68225  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68226  0.0  0.6 99912 12540  ??  IJ   11:56PM   0:00.00 /usr/local/sbin/php-fpm
www  68402  0.0  0.1  9092  1172   1  R+J  12:00AM   0:00.00 grep php
  • Configuration de Nginx

Le module FastCGI doit être configuré pour utiliser le pool. J’utilise pour ce faire un fragment de configuration que j’inclue dans chaque vhost où PHP est nécessaire :

# cd /usr/local/etc/nginx/
# more fragments/php.conf
fastcgi_pass   unix:/tmp/php-fpm.sock;
fastcgi_index  index.php;
fastcgi_param  SCRIPT_FILENAME    $request_filename;
include        fastcgi_params;

Le nom et le type de socket doit correspondre ici à ce qui a été déclaré dans la configuration de PHP-FPM.

# more vhosts/site_fr.conf
server {
 listen 172.16.0.5:80;
 server_name www.site.fr;
 root /usr/local/www/site.fr/www/html;
 index index.php;

 location ~ \.php$ {
  include fragments/php.conf;
 }
}

Nginx peut enfin être redémarré  :

# /usr/local/etc/rc.d/nginx restart
Performing sanity check on nginx configuration:
the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
configuration file /usr/local/etc/nginx/nginx.conf test is successful
Stopping nginx.
Waiting for PIDS: 89603.
Performing sanity check on nginx configuration:
the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.

L’exécution des scripts PHP est à présent assurée par le pool PHP-FPM.

Haut de page