lindev : administration linux , développement php

Aller au contenu | Aller au menu | Aller à la recherche

11 déc. 2009

Réplication mysql

MIse en place

Refresh.png Après avoir écrit cet article je me suis aperçu que le procédé ne fonctionne pas parfaitement .... notamment si des données existent déjà sur le serveur maître .

Je vous présente donc ma façon de faire , afin de mettre en place une réplication avec un serveur déjà en production .

Note : Pas de différence entre le moteur des tables MyIsam , InnoDb .

Utilisateur de replication

Je vais créer sur le maître un utilisateur , qui aura les droits pour effectuer sa réplication .

mysql -uroot -p*******
CREATE USER 'slave_user'@'%' IDENTIFIED BY  'motdepasse';
GRANT REPLICATION SLAVE ON *.* TO slave_user@'%' IDENTIFIED BY 'motdepasse';
FLUSH PRIVILEGES;

Faits attention à ce qu'il n'y ai pas plusieurs utilisateurs du même nom .

Fichiers de conf

Il nous faut faire quelques modifications dans les fichiers de conf des serveurs maître est esclave ... pas de panique , quivez le guide .

Maître

vim /etc/mysql/my.cnf

Dans la section mysqld Il faut juste vérifier à ce que les paramètres suivants soient dé-commentés.

server-id = 1
log_bin   = /var/log/mysql/mysql-bin.log

Server-id se doit d'être unique , sur l'esclave , nous mettrons donc 2
Par contre , commentez le paramètre suivant:

#bind-adress = 127.0.0.1 

Sans quoi les connexions extérieures n'aboutirons pas .

Esclave

vim /etc/mysql/my.cnf

Dans la section mysqld Il faut juste vérifier à ce que les paramètres suivants soient dé-commentés et configurés.

server-id = 2
master-host=ipservmaitre
master-user=slave_user
master-password=******
master-port=3306


Par contre , commentez le paramètre suivant:

#bind-adress = 127.0.0.1 

Sans quoi les connexions extérieures n'aboutirons pas .




Ensuite , Commençons par couper les services qui écrivent sur les bases

Sur le maître et l'esclave :

Dans mon cas , je n'ai eu qu'à couper apache et cron

/etc/init.d/apache2 stop
/etc/init.d/cron stop

Sur le maître

Le principe est le suivant :

  1. RESET des logs binaires
  2. Visualisation de la position
  3. Dump
  4. Envoie du dump ver l'esclave

1 RESET des logs 2:Position

Connectons nous à mysql:

mysql -uroot -p*******
RESET MASTER;
SHOW MASTER STATUS;
exit

Ce qui doit donner :

showmaster.png

Il vous faut noter précieusement le nom du fichier binaire , et la position ici par exemple :

  • mysql-bin.000001
  • 13860

3 Dump des bases du maître

Dans mon cas , je vais repliquer l'ensemble des bases de mon serveur maître, j'utilise donc la commande suivante :

mysqldump -uroot -p******* --databases databaseA databaseB databaseC >/root/dump.sql

Note: je n'ai pas utilisé --all-databases , pour la simple raison , que cela produit un conflit avec la base information_schema et mysql , j'ai donc sélectionné toutes mes bases .

4: Envoie du dump vers le serveur esclave

Bon , juste pour rappel hein ...

scp /root/dump.sql adminservEsclave@ipservesclave:/home/adminservEsclave/dump.sql

Voilà laissons le maître de coté , passons à l'esclave ..

L'esclave:

Voilà le programme ...

  1. On vide entièrement mysql pour être sur du neuf
  2. Importation du dump
  3. Arrêt SQL_SLAVE
  4. RESET SQL SLAVE
  5. Définition du master
  6. Redémarrage du master
  7. Tests

1 nettoyage:

une simple requête :

DROP DATABASES databaseA;
DROP DATABASES databaseB;
DROP DATABASES databaseC;

2 Importation

L'importation aussi simple que l'export :

mysql -uroot -p******* < dump.sql

L'informatique compliqué ... quenéni !!!

3 Reconfiguration SLAVE

Nous voilà à l'étape de configuration de l'esclave .. commençons par le couper

mysql -uroot -p*******
STOP SLAVE;
RESET SLAVE;

Puis définissons le master (toujours dans la console mysql)

