PostgreSLQ & Streaming Replication (9.2)
Installation de PostgreSQL 9.2
Téléchargement des sources
$> cd /usr/src/
$> wget http://ftp.postgresql.org/pub/source/v9.2.2/postgresql-9.2.2.tar.gz
$> tar xzvf postgresql-9.2.2.tar.gz
Compilation et Installation
Pré-requis
$> apt-get install libreadline5-dev
$> apt-get install zlib1g-dev
$> cd /usr/src/postgresql-9.2.2/
$> ./configure
$> make
$> make install
Création du user postgres
$> adduser postgres
Adding user `postgres' ...
Adding new group `postgres' (1001) ...
Adding new user `postgres' (1001) with group `postgres' ...
Creating home directory `/home/postgres' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: <= http://strongpasswordgenerator.com/
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for postgres
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
$> mkdir /usr/local/pgsql/data
$> chown postgres.postgres /usr/local/pgsql/data
Streaming Réplication
Introduction
La version 9.0 ne se limite pas seulement aux fonctionnalités basiques de la réplication postgres.
Elle ajoute en plus une technologie appelée « Streaming Replication » :
Cette technologie permet d'envoyer les données modifiées, non plus journal de transactions par journal de transactions, mais groupe de transactions par groupe de transactions. Cela permet une granularité plus fine dans la réplication, et du coup un lag moins important.
Pré-requis
Avant de mettre en place la streaming replication vous devez avoir 2 serveurs, qui communique via le réseaux.
Vous devez avoir installé PostgreSQL de la manière expliqué précédemment.
La réplication PostgresSQL fonctionne avec des versions mineures différentes.
Des version 9.1 et 9.2 pourront fonctionner ensemble.
Une version 8.4 et 9.0 ne fonctionneront pas en réplication.
Mise en place
Dans un soucis de facilité de mise en place et de compréhension, je précéderai chaque bloc de code avec les indices "M." ou "S." respectivement pour Master et Slave.
Ainsi on saura sur quel serveur on est à chaque fois.
Voici ma configuration IP :
Master : 10.234.210.50
Slave : 10.234.210.51
Initialisation des données
Tout d'abord nous allons initialisé les données via la commande :
M.
$> /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
Configuration
Pour ne pas à "salir" le fichier principale de configuration postgresql.conf, je vais simplement lui ajouter des include et faire ma configuration dans des fichiers à part :
M.
$> cd /usr/local/pgsql/data/
$> cat >> postgresql.conf << _EOF_
> include 'communs.conf'
> _EOF_
$> cat >> communs.conf << _EOF_
> logging_collector = on
> log_line_prefix = '%t '
> lc_messages = 'C'
> _EOF_
$> cat >> postgresql.conf << _EOF_
> include 'master.conf'
> _EOF_
$> cat >> master.conf << _EOF_
> listen_addresses = '10.234.210.50'
>
> wal_level = 'hot_standby'
> max_wal_senders = 5
> wal_keep_segments = 32
>
> archive_mode = on
> archive_command = 'cp %p /usr/local/pgsql/archive/%f'
> _EOF_
$> mkdir /usr/local/pgsql/archive && chown postgres.postgres /usr/local/pgsql/archive
Pour que le slave puisse accéder au master, il faut autoriser l'ip du slave dans le fichier pg_hba.conf :
M.
$> cat >> pg_hba.conf << _EOF_
> host replication postgres 10.234.210.51/32 trust
> _EOF_
Création de données
Maintenant, nous pouvons démarrer le serveur maître, histoire de vérifier que l'archivage fonctionne bien. Nous allons créer une base, y ajouter une table, dans laquelle nous allons insérer un million de lignes pour que des journaux de transactions soient archivables.
M.
$> chown -R postgres.postgres /usr/local/pgsql/data/
$> /usr/local/pgsql/bin/pg_ctl -U postgres -D /usr/local/pgsql/data start
$> /usr/local/pgsql/bin/createdb b1
$> /usr/local/pgsql/bin/psql b1
psql (9.2.2)
Type "help" for help.
b1=# CREATE TABLE t1 (c1 integer);
CREATE TABLE
b1=# INSERT INTO t1 SELECT generate_series(1, 1000000);
INSERT 0 1000000
b1=# \q
Mise en place de la "stucture" du slave
Maintenant nous allons faire la base du serveur slave en créant un backup des données sur le master puis en les transférant sur le slave.
Pour commencer il faut créer le répertoire vide sur le slave :
S.
mkdir /usr/local/pgsql/data && chown postgres.postgres /usr/local/pgsql/data
Ensuite on s'attaque au contenu :
M.
$> /usr/local/pgsql/bin/psql -U postgres -c "SELECT pg_start_backup('my_backup', true)" postgres
pg_start_backup
-----------------
0/6000020
(1 row)
$> rsync -a /usr/local/pgsql/data/ 10.234.210.51:/usr/local/pgsql/data/ --exclude postmaster.pid
root@10.234.210.51's password:
$> /usr/local/pgsql/bin/psql -U postgres -c "SELECT pg_stop_backup();" postgres
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
pg_stop_backup
----------------
0/60000A8
(1 row)
Configuration du Slave : "Be Ready !"
Nous allons légèrement modifier les paramètres sur le slave et faire en sorte que le serveur slave puisse prendre le relais après le failover.
S.
$> cd /usr/local/pgsql/data/
$> sed -i -e 's/10.234.210.51/10.234.210.50/' pg_hba.conf
$> sed -i -e 's/master.conf/slave.conf/' postgresql.conf
$> mv master.conf slave.conf
$> sed -i -e 's/10.234.210.50/10.234.210.51/' slave.conf
$> cat >> slave.conf << _EOF_
>
> hot_standby = on
> _EOF_
Démarrage de la Replication
Il faut créer un fichier de "recovery", c'est essentiel à la streaming replication :
S.
$> mkdir /usr/local/pgsql/archive && chown postgres.postgres /usr/local/pgsql/archive
$> emacs recovery.conf
# Note that recovery.conf must be in $PGDATA directory.
# Specifies whether to start the server as a standby. In streaming replication,
# this parameter must to be set to on.
standby_mode = 'on'
# Specifies a connection string which is used for the standby server to connect
# with the primary.
primary_conninfo = 'host=10.234.210.50 port=5432 user=postgres'
# Specifies a trigger file whose presence should cause streaming replication to
# end (i.e., failover).
trigger_file = '/tmp/stopstandby'
# Specifies a command to load archive segments from the WAL archive. If
# wal_keep_segments is a high enough number to retain the WAL segments
# required for the standby server, this may not be necessary. But
# a large workload can cause segments to be recycled before the standby
# is fully synchronized, requiring you to start again from a new base backup.
restore_command = 'cp /usr/local/pgsql/archive/%f "%p"'
La ligne trigger ici est très imporante. Si vous créez le fichier '/tmp/stopstandby' alors le serveur slave ne sera plus en standby. \
Il ne faut donc pas que ce fichier existe('/tmp/stopstandby') lorsque vous mettez en place la streaming replication. Il ne faudra le créer qu'après, si le master est en erreur.
Voilà la configuration est terminée, il vous reste qu'à démarrer le serveur de standby(slave), et la streaming réplication démarrera :
$> chown -R postgres.postgres /usr/local/pgsql/data/
$> /usr/local/pgsql/bin/pg_ctl -U postgres -D /usr/local/pgsql/data start
Vous pouvez maintenant vérifier que tout est ok avec la commande :
M. $> ps -ef | grep sender
postgres 6879 6831 0 10:31 ? 00:00:00 postgres: wal sender process postgres 10.234.210.50(44663) streaming 0/2000000
S. $> ps -ef | grep receiver
postgres 6878 6872 1 10:31 ? 00:00:01 postgres: wal receiver process streaming 0/2000000
Failover
Dans cette partie nous allons tester et donc détailler les processus à mettre en place en cas de problème sur le master.
Simuler un arrêt du master
Pour simuler un problème sur le master, vous n'avez qu'à couper le postgres :
M.
$> /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data stop
WakeUP du slave
Une fois que c'est fait pour que le slave sorte de son état de standby, vous n'avez qu'à créer le fichier trigger expliquer plus haut :
S.
$> touch /tmp/stopstandby
Pour savoir rapidement où créer ce fichier, vous n'avez qu'à utiliser la commande suivante sur le serveur slave :
$> cat /usr/local/pgsql/data/recovery.conf | grep trigger_file
La création de ce fichier aura pour effet de sortir le slave du mode standby, c'est a dire qu'il ne sera plus en read-only.
De plus le fichier "recovery.conf" se transformera en "recovery.done" pour ne pas qu'il tente de repasser en slave.
Apres la sortie du mode standby le fichier trigger ('/tmp/stopstandby') est automatiquement supprimé.
Remise en état Master/Slave
Pour remettre en état le Master/Slave, nous allons simplement faire en sorte que l'ancien Master devienne Slave, et nous allons faire de l'ancien Slave le nouveau Master.
Contrairement à l'état initiale nous allons donc faire en sorte d'avoir :
Master : 10.234.210.51 => M.
Slave : 10.234.210.50 => S.
Dans une réplication, peu importe qui est le Master, ou qui est le Slave, l'important c'est qu'on est les deux, que l'on soit capable de les identifier, et qu'ils soient fonctionnels.
On va préparer le nouveau slave à accueillir les données :
S.
$> mv /usr/local/pgsql/data /usr/local/pgsql/data.old
$> mkdir /usr/local/pgsql/data && chown -R postgres.postgres /usr/local/pgsql/data
Puis on va créer un backup des données sur le nouveau Master :
M.
$> /usr/local/pgsql/bin/psql -U postgres -c "SELECT pg_start_backup('my_backup', true)" postgres
pg_start_backup
-----------------
0/12000020
(1 row)
$> rsync -a /usr/local/pgsql/data/ 10.234.210.50:/usr/local/pgsql/data/ --exclude postmaster.pid
root@10.234.210.50's password:
$> /usr/local/pgsql/bin/psql -U postgres -c "SELECT pg_stop_backup();" postgres
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
pg_stop_backup
----------------
0/12000178
(1 row)
On modifie les configurations avec l'ip du nouveau slave, et on prépare le recovery pour que la streaming réplication redémarre :
S.
$> cd /usr/local/pgsql/data/
$> sed -i -e 's/10.234.210.50/10.234.210.51/' pg_hba.conf
$> mv recovery.done recovery.conf
$> sed -i -e 's/10.234.210.50/10.234.210.51/' recovery.conf
$> sed -i -e 's/10.234.210.51/10.234.210.50/' slave.conf
On relance le slave :
$> /usr/local/pgsql/bin/pg_ctl -U postgres -D /usr/local/pgsql/data start
Evidemment votre slave est en read-only et désormais en standby ;)
Vous pouvez maintenant vérifier que tout est ok avec la commande :
M. $> ps -ef | grep sender
postgres 6879 6831 0 10:31 ? 00:00:00 postgres: wal sender process postgres 10.234.210.50(44663) streaming 0/2000000
S. $> ps -ef | grep receiver
postgres 6878 6872 1 10:31 ? 00:00:01 postgres: wal receiver process streaming 0/2000000