Server@Home
Economie d'énergie : disques

Comme on s'intéresse ici à un serveur, il n'y a pas d'écrans ou autres périphériques consommateurs d'énergie. Pour économiser l'énergie consommée par le serveur, le gros du travail va donc concerner les disques. Les deux possibilités principales sont :
  • placer le disque dur en mode standby aussi souvent que possible
  • remplacer un disque dur traditionnel par une mémoire flash
Dans les deux cas, la problématique est la même : il faut limiter autant que possible la fréquence des accès au disque dur. En effet, la durée de vie d'une mémoire flash est limitée par le nombre de cycles lecture/écriture supportés. D'autre part, chaque accès à un disque dur le fait nécessairement sortir du mode standby. Une grande partie des idées présentées ici provient originellement de cette page.

Arrêt du disque dur en cas d'inactivité

Mode standby

Sur les disques durs modernes, il existe un mode de fonctionnement en économie d'énergie (mode standby), dans lequel le disque dur s'arrête de tourner.

L'utilitaire hdparm permet de gérer différents aspect de configuration des périphériques IDE. En particulier, il permet de configurer la durée d'inactivité après laquelle le disque dur doit passer en mode d'économie d'énergie (standby).

Test des commandes hdparm

On peut commencer par tester temporairement l'effet de hdparm, en utilisant les options de la ligne de commande.

Pour configurer la durée d'inactivité avant le mode standby, on utilise l'option -S (la table de correspondance entre la valeur passée à l'option et la durée d'inactivité correspondante est disponible dans la page de manuel) :

# hdparm -S 120 /dev/hda

/dev/hda:
 setting standby to 120 (10 minutes)

Pour passer immédiatement en mode standby, on utilise l'option -y :

# hdparm -y /dev/hda

/dev/hda:
 issuing standby command

L'état actuel du disque dur est donné par l'option -C :

# hdparm -C /dev/hda

/dev/hda:
 drive state is:  standby

Configuration définitive de hdparm

Lorsqu'on a déterminé la période optimale de passage en mode standby, on peut configurer de manière plus définitive les options de hdparm. Pour cela, on utilise le fichier de configuration de hdparm qui est lu à chaque démarrage du serveur :

/etc/hdparm.conf :

/dev/hda {
       spindown_time = 120
}

Monitoring de l'activité des disques

