Actions











LAMP Load Balancing pour les pauvres

Écrit le 04/05/2006 @ 13:57 par Drizzt

Informatique - Internet / RéseauDepuis le temps que ça fonctionne, je me disais que peut-être je pourrais dévoiler les secrets derrière le setup de load-balancing de MenzoNet. Puisque la solution a été développée sans utiliser aucune aide extérieure, puisqu'il n'en existe pas.. j'ai cherché longtemps et souvent.. je ne crois pas qu'il existe d'autres sites utilisant cette méthode.

Bonne lecture!

La première étape est d'avoir minimum 2 serveurs configurés de façon similaire. Il faut donc avoir Apache + MySQL + PHP de configurés et fonctionnels. Des modifications profondes devront être faites à l'application PHP et aux tables MySQL, mais pour l'instant, contentons nous de synchroniser le contenu.

Dans mon cas, nous avons 3 serveurs. Les 3 serveurs ont des connexions internet indépendante, et l'un des serveurs est dans une autre ville. Il pourrait être au japon que la même méthode pourrait être utilisée. Commençont pas synchroniser les banques de données SQL. Pour bien comprendre la réplication avec MySQL, je vous conseille de lire la section réplication du manuel de MySQL. Il est conseillé de fermer son serveur MySQL et copier les banques de données sur les autres serveurs avant de modifier les paramètres dans my.cnf.

Voici mon fichier /etc/my.cnf

[mysqld]

default-character-set=utf8
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
max_allowed_packet=64M
server-id=1
query_cache_size=64MB
log-bin
log-slave-updates
binlog_do_db=db_a_synchroniser
slave_compressed_protocol
master-host=serveur_no_2.tld
master-user=repluser
master-password=replpasswd
master-connect-retry=15
replicate-do-db=db_a_synchroniser
skip-innodb
skip-bdb

[mysql.server]
user=mysql
basedir=/var/lib

[safe_mysqld]
pid-file=/var/run/mysqld/mysqld.pid
log=/var/log/mysqld_query.log
err-log=/var/log/mysqld.log


Ceci vous donneras donc un premier serveur capable de se répliquer et d'être répliqué. En modifiant les lignes srvr-id et master-host, vous pourrez configurer le serveur 2, 3, ..., N.

Une fois que c'est fait, assurez-vous que la réplication fonctionne bien en lançant la commande SQL show slave status;

Ça fonctionne? Parfait, maintenant synchronisons notre application PHP. Dans mon cas, sur le serveur qui est le plus près de moi et le plus rapide, j'ai créé un partage rsync et automatisé la synchronisation sur les autres serveurs. Voici mon fichier /etc/rsyncd.conf



#### rsyncd.conf file ####
uid = 501
gid = 502
pid file = /var/run/rsyncd.pid
syslog facility = daemon
[web] #Module name could be any name
path = /var/www/
comment = Repertoire du site de MenzoNet
read only = 1
secrets file =/etc/rsyncd.secrets
max connection = 2
use chroot = true
timeout = 60
[ftp] #Module name could be any name
path = /var/ftp/pub
comment = Repertoire du ftp de MenzoNet
read only = 1
secrets file =/etc/rsyncd.secrets
max connection = 2
use chroot = true
timeout = 60
#### End of configuration file ####


Tant qu'à lui, le fichier rsyncd.secrets contient une combinaison nom_utilisateur:mot_de_passe

Assurez-vous que seulement root a accès en lecture sur rsyncd.secrets, car votre mot de passe est écrit en texte clair Eek!

Maintenant, voici mon script de synchronisation. Je la roule chaques jours, et donc mon script s'appelle /etc/cron.daily/sync_web_site.



#!/bin/bash
rsync -qrlz --progress --delete nom_utilisateur@menzonet.org::web /var/www/
rsync -qrlz --progress --delete nom_utilisateur@menzonet.org::ftp /var/ftp/pub
chown -R nom_utilisateur.apache /var/www/
chmod -R 0770 /var/www/


Maintenant, vos données SQL sont synchronisées, et vos scripts PHP le sont aussi! Si vous publiez votre site ainsi, vous allez rencontrer plusieurs problèmes, surtout lors d'opérations split-bain (les serveurs ne sont plus synchronisés). La solution est bien simple, mais requiert une modification aux tables SQL et aux scripts PHP.

Dans toutes les tables, il faut ajouter une clé int non incrémentée nommée srvrid. Par exemple, voici la description de ma table Commentaires :

+------------+---------------------+------+-----+---------+----------------+

| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| idComm | bigint(40) unsigned | | PRI | NULL | auto_increment |
| srvrid | int(10) unsigned | | PRI | 1 | |
| idBlog | varchar(13) | | | 0 | |
| uid | varchar(13) | | | 0 | |
| theDate | bigint(40) unsigned | YES | | NULL | |
| content | text | | | | |
| lastMod | bigint(40) unsigned | YES | | NULL | |
| parentID | varchar(13) | YES | | 0 | |
| moyenneMod | float | YES | | 0 | |
+------------+---------------------+------+-----+---------+----------------+


Ensuite, il faut changer tous les champs servant à faire les liens entre les tables (comme idBlog et uid ci-haut) en champs texte. Dans mon cas, j'ai utilisé une taille fixe plus grande que le nombre de caractère du plus gros chiffre d'ID unique + 3 (varchar d'une longueur 13), ce qui me premettrais d'avoir 100 serveurs différents.

Maintenant qu'on a des nouveaux champs dans la BD, il faut modifier les scripts PHP pour les utilisés. Tous les scripts ajoutant des données devront être modifiés pour insérer aussi un ID unique représentant l'id du serveur. Dans mon cas, j'utilise le même chiffre que server-id dans /etc/my.cnf

J'utilise une fonction PHP retournant automatiquement le srvrid selon le nom de domaine du serveur. Vous pourriez fonctionner différamment, mais bon.. je suis paresseux! Laughing out loud Voici le code :



function getSrvrID(){
$srvrid=0;
if (preg_match("/srvr1/i", $_SERVER["SERVER_NAME"])) {
$srvrid = 2;
} else {
if (preg_match("/srvr2/i", $_SERVER["SERVER_NAME"])) {
$srvrid = 3;
} else {
if (preg_match("/srvr3/i", $_SERVER["SERVER_NAME"])) {
$srvrid = 1;
}
}
}
return $srvrid;
}


Ainsi, dans mes autres scripts PHP, je n'ai qu'à faire :

$srvrid = getSrvrID();



Nos scripts ajoutent les bonnes données, maintenant il faut utiliser ces nouveaux IDs de façon intelligente, car s'il arrive une opération en split-brain, vous pourriez vous retrouver avec 2 entrées retournées par un select.

Pour ce faire, nous devons prendre un ID à 2 clés, comme par exemple 2710-2, et le séparer. Voici le code faisant ce travail :

$pos = strpos($idBlog, '-');

$sql_id = substr($idBlog, 0, $pos);
$sql_srvrid = substr($idBlog, $pos+1);


Ensuite, il suffit de modifier les requêtes SELECT pour utiliser 2 ID au lieu de 1. D'autres méthodes sont possible (CONCAT(idBlog, '-', srvrid) = 2710-2 par exemple) mais sont moins performantes.

Ainsi, une requête SQL comme :

$query = "select * from Blogs where idBlog = $idBlog";


devient :

$query = "select * from Blogs where idBlog = '$sql_id' and srvrid = '$sql_srvrid';



Vous devrez aussi modifier vos scripts pour qu'ils retournent des ID à 2 clés dans le format idIncrémentable-idServeur.

Nos données sont répliquées, nos scripts sont répliqués, nos scripts utilisent les ID à deux clés, il ne vous reste qu'à modifier votre DNS pour faire un round-robin! Dans mon cas, j'ai du créer un serveur DNS local et des scripts mettant à jour les IPs des serveurs. Si vous avez des IPs statiques, c'est encore plus facile!

Notes :
Cette méthode a été testée pendant plusieurs mois et fonctionne très bien. Par contre, dans un setup plus professionnel où les serveurs sont dans le même cabinet, un ou 2 serveurs pourraient être utilisés pour les requêtes SQL, et les autres serveurs seraient des serveurs web spécialisés se branchant sur la même base de données. J'ai testé la capacité de réplication de MySQL sur des liens ADSL de 40kb/sec, sur des entrées de données simples (comme du texte), c'est pratiquement instantané. Par contre, si vous répliquez du gros contenu (comme une image de 2MB), les serveurs vont opérer en split-brain tant que la réplication de sera pas terminée. En envoyant environ 9000 requêtes web très rapidement, mes serveurs de se sont jamais désynchronisés.


Dernière modification le 04/05/2006 @ 18:30 par Drizzt

Pas de commentaires


Tous les blogs
<< Présentations de Windows Vista | Retour aux blogs | L'hôte pas responsable de ses invités >>
Blogs de la même catégorie
<< Routage de paquets avec 2 connexions internet | Problème chez Vidéotron >>