Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Tag: Serveur

Uptime

fév 20

Niveau : Star Empty Empty Empty Empty
Résumé : uptimed

Linux est un système très stable. Tellement stable que certaines personnes font une gloire personnelle de la durée d'allumage de leurs machines.

Pour pouvoir vous vanter vous aussi auprès de vos collègues et briller dans les soirées, voici le moyen de savoir quelles ont été les différentes durées d'uptime de votre machine.

Uptimed est un simple démon disponible dans le paquet éponyme. Il suffit de l'installer. Ce démon ne fait qu'une chose et il le fait bien, il enregistre la durée depuis laquelle le système est lancé dans un fichier de logs et rend ces informations disponibles aux utilisateurs. Il est même capable de vous envoyer un mail pour vous avertir de l'atteinte d'un record.

Les utilisateurs ont accès aux meilleurs temps avec la commande uprecords :

$ uprecords

Tout ceci est bien joli et il est vrai que c'est très gratifiant d'avoir réussi à maintenir un système en fonctionnement pendant plusieurs années, voir une dizaine d'années pour certains. Mais il ne faut pas oublier une chose, la sécurité. Un noyau qui n'est jamais rebooté, ce n'est pas seulement un noyau stable, mais c'est aussi un noyau vulnérable car non mis à jour.

Alors bien sur certains argueront qu'il existe maintenant ksplice. Mais je pense que son usage reste marginal. C'est pourquoi, n'oubliez pas de mettre une sonde sur vos serveur pour surveiller les machines qui n'ont pas été rebootées depuis longtemps, c'est en fait un signe de faiblesse.

Niveau : Star Star Star Empty Empty
Résumé : lvm raid

Aujourd'hui la suite tant attendue d'une série sur lvm.

Figurez-vous qu'il est possible de faire du raid avec LVM. Hé oui, vous avez déjà remarqué que vous pouviez mettre plusieurs disque dans un vg. Pour l'instant lvm se contente de les mettre bout à bout, comme le ferait un raid de type linear.

Striping

Le raid0 aussi appelé striping découpe un disque en petits morceaux et les alterne pour en faire un disque plus gros. Le but est d'avoir un disque plus gros, mais aussi d'augmenter les performances. En effet, dès qu'on va lire ou écrire un fichier un peu plus gros que ces morceaux, on va le faire sur 2 disques simultanément et donc augmenter la bande passante.

Lorsqu'on répartit les données linéairement sur le disque on ne gagne pas en performances, par contre en cas de crash d'un des deux disques, il y a moyen de récupérer presque la moitié des données, alors que dans le cas du striping, vous êtes sur de ne rien pouvoir récupérer.

Pour faire du striping avec lvm, rien de plus simple. Lors de la création de votre LV, il suffit de préciser le nombre de "stripes" qu'on désire :

# 2 etant le nombre de partition sur lesquels découper le volume
$ lvcreate -L 1G -i 2 lv0 vg0

Notez que si vous voulez forcer le striping sur certaines partitions, il est possible de donner en paramètre à lvcreate la liste des pv sur lesquels vous vous qu'il soit.

Et pour vérifier que c'est bien ce que vous vouliez :

$ lvdisplay -m /dev/vg0/lv0

Miroir

Le raid1 aussi appelé "mirroring" recopie intégralement un disque sur un autre pour permettre une récupération sans soucis lorsqu'un des deux disques grille, au coût d'un disque supplémentaire.

Alors lvm permet aussi de faire ce genre de chose tout aussi simplement :

# 1 correspond au nombre de copies du volume
$ lvcreate -m 1 -L 1G lv0 vg0

Les miroirs lvm diffèrent du raid1 du fait que lvm demande un périphérique supplémentaire pour stocker les logs du miroir. C'est grâce à ces logs qu'il sait quel est l'endroit qui pose problème lorsqu'il y en a un. C'est donc un peu plus exigeant qu'un raid1.

Et pour vérifier que c'est bien ce que vous vouliez :

$ lvdisplay -m /dev/vg0/lv0

lvm_raid.png

Niveau : Star Star Star Empty Empty
Résumé : lvcreate -s