Le mode standby des disques durs est très intéressants, mais requiert que le disque passe de long intervalles de temps sans recevoir de requêtes. En effet, dès que le disque reçoit la moindre requête, il doit se remettre à tourner pour pouvoir y répondre. Outre le fait que le "réveil" du disque est assez long et induit de gros délais sur les temps de réponse du système, il faut être conscient que la plupart des disques durs (surtout les disques 3.5" destinés aux PC Desktop), ne sont pas conçus pour subir des cycles activité/standby trop fréquents.

Pour être capable de vraiment diminuer la quantité d'accès aux disques, la première étape consiste à monitorer ces accès et à déterminer quelles en sont les causes. Dans un deuxième temps, une fois qu'on a assez restreint la quantité d'accès à un disque dur, on peut le configurer pour passer en mode d'économie d'énergie (standby) en cas d'inactivité. A partir de ce moment là, le monitoring des changements d'état du disque l'état du disque (standby -> actif) permet de détecter très finement le moindre accès disque ponctuel. Une analyse fine des logs peut permettre d'en trouver l'origine et éventuellement de supprimer encore quelques accès.

Monitoring des accès disque

Le paquet Debian sysstats contient des outils utiles pour surveiller (entre autres) les I/O sur le disque dur. Par exemple :
# iostat -d 1 3
Linux 2.6.18-4-686 (zenega)     06/29/2007

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
hda               0.24         5.23         0.22      58979       2534

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
hda               0.00         0.00         0.00          0          0

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
hda               0.00         0.00         0.00          0          0
ou bien :
# sar -d 1 3
Linux 2.6.18-4-686 (zenega)     06/29/2007

03:13:01 PM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:13:02 PM    dev3-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

03:13:02 PM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:13:03 PM    dev3-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

03:13:03 PM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
03:13:04 PM    dev3-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

Average:          DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
Average:       dev3-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
J'ai pris cet exemple après avoir appliqué toutes les techniques expliquées dans la suite de ce document pour réduire la fréquence des accès aux disques. On voit clairement que les accès disque ont été reduits à leur minimum (zéro !)

Monitoring de l'état du disque dur

Comme on l'a vu précédement; la commande hdparm -C permet de connaitre l'état d'activité d'un disque dur (actif ou en mode standby). Voici un petit script bash qui permet de suivre l'état du disque et de noter chaque changement d'état.
#! /bin/bash

while true ; do
  STATE=$(                                          \
      hdparm -C /dev/hda                            |
      perl -ne 'print if s/\s*drive state is:\s+//' )
  
  if [ ! "${STATE}" = "${STATE_OLD}" ] ; then
      STATE_OLD=${STATE}
      DATE=$(date)
      echo -e "${DATE}\t${STATE}"
  fi
  
  sleep 1
done

Attention, lors du lancement du script, bash effectue plein d'accès à des fichiers de /etc et /home. Ceci peut "réveiller" artificiellement le(s) disque(s) sur lesquels ces répertoires sont montés. Comme le script ne fait plus d'acès disques après, ça ne perturbe l'état du disque qu'au début, mais il vaut mieux être conscient de l'existence de cet effet collatéral.

Réduction de la fréquence des accès au disque

Option de montage noatime

Un premier petit coup de sar permet immédiatement de remarquer un phénomène inattendu : on passe notre temps à écrire sur les disques !

Certains systèmes de fichiers (en particulier les systèmes de fichiers journalisés comme par exemple ext2 ou ext3) tiennent à jour une date de dernier accès (atime) pour chaque fichier. Ceci signifie que chaque lecture sur le système de fichiers provoque aussi la mise à jour de la date de dernier accès au fichier. On peut éviter ceci (et ainsi économiser de nombreux accès en écriture) en utilisant l'option noatime au montage de toutes les partitions du disque :

ex: /etc/fstab :

/dev/hda1 / ext3 defaults,errors=remount-ro,noatime 0 1

Montage de /var/log en mémoire vive

La deuxième source importante d'utilisation des disques durs est l'écriture des journaux du système et des différents services. On pourrait essayer de reconfigurer syslogd et tous les démons pour éviter les logs inutiles, mais une solution beaucoup plus simple consiste à monter /var/log en mémoire vive, avec un système de fichiers tmpfs :

/etc/fstab :

tmpfs   /var/log   tmpfs   size=10m   0   0

L'inconvénient d'une telle solution est que les logs sont perdus à chaque arrêt de la machine (puisque la mémoire vive est effacée). Si l'on souhaite garder les logs (ce qui est assez recommandé pour un serveur), il faut donc mettre en place un système de back-up régulier des logs sur le disque.

Fonctionnement de la gestion des logs sous Debian

Dans un système classique, la gestion des logs se résume à leur rotation : pour éviter que les fichiers de logs prennent trop de place, on jette périodiquement (en général tous les jours ou toutes les semaines) les logs trop anciens.

Sous Debian, la rotation des logs est assurée par deux mécanismes :

  • syslogd : la plupart des journaux du système (/var/log/syslog, /var/log/auth.log, /var/log/daemon.log, etc.) sont gérés par le démon syslogd. Leur rotation est assurée par des scripts cron appelés quotidiennement et hebdomadairement : /etc/cron.daily/sysklogd et /etc/cron.weekly/sysklogd.
     
  • logrotate : bien que certains services utilisent aussi syslogd pour écrire leurs journaux, beaucoup d'autres (comme par exemple Apache) préfèrent écrire eux-même dans leurs propres fichiers de log. Dans ce cas, la rotation des logs est effectuée par l'outil logrotate. logrotate est conçu pour permettre une rotation des journaux aussi peu intrusive que possible (i.e. il ne faut pas que la rotation d'un fichier de log perturbe le fonctionnement du démon qui est en train d'écrire dedans). Chaque service est installé par défaut avec un fichier de configuration de logrotate placé dans le répertoire /etc/logrotate.d. Un script cron (/etc/cron.daily/logrotate) appelle quotidiennement logrotate pour qu'il effectue la rotation des journaux.

Mécanisme de sauvegarde des journaux

Si /var/log est monté en mémoire vive et que nous voulons sauvegarder régulièrement les logs sur le disque dur, il nous faut rajouter une couche supplémentaire au mécanisme traditionnel de rotation des logs.

Typiquement, le système de back-up des logs doit permettre deux choses :

  1. Sauvegarder les logs (sic !), de manière non-obtrusive pour les démons qui les gèrent, régulièrement et juste avant l'arrêt du système
  2. Restaurer les logs au démarrage du système
Pour effectuer la sauvegarde de manière complètement non-intrusive pour les démons, l'une des meilleures manières consiste à forcer une rotation des logs, et ne sauvegarder que les logs "tournés".

Suivant cette idée, j'ai essayé de mettre en place un système global et modulaire de sauvegarde des logs (que j'ai appelé logbackup), dont le fonctionnement est le suivant :

  1. Le système de rotation traditionnel des journaux est désactivé
  2. Un répertoire /etc/logbackup.d contient des shell-scripts permettant de gérer différents fichiers de log. Ces shell-scripts supportent les deux commandes :
    • retrieve : restaure les logs depuis le répertoire de back-up vers /var/log. Les fichiers de logs ne sont restaurés que s'il y en a besoin (i.e. si /var/log ne contient pas le journal concerné).
    • backup : restaure les logs si besoin, effectue la rotation des logs, et sauvegarde les journaux "tournés". La rotation des logs est assurée en appelant directement le script de rotation sysklogd ou logrotate concerné suivant les cas.
  3. Un script /etc/init.d/logbackup permet d'appeler d'un seul coup tous les scripts de /etc/logbackup.d en leur envoyant la commande :
    • retrieve juste après le démarrage du système (/etc/init.d/logbackup start)
    • backup juste avant l'arrêt du système (/etc/init.d/logbackup stop)
    • backup sur appel direct (/etc/init.d/logbackup reload)
  4. Un script cron /etc/cron.daily appelle /etc/init.d/logbackup reload pour forcer la sauvegarde des logs au moins une fois par jour.