CHANGE MASTER TO MASTER _HOST='ip du maitre',
MASTER_USER='slave_user',
MASTER_PASSWORD='pwdslaveuser',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=13860;

SLAVE START;

Voyez l'utilisation du nom du fichier de logs binaires , et la position noté auparavant .
J'ai eu des soucis pour le bon démarrage de l'esclave , je suis obligé de le redémarrer complètement

STOP SLAVE;
RESET SLAVE;
START SLAVE;

4 : passons aux tests

Il faut nous assurer que l'esclave soit bien démarré , pour cela utilisez la commande sql suivant :

SHOW SLAVE STATUS;

Ce qui donne quelque chose du genre :

showslave.png

Il faut vérifier le début , à savoir :

  • Slave_IO_State soit à Waiting for master to send event
  • Slave_IO_Runnig soit à Yes
  • Slave_SQL_Running soit aussi à Yes

Ne vous reste plus qu'à tester l'évolution de la valeur de la position ( dans le log binaire ) à l'insertion d'un enregistrement sur le serveur maître ...
Ici la valeur qui doit évoluer : 972765

A savoir

  • Il est IMPERATIF de ne pas faire d'insertions sur le serveur esclave , seul les sélections sont possible, sinon , votre réplication s'arrêtera.
  • Les valeurs des screens ci dessous ne correspondent pas ensembles , pour la simple raison que les commandes pour faire cet article se sont fait sur des postes qui ne sont pas de vrai replications.


Si vous avez des commentaires , n'hésitez pas .
Ch.

Toujours est-il ,

21 sept. 2009

big brother is watching you !

Surveiller la disponibilité d'un site camera.jpg

Rien de bien extraordinaire , mais le principe m'amuse alors autant partager , et pourquoi pas récupérer des idées dans vos commentaires .

Le principe :

L'on me demande un moyen de vérifier si des sites distants sont disponibles , ou non de façon permanente .

J'ai donc décidé de développer un bout de script , qui ne fait ni plus ni moins que:

  • Tester la résolution DNS ( Type A uniquement pour le moment )
  • récupérer les en-têtes envoyés par le serveur en réponse à une requête HTTP.
  • Récupération de ce qui m'intéresse , à savoir le code de status http , et le temps qu'a mis la réponse à me parvenir .

Ensuite , grâce à ces informations , je peux faire toute sorte de graphiques m'indiquant la disponibilité du/des sites surveillés dans le temps .

Exemple :

viewer.png

Le code

pour récupérer des données le plus proche de la réalité possible Comme j'ai dit plus haut , le code est très simple et rapide à mettre en place , il vous faudra cependant activer curl pour l'utiliser .

Vérifier la résolution de nom

Pour cette étape , nous allons utiliser la fonction native à php 4&5 checkdnsrr() :

checkdnsrr($adresse,"A");

Récupérer les entêtes HTTP

Pas besoin de récupérer la totalité , celà surchargerais inutilement les deux cotés , une première solution consiste à utiliser la fonction get_headers() en natif dans php5 :

get_headers  ( "htp://".$adresse  , true );

L'inconvénient de cette fonction , est qu'elle ne possède pas de timeout hormis celui de php , qui est bien trop long et peut pratique .
Les tests avec cette fonction se sont donc révélé bon , mais a généré l'augmentation de la consommation de mémoire de façon spectaculaire , voyez vous-même !

eluna.png

La solution , est donc d'utiliser curl , qui s'est révélé bien plus efficace , voici donc la fonction de remplacement de get_headers() ( sources fr2.php.net )

function get_headers_curl($url)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL,            $url);
    curl_setopt($ch, CURLOPT_HEADER,         true);
    curl_setopt($ch, CURLOPT_NOBODY,         true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT,        20);

    $r = curl_exec($ch);
    $r = split("\n", $r);
    return $r;
} 

Si vous ne pouvez pas utiliser curl , vous pouvez aussi utiliser la fonction suivante , qui se base sur fsockopen() en natif dans php .

Note:merci à clochix de m'avoir signalé cette autre alternative.

/*
 * Fonction de remplacement de get_headers 
 * Attention à ne pas mettre les http:// devant l'adresse 
 */