Avançons encore un peu dans notre exploration de lvm. LVM permet beaucoup de choses, et entre autre la création de snapshots. Un snapshot c'est une prise de vue instantanée, qui dans le cas de lvm se fait au niveau du disque. Ce qui veut dire que le contenu du snapshot peut ne pas être cohérent, par exemple si le système est en train d'écrire sur le disque en plein milieu du snapshot.

On peut utiliser les snapshot pour beaucoup de choses, par exemple, Apple l'utilise pour faire sa machine à remonter dans le temps, on peut l'utiliser pour faire des backup cohérents mais aussi pour faire des expérimentations avec des retours arrière rapide.

Un snapshot

Partons d'un lv existant avec un vg sur lequel il reste de la place (hé oui, il faudra bien stocker nos snapshots. Faisons un snapshot de notre lv :

$ lvcreate -L 50M -s -n snap /dev/vg0/original

Et voila, c'est tout, fin de l'article !

Bon, pas encore. Déjà, pourquoi donner une taille au snapshot ? Tout simplement parce que celui-ci est intelligent, donc il ne va pas copier l'intégralité du lv original. Au contraire, il ne va stocker que les différences. C'est pourquoi il est instantané et commence avec une occupation taille nulle. Par contre, il faut lui allouer une taille dans le vg, donc 50Mo sera la quantité maximum de différence qu'il pourra stocker. Au delà de cette taille, le snapshot sera cassé et il ne pourra plus fonctionner correctement (les données ne sont plus valides, laissez tomber).

lvm_snapshot.png

C'est pourquoi il faut faire attention à bien dimensionner ses snapshots et les gérer correctement. Après quelque quelque temps, vous verrez l'occupation du lv augmenter avec la commande lvdisplay :

$ lvdisplay /dev/vg0/snap
...
  Allocated to snapshot  26,27% 
...

Votre snapshot est occupé à 26%, vous avez encore un peu de marge. C'est à vous de monitorer cette valeur et de prendre une décision, soit supprimer le snapshot (lvremove) ou d'augmenter la place allouée au snapshot (lvextend).

Une fois le snapshot cassé, vous avez ce genre d'information :

$ lvdisplay /dev/vg0/snap
...
  LV snapshot status     source of
                         /dev/vg0/original [INACTIVE]
...

Backup

Donc la technique pour faire un backup est simple, seul problème, si la machine a besoin du système de fichier pendant le snapshot. La meilleure méthode serait de faire "umount;lvcreate -s;mount", mais on ne peut pas. On va donc avoir de préférence un système de fichier qui résiste au crash (ext3 par exemple) et limiter la casse avec sync :

$ sync
# on espère qu'il y aura le moins d'écriture possible entre ces 2 commandes
$ lvcreate -s -L 500M -s -n backup /dev/vg0/original

# et maintenant on a une durée de 500Mo d'écriture pour faire tranquillement notre backup
$ mount /dev/vg0/backup /mnt
$ tar cfz /backups/today.tgz /mnt # c'est vous qui voyez
$ umount /mnt
$ lvremove /dev/vg0/backup

Annulation