En termes d'implémentation, voici ce que ça donne :
/etc
   |
   |-- cron.daily
   |   |-- logrotate    # Appel quotidien de logbackup au lieu de logrotate
   |   `-- sysklogd     # Désactivation de la rotation quotidienne avec syslogd
   |
   |-- cron.weekly
   |   `-- sysklogd     # Désactivation de la rotation hebdomadaire avec syslogd
   |
   |-- init.d
   |   `-- logbackup    # Gestion globale de logbackup
   |
   |-- logbackup.conf   # Fichier de configuration global
   |
   |-- logbackup.d
   |   |-- clean        # Suppression des logs non désirés
   |   |-- skeleton     # Modèle de script logbackup
   |   `-- syslog       # Gestion des logs système
   |
   `-- logrotate.d
       `-- login        # Rotation des journaux de login (wtmp, btmp, lastlog)

Mise en place et utilisation de logbackup

Pour installer logbackup, vous pouvez télécharger l'archive tgz : logbackup.tar.gz, qui contient tous les fichiers nécessaires. Désarchivez puis lancez le script d'installation logbackup:
$ wget http://francesco.ovh.org/serveur/logbackup.tar.gz
$ tar xzf logbackup.tar.gz 
$ su
# cd logbackup
# ./logbackup config

###############################################
# Configuring system logs rotation and backup #
###############################################
Using configuration file config/etc/cron.daily/sysklogd
Using configuration file config/etc/cron.weekly/sysklogd
Using configuration file config/etc/init.d/logbackup
Using configuration file config/etc/logbackup.conf
Using configuration file config/etc/cron.daily/logrotate
Using configuration file config/etc/logbackup.d/syslog
Using configuration file config/etc/logbackup.d/clean
Using configuration file config/etc/logrotate.d/login
 Removing any system startup links for /etc/init.d/logbackup ...
 Adding system startup for /etc/init.d/logbackup ...
   /etc/rc0.d/S37logbackup -> ../init.d/logbackup
   /etc/rc2.d/S17logbackup -> ../init.d/logbackup
   /etc/rc3.d/S17logbackup -> ../init.d/logbackup
   /etc/rc4.d/S17logbackup -> ../init.d/logbackup
   /etc/rc5.d/S17logbackup -> ../init.d/logbackup
   /etc/rc6.d/S37logbackup -> ../init.d/logbackup

On peut lancer manuellement la rotation et sauvegarde des logs avec la commande

invoke-rc.d logbackup reload