function get_headers_sock($adresse, $timeout=15){
    //Tableau qui vat contenir les entêtes
    $Theaders   =   array();
    $UserAgent = "User-Agent : Mozilla/4.0 (compatible; MSIE 5.0; Windows 95)";

    $fp = fsockopen($adresse, 80, $errno, $errstr, $timeout);
    if (!$fp) {
        echo "$errstr ($errno)<br />\n";
    } else {
        $out = "HEAD / HTTP/1.1\r\n";
        $out .= "Host: $adresse\r\n";
        $out .= "$UserAgent\r\n";
        $out .= "Connection : Close\r\n\r\n";

        fwrite($fp, $out);
        while (!feof($fp)) {
            $Theaders[] =   fgets($fp, 128);
        }
        fclose($fp);
    }


    //renvoi des entêtes
    if(!empty($Theaders)){
        return $Theaders;
    }else{
        return null;
    }
    
}

Cette fonction retourne tout comme get_headers() un tableau avec une ligne = une entrée .
Le timeout est ici défini à 20 secondes , car je juge que si l'entête n'est toujours pas là en 20secondes , le site n'est pas navigable normalement , donc indisponible .

Le code de status HTTP

Après avoir reçut la réponse HTTP , l'on décortique l'entrée du tableau n°0 qui correspond normalement à : HTTP/1.1 <code http> <etat>

Une petite expression régulière plus tard , on se retrouve avec le code http seul

//récupération des entêtes
$header = get_headers_curl($adresse);
if(!empty($header){

  //le code http sera disponible dans $retour[1]
  preg_match('#([0-9]{3})#',$header[0],$retour);

}

Le temps pour faire tout ça !

Il ne nous reste plus qu'à récupérer le temps total pour effectuer ces opérations ( temps d'envoi + temps de réception ).
Rien de plus simple , comme dans beaucoup d'exemple sur le net , nous utiliserons microtime()

$debut  = microtime(true);
$header =   get_headers_curl("http://".$adresse) ;
//ou  get_headers_sock($adresse); ( attention à ne pas mettre http devant )
$fin    =  microtime(true);
$temps  =   round($fin-$debut,4);

Et voilà , nous avons toutes les infos nécessaire , ne reste plus qu'à les enregistrer .. en base , fichier .. comme vous voulez .

Exemple de code complet :

<?php
//definition du user_agent

$useragent="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";

//adresse à surveiller ( sans les http )
$adresse = "lindev.fr";
        
$retour = array(0,0);
$dns        =   0;
$temps      =   0;

//test du dns
if( checkdnsrr($adresse,"A")){


	$debut  = microtime(true);
        $header =   get_headers_curl("http://".$adresse) ;
        $fin    =  microtime(true);
        $temps  =   round($fin-$debut,4);

        preg_match('#([0-9]{3})#',$header[0],$retour);
        $dns    =   1;

}else{
        echo "Résolution DNS impossible";   
}

//enregistrement des resulats
$result =   array(
   "DNS"           =>     $dns,
   "reponse"       =>  $retour[1],
   "tempsReponse"  =>  $temps
);

//si timeout , impose le code 503
if($result['reponse'] == NULL){
   $result['reponse'] = 503;
}

//enregistrement dans la base de données / ou fichier
        ... ... ...
function get_headers_curl($url)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL,            $url);
    curl_setopt($ch, CURLOPT_HEADER,         true);
    curl_setopt($ch, CURLOPT_NOBODY,         true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT,        20);

    $r = curl_exec($ch);
    $r = split("\n", $r);
    return $r;
} 
?>

Mise en place

Il faut maintenant lancer ce script de façon régulière afin de surveiller le site de façon précise .
Je vais donc dans mon cas le lancer toutes les 10min , via cron

Vous voilà avec une table qui se remplis toute seule des informations citées ci-dessus , il ne vous reste plus qu'à en faire ce que bon vous semble avec .

Pour info , les graphiques générés ci-dessus sont fait via JPgraph qui fera pourquoi pas l'objet d'un billet pour son initiation .

Conclusion

Voilà une approche d'un outil qui je pense peut s'avérer utile , à partir du moment ou les sites surveillés ne se trouvent pas au même endroit que ce script de monitoring .
Ensuite , n'a de limite que votre imagination pour le faire évoluer , pour par exemple lancer des alertes au bout de X minutes de non-disponibilités , tester d'autres type de services comme les mails , ftp ect ...

J'attends vos retours et vos critiques sur l'idée ou la façon de faire afin d'améliorer tout ça .

A bientôt ,

Ch.

- page 1 de 2