Maintenant supposons que nous voulions nous amuser avec notre système de fichier (par exemple pour tester un passage à la prochaine debian sans pour autant avoir peur de tout perdre. Sur / ça risque d'être un peu difficile. Pour ne pas vous perturber nous allons simplifier en utilisant le / d'une machine virtuelle (ou d'un chroot).

$ sync
# choisissez une grande taille, voire la même taille que la partition originale pour éviter tout problème
$ lvcreate -L 5G -s -n backup /dev/vg0/root
# pas besoin de monter le backup
# on bidouille
$ vi /etc/apt/sources.list && apt-get update && apt-get dist-upgrade

Et là paf c'est cassé, réparons (avec un noyau <= 2.6.27):

# on va le casser
$ umount /dev/vg0/root

# on récupère le backup dans un espace temporaire
$ dd if=/dev/vg0/bakup of=/srv/temp.dd

# et on le restaure sur le lv d'origine
$ lvremove /dev/vg0/bakup
$ dd if=/srv/temp.dd of=/dev/vg0/root

# fin
$ rm /srv/temp.dd
$ mount /dev/vg0/root

Le problème de cette technique est qu'elle nécessite un espace aussi gros que la partition disponible pour la restauration. Un petit gzip bien placé peut vous faire gagner en place, mais c'est pas top. Mais ... un patch est en cours pour permettre de faire tout ça en une seule commande et sans nécessiter d'espace intermédiaire. Il ne devrait malheureusement pas être disponible dans le noyau avant la version 2.6.28

# noyau > 2.6.27
$ lvconvert -M bakcup --nameorigin

Lire l'annonce pour plus de détails.

Deux snapshots

Remarquez que de même, il n'est pas possible de faire le snapshot d'un snapshot. C'est bien dommage car cela empêche de faire des tests sur un arbre de test et de revenir à l'endroit ou l'on veut. Ce genre de fonctionnalité n'est pas encore prévue. Il va donc falloir le faire soi-même, ou alors atendre la sortie de btrfs qui devrait permettre ce genre de chose directement sur le système de fichiers..

Donc en attendant, les commandes qui permettent de faire ça en utilisant des copies complètes :

# on snapshotte l'original
$ lvcreate -L 50M -s -n v1x /dev/vg0/v0

# on copie le snapshot (vérifier la taille)
$ lvcreate -L 5G -n v1 vg0
$ dd if=/dev/vg0/v1x of=/dev/vg0/v1
$ lvremove /dev/vg0/v1x

# et on peut resnapshotter
$ vcreate -L 500M -s -n v2 /dev/vg0/v1

Niveau : Star Star Star Empty Empty
Résumé : lvreduce ; vgreduce ; lvextend ; vgextend

Après l'immense succès du premier article, voici sous vos applaudissements le deuxième article.

Ajout d'un nouveau disque

Maintenant que vous savez utiliser LVM vous voudriez aller un peu plus loin. Mise en situation : vous avez un disque de 50To, un vg déjà en place et sur sur ce VG un LV de 30To dédié à votre médiathèque. Or vous venez de découvrir (et donc d'acheter) un nouveau disque de 1Po (Péta Octet pour ceux qui ne suivent pas, hé oui, c'est fou comme ça avance vite la technologie).

$ lvdisplay /dev/vg0/mediatheque
  --- Logical volume ---
  LV Name                /dev/vg0/mediatheque
  VG Name                vg0
  LV UUID                AN0y7h-1cIF-cPmC-vE9E-JnKG-worn-dWeQ8D
  LV Write Access        read/write
  LV Status              available
  # open                 1
  LV Size                31,82 TB
  Current LE             81459200
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0

Commençons donc par ajouter notre nouveau disque au vg :

# partitionnement
$ cfdisk /dev/sdc
# formatage
$ pvcreate /dev/sdc1
# ajout au vg
$ vgextend vg0 /dev/sdc1

Et voila nous avons de la place disponible. Maintenant agrandissons notre LV. Remarquez qu'il faut le faire avant d'agrandir le système de fichier puisqu'il se trouve au dessous. Au contraire en cas de réduction, il faut commencer par le système de fichier qui se trouve au dessus.

$ lvextend -l +100%FREE /dev/vg0/mediatheque
# cas de l'ext2/ext3
$ umount /dev/vg0/mediatheque
$ resize2fs /dev/vg0/mediatheque
$ mount /dev/vg0/mediatheque

Remarquez que chaque système de fichier a ses propres contraintes quant au redimensionnement :

  • ext2 doit être démontés
  • ext3 doit être démonté pour être réduit, il semblerait qu'il est possible de l'agrandir monté (voir ext2online)
  • reiserfs peut être agrandi monte, mais doit être démonté pour être réduit
  • xfs peut (et doit) être agrandi monté, mais ne peut pas être réduit
  • jfs peut (et doit) être agrandi monté, mais ne peut pas être réduit

En résumé : lvm_extend.png

Suppression d'un disque

Il est possible de faire la même chose dans l'autre sens a quelques subtilités près. Il faut forcer la désallocation de l'espace présent sur ce disque. Pour cela on va réduire un système de fichier pour faire de la place, puis faire en sorte que cette place libre soit sur le disque à retirer, et enfin retirer ce disque du groupe. Le tout pouvant se faire en direct.

Comme dit précédemment, lors d'une réduction, le système de fichier doit être réduit en premier :

# supposons de l'ext3
$ umount /dev/vg0/mediatheque
# il faut préciser la nouvelle taille puisqu'il n peut pas la deviner seul
$ resize2fs -p /dev/vg0/mediatheque 1024G
$ mount /dev/vg0/mediatheque
# réduction du lv, je vous conseille d'utiliser la taille absolue pour éviter les erreurs
$ lvreduce-L 1024G /dev/vg0/mediatheque

On fait en sorte que le disque à supprimer ne contienne plus de données :

# il se débrouille pour savoir où mettre les données
$ pvmove /dev/sdb1

Et enfin on le supprime du vg :

$ vgreduce vg0 /dev/sdb1

Niveau : Star Star StarStarEmpty
Résumé : postfix ; catch all

Supposons que vous ayez une application qui envoie régulièrement des mails. Vous voulez tester cette application en conditions réelles, mais sans envoyer ces mails (par exemple un logiciel de spam :-)

Pour ne pas tout casser, nous allons juste changer la configuration du serveur utilisé pour envoyer les mails. Tous les mails, tous domaines et tous destinataires confondus seront acceptés.

Étape 1 : tout rediriger vers le compte de quelqu'un

Prenons un postfix. Ajoutons une ligne dans main.cf :

# on matche les mails qu'on va accepter avec des expression régulières
virtual_alias_maps = regexp:/etc/postfix/virtual

Créons notre fichier /etc/postfix/virtual :

# on redigige tout vers quelqu'un qui va pouvoir lire les mails
/@/	peck@ditoto.com

C'est bien mais on voudrait pouvoir centraliser ce serveur de test et trier les mails envoyés en fonction des serveurs d'origine (développement, recette, qualification ...).

Étape 2 : filtrer

On va donc rediriger vers un compte local créé pour l'occasion (si vous avez des comptes virtuel, il faut faire différemment, vous saurez faire) :

$ adduser genericmail

On adapte /etc/postfix/virtual et on n'oublie pas de laisser passer les destinataires finaux sous peine de boucles :

# on redirige tout vers notre filtre
if !/@ditoto.com$/
/@/	genericmail
endif

On relaie les mails de ces mêmes destinataires finau[xd] vers leur vrai serveur dans main.cf et on se prépare à utiliser procmail :

relay_domains = ditoto.com
mailbox_command = /usr/bin/procmail -Y -a $DOMAIN

Et enfin, le plus important, nous allons filtrer avec un procmail (au passage vérifiez qu'il est installé :-p) :

# on redirige en fonction du destinataire
:0
* ^TO.*@gnagna.fr.*
! peck@ditoto.com

# plus important, on peut le faire en fonction de la source
:0
* ^Received:.*from mamachine.dev.ditoto.com
! pock@ditoto.com

# et une petite règle par défaut pour ne pas être surchargé
:0
* ^.*
/dev/null

La fin

Voilà, vous avez un serveur mail paramétrable qui vous permet de faire ce que vous voulez des mails qu'il reçoit, quels que soient leurs destinataires. Rien de bien compliqué, mais bien pratique en environnement de test.

Niveau : Star Star Star Empty Empty
Résumé : memcached (bis)

Dans un précédent article j'expliquais ce qu'était memcached et comment l'utiliser. Nous allons maintenant nous enfoncer un peu plus dans les détails.

Performances

Tout d'abord parlons performances. Commençons par utiliser les bonnes options :

  • utilisez autant de thread que votre machine a de coeurs (option -c)
  • utilisez autant de ram que possible en n'oubliant pas d'en laisser aux autres applications s'il y en a (option -m)

Si vous utilisez memcached localement sur une seule machine, utilisez l'option -s pour sauter toute la pile tcp/ip et gagner un peu en performances.

Ensuite vous devez modifier votre code pour cacher un maximum de choses, encore faut-il que cela vaille le coup. Heureusement mettre en place le cache et le désactiver est très rapide, vous pouvez donc mesurer facilement vos gains. Sur un site web chargé, il est conseillé de cacher en premier les résultats des requêtes SQL. Mais il peut aussi être judicieux de cacher un tableau, voire toute une page, car elle est probablement une agrégation de plusieurs requêtes SQL (et rien ne vous empêche de faire les 2, voire de mettre des cache partout où vous pouvez). Mais n'oubliez pas, faites des mesures, c'est le meilleur moyen de savoir si votre cache sert à quelque chose.

Choisir quoi cacher est finalement assez simple : la partie la plus grosse possible de la page pouvant être partagé par le plus de monde possible. Donc choisir des éléments ne dépendant pas de l'instant présent. Par exemple ce n'est peut-être pas une bonne idée de cacher une page dépendant d'une session, sauf si votre utilisateur est sensé rester longtemps sur votre site. Contre exemple, quelque chose qui vu par tout le monde mais qui dépend de l'heure qu'il est (le dessin de votre horloge analogique par exemple).

Si votre cache est uniquement local, il peut être plus intéressant d'utiliser une bibliothèque dans votre processus que memcached. Comme le mesure ce site : http://www.mysqlperformanceblog.com... l'api cache interne de php serait 30 fois plus rapide que memcached.

Si votre cache n'est pas uniquement local mais que vous avez toujours des problèmes de performances, il peut être utile d'empiler plusieurs niveaux de cache (local puis distribué).

Et enfin optimisez l'usage de votre cache pour faire en sorte que le maximum de choses demandées au cache s'y trouve déjà. Pour cela faut mesurer son utilisation. Memcached enregistre déjà les mesures et propose une commande pour retourner les résultats.

# on se connecte au démon et on envoit la commande stats
$ telnet localhost 11211
stats
....
END

Si le ratio get_misses/get_hits est élevé (plus de 20%) il est probable que votre cache ne soit pas optimisé au mieux (faites le ratio des différences et non le ratio tout court pour grapher sur une longue durée). Le but est de le garder le plus petit possible, les riches utiliseront même suffisamment de machines et de ram (et de code) pour qu'il atteigne 0%.

Distribution

Un cas particulier dans la gestion des machines doit attirer votre attention. Comme dit au numéro précédent, c'est le client qui gère la répartition des données sur les différentes machines. Ce qui veut dire qu'il n'est pas au courant lorsqu'une des machines tombe. Dans ce cas un Nième du cache disparaît, et il n'est plus possible d'écrire dans cette portion du cache. La solution évidente serait de faire en sorte que les clients soient synchronisés et se mettant d'accord sur la liste des machines régulièrement. Mais attention, remanier la liste des machines implique que l'algorithme de choix des machines a changé, il faut donc invalider tout le cache.

Mais en changeant l'algorithme utilisé coté client, il est possible de s'éviter se problème, la technique est décrite ici : http://amarok.kde.org/blog/archives...

Fin

Notez que votre distribution doit vous fournir un fichier nommé protocol.txt décrivant le protocole utilisé par memcached. Vous pourrez ainsi jouer avec telnet ou coder votre propre client.

Et enfin n'oubliez pas de lire la FAQ

Niveau : Star Star Empty Empty Empty
Résumé : memcached

Memcached, cékoidonc ? Tout d'abord c'est un mot, contrairement aux rigolos (comme moi) qui croient savoir parler anglais, on ne dit pas "memcachde" ou "mèmecachède" mais "mèmecachdi". Cette prononciation explique ce qu'est memcached : memory cache daemon, un processus qui utilise une partie de votre RAM pour en faire un cache. Le but est d'accélérer vos applications quasiment gratuitement.

Sur un site web

Ce démon a été créé pour servir de cache réparti entre plusieurs machines pour accélérer des calculs fréquents et répétitifs, comme par exemple récupérer la liste des défaites du PSG sur le site de l'Équipe. Il permet donc sur les très gros sites de réduire la charge des serveurs de base de données en mettant en RAM une bonne partie des informations.

Au lieu d'interroger directement la base de données, le site va commencer par interroger le cache et seulement ensuite la base si les informations ne se trouvent pas dans le cache (et bien sûr remplir le cache suite à ça). Ce cache fonctionne de manière distribuée, ce qui veut dire que l'information ne se trouve pas nécessairement sur la machine qui sert la page web. Plusieurs machines partagent leur mémoire et contiennent chacune une partie du cache.

Le démon

Le fonctionnement du démon est très simple : au lancement il s'alloue de la mémoire et ensuite écoute sur un port tcp les commandes qui lui sont envoyées. Celles-ci sont d'une simplicité extrême, pour simplifier, disons qu'il y en a 2 : get et set. Set permet de stocker dans la mémoire une valeur associée à une clé (exemple 199$ associé à prix_baril), get permet vous l'aurez compris de récupérer la valeur associée à une clé. L'intérêt de ces fonctions est qu'elles sont très rapides. Ajoutons pour faire plus complet une dose de timeout sur les valeurs (oui ce n'est qu'un cache), et la possibilité de supprimer ou de mettre à jour une valeur.

Cette simplicité implique qu'une certaine logique doit être implémentée au niveau du client. La bibliothèque que vous utiliserez pour communiquer avec memcached doit gérer elle même la distribution des données. La solution communément retenue est de faire un hash sur la clé et de choisir le serveur à interroger en fonction de ce hash. Le fait d'avoir le même algorithme sur tous les clients permet de garantir un fonctionnement cohérent.

Cette solution a l'avantage d'être très rapide puisque nécessitant un minimum de logique.

En pratique

En pratique, il faut une coopération sysadmin / développeur. En effet, c'est le sysadmin qui va mettre en place les cache, mais le développeur doit modifier son application pour appeler memcached et connaitre la liste des machines memcached.

Pour l'installation, je vous laisse vous faire plaisir avec votre distribution préférée. Lançons simplement memcached (en utilisateur normal, cela va de soi) :

# 1Go de ram dédié au cache par machine
<machine1>$ memcached -d -m 1024
<machine2>$ memcached -d -m 1024

Par défaut memcached écoute sur le port tcp 11211, attention il n'y a aucun contrôle d'accès, ce qui signifie que si votre machine est partagée ou si l'accès au port n'est pas filtré, toutes les informations que vous mettrez dans ce cache seront accessible (et modifiables) par des inconnus.

Maintenant il faut choisir une bibliothèque à utiliser en fonction de votre langage.

Choisissons php :

<?php

// on crée le cache et on déclare les différents serveurs
$memcache = new Memcache;
$memcache->addServer('machine1', 11211);
$memcache->addServer('machine2', 11211);

// on va chercher la données qui nous intéresse d'abord dans le cache
$value = $memcache->get('key');
if(!$value) {
  // on récupère la donnée en base
  $value = ...

  // on met à jour le cache (la valeur est valable 2minutes)
  $memcache->add('key', $value, false, 120);
}
?>

Voila, vous avez divisé par 2 la charge de votre base de données (enfin j'espère).

Remarques

Vous êtes limités sur les données à transmettre :

  • Les clés ne peuvent pas faire plus de 250 octets (attentions aux javamen qui ont des noms de variable un peu long)
  • Les données ne peuvent faire plus de 1Mo, on ne peut donc pas chacher n'importe quoi

Vous pouvez mettre les caches sur n'importe quelle machine, vous pouvez par exemple dédier un pool de machine avec beaucoup de RAM au cache. Mais dans le cas des sites web, ils consomment en général plus de CPU que de RAM, vous pouvez donc mettre les memcached directement sur les serveurs web (quitte à ajouter une barette).

Attention, le stockage des données est binaire, c'est donc le client qui les interprète, il est donc fort peu probable que vous puissiez partager des données entre différents langages à travers un memcached, en tout cas avec les bibliothèques que vous trouverez sur le net.

Vous n'êtes pas obligé d'utiliser memcached uniquement pour des sites web, toute application faisant des calculs répétitifs et ne tenant pas sur une seule machine peut avoir besoin d'un cache distribué, par exemple un moteur de recherche, un système de réservation ou toute application centralisée avec une forte audience.

Il existe 2 instructions spécifiques "incr" et "decr" permettant de gérer des compteurs sans avoir à effectuer les longs get/++/set qui pourraient de plus se retrouver en concurrenc. Mais celles-ci ne sont pas implémentées par tous les clients. Vous disposez ainsi d'un compteurs distribué entre tous vos clients. Mais temporaire car n'oubliez pas que memcached n'est qu'un cache. En tant que tel il peut disparaître, vos données ne doivent pas en pâtir, vous devez être de les récupérer (plus lentement certes) par d'autres moyens.

Cet article me semble suffisamment longs, nous verrons dans le prochain les aspects gestion et performance de memcached.