Après chaque exécution de logbackup, le fichier /var/log/logbackup.included contient la liste de journaux sauvegardés par logbackup. Inversement, le fichier /var/log/logbackup.omitted contient la liste des journaux qui n'ont pas été traités. Ces journaux sont à surveiller car rien ne les empêche de grossir démesurément et d'occuper toute la place sur la partition /var/log. Si ces journaux ne vous intéressent pas, vous pouvez les rajouter dans la liste des fichiers traités par /etc/logbackup.d/clean pour qu'ils soient automatiquement détruits. Si vous voulez sauvegarder certains journaux qui ne sont pas encore gérés par logbackup, vous pouvez vous inspirer de /etc/logbackup.d/skeleton pour construire un script logbackup permettant de les sauvegarder.

Tâches cron

Par défaut, le démon cron se réveille donc toutes les heures pour exécuter les scripts présents dans le répertoire /etc/cron.hourly. Si ce répertoire esst vide (ce qui est en général le cas), vous pouvez commenter la ligne correspondante dans la crontab principale :

/etc/crontab :

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
#17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

Configuration de ntpd

En monitorant l'état du disque dur à l'aide d'hdparm, on observe un certain nombre de "réveils" du disque suivant un motif régulier :
# monitor_hdparm > /var/log/monitor_hdparm &
# tail -f /var/log/monitor_hdparm
Mon Jul 16 10:12:10 CEST 2007   active/idle
Mon Jul 16 10:26:11 CEST 2007   standby
Mon Jul 16 10:29:54 CEST 2007   active/idle
Mon Jul 16 10:47:31 CEST 2007   standby
Mon Jul 16 13:00:06 CEST 2007   active/idle
Mon Jul 16 13:45:39 CEST 2007   standby
Mon Jul 16 14:29:53 CEST 2007   active/idle
Mon Jul 16 14:40:25 CEST 2007   standby
Mon Jul 16 14:57:50 CEST 2007   active/idle
Mon Jul 16 15:08:31 CEST 2007   standby
Mon Jul 16 15:25:52 CEST 2007   active/idle
Mon Jul 16 15:40:21 CEST 2007   standby
Mon Jul 16 16:29:54 CEST 2007   active/idle
Mon Jul 16 16:40:27 CEST 2007   standby
Mon Jul 16 17:29:54 CEST 2007   active/idle
Mon Jul 16 17:48:14 CEST 2007   standby
Mon Jul 16 20:29:54 CEST 2007   active/idle
Mon Jul 16 20:40:27 CEST 2007   standby
Mon Jul 16 21:29:53 CEST 2007   active/idle
Mon Jul 16 21:40:28 CEST 2007   standby
Mon Jul 16 23:29:54 CEST 2007   active/idle
Mon Jul 16 23:40:25 CEST 2007   standby
Tue Jul 17 02:29:54 CEST 2007   active/idle
Tue Jul 17 02:40:28 CEST 2007   standby
Tue Jul 17 03:29:54 CEST 2007   active/idle
Tue Jul 17 03:40:24 CEST 2007   standby
Tue Jul 17 04:29:53 CEST 2007   active/idle
Tue Jul 17 04:40:25 CEST 2007   standby
Tue Jul 17 05:29:54 CEST 2007   active/idle
Tue Jul 17 05:40:26 CEST 2007   standby
Tue Jul 17 06:25:07 CEST 2007   active/idle
Tue Jul 17 06:40:28 CEST 2007   standby
Tue Jul 17 07:29:53 CEST 2007   active/idle
Tue Jul 17 07:40:34 CEST 2007   standby
Tue Jul 17 08:29:54 CEST 2007   active/idle
Tue Jul 17 08:40:29 CEST 2007   standby
Tue Jul 17 10:15:06 CEST 2007   active/idle
Après quelques recherches, il apparaît que cron n'a rien à voir avec ces réveils, mais que c'est ntpd le fautif :
# ls -al /var/lib/ntp/ntp.drift
-rw-r--r-- 1 ntp ntp 8 2007-07-17 08:29 /var/lib/ntp/ntp.drift
En effet, ntpd met régulièrement à jour (toutes les heures, par défaut) la valeur de dérive de l'horloge dans le driftfile. Déplacer ce fichier dans /var/log (que l'on a monté en tmpfs) permet d'éviter un réveil du disque inutile.

/etc/ntp.conf :

# Put the drift file on /var/log (tmpfs partition)
driftfile /var/log/ntp.drift