8 / 2010

Réplication de tables PostgreSQL avec londiste

Je me suis mis un petit mx secondaire, ce n'est pas vraiment utile dans mon cas, mais j'ai des pulsions d'admin en ce moment je ne peux pas résister.

Et j'ai même fait pire en termes de simplicité, ma liste d'adresse n'étant pas très mouvante répliquer les tables postgresql est vraiment inutile dans mon cas. Mais le sujet m'intéresse alors allons y.

Les besoins :

  • master/slave (pas d'écriture sur la table répliquée)
  • Quand le master tombe (même pendant plusieurs jours) on doit pouvoir lire sur le slave
  • Quand le slave tombe, ça ne doit pas empêcher d'écrire sur le master

Donc il me fallait un système asynchrone, facile à utiliser et à resynchroniser quand un des deux serveurs tombe.

En gros pour répondre à ces besoins il y a deux systèmes de réplication PostgreSQL, slony et londiste. Slony m'avait l'air assez complexe à utiliser, et la resynchronisation après une panne risquée, londiste fait partie des Skytools un ensemble d'utilitaires en python, dont PGQ un système de réplication des résultats basés sur les trigger postgresql.

Quand on écrit sur le master, via les trigger ça met dans une queue, et un daemon s'occupe de donner les résultat de la queue aux slaves qui le demandent (tout ce fait à travers postgresql). Du coup en termes de réseau, il faut juste que les slaves aient accès aux tables répliquées.

Une utilisation sympa de londiste, c'est pour migrer de version postgresql. Si la base est grosse, faire un pg_dump/pg_restore prend du temps et si l'application est critique et fait beaucoup d'écritures, le dump est déjà obsolète quand il est fait. Pour éviter d'avoir à bloquer l'application pendant la migration, on peut faire une réplication (vers un slave avec la nouvelle version de PostgreSQL), qui prendra son temps mais une fois finie, on aura deux base identiques (à quelques secondes près). Alors on peut faire une basculement sur le slave et la coupure du service ne dure que quelques minutes.

Moi je veux juste répliquer une table (les alias de postfix), entre mon mx primaire et secondaire, avec deux serveurs sous FreeBSD, il faut installer skytools (sur les deux) :

[root] % portmaster databases/skytools

Ensuite créer un utilisateur (ou utiliser un déjà existant).

[pgsql] % createuser -S -D -R -P replication
Enter password for new role: rirififiloulou

On va répliquer la table alias de la base postfix, il faut créer la base et la table sur le slave et donner des droits sur le master. La base et la table peuvent avoir des noms différents sur les deux serveurs (ce n'est pas le cas ici).

[pgsql] % psql -d postfix
postfix# GRANT ALL ON alias TO replication;

Autoriser la connexion du slave vers le master. (à faire sur le master naturellement)

[root] % $EDITOR ~pgsql/data/pg_hba.conf
host    postfix    replication    IP_SLAVE/32    md5

Faites en sorte que le firewall laisse passer tout ça

Depuis le slave, créez la base et copiez le schéma de la table, on en profite pour rajouter une entrée dans le ~pgsql/.pgpass pour ne plus avoir besoin du password :

[pgsql] % cat >> .pgpass << EOF
MASTER:PORT:postfix:replication:rirififiloulou
EOF
[pgsql] % createdb postfix
[pgsql] % pg_dump -h MASTER -p PORT -U replication -d postfix -t alias -s | pgsql -d postfix

Ensuite créez la configuration du daemon pgq (à lancer sur le master, avec l'user pgsql), dans ~pgsql/postfix.ini

[pgqadm]
job_name = postfix_ticker

db = dbname=postfix
# how often to run maintenance [seconds]
maint_delay = 600

# how often to check for activity [seconds]
loop_delay = 0.1
logfile = ~/%(job_name)s.log
pidfile = ~/%(job_name)s.pid

Install de pgq sur la base et lancement du daemon :

[pgsql] % pgqadm.py postfix.ini install
[pgsql] % pgqadm.py postfix.ini ticker -d

Maintenant sur le slave, configuration de londiste (toujours avec l'user pgsql), postfix.ini

[londiste]
job_name = postfix_alias

provider_db = dbname=postfix port=PORT host=MASTER user=replication
subscriber_db = dbname=postfix port 5432 host=localhost
pgq_queue_name = postfix
logfile = ~/%(job_name)s.log
pidfile = ~/%(job_name)s.pid

On installe (qu'une seule fois) et on lance le daemon londiste (toujours depuis le slave) :

[pgsql] % londiste.py postfix.ini provider install
[pgsql] % londiste.py postfix.ini subscriber install
[pgsql] % londiste.py postfix.ini replay -d

Ensuite on peut ajouter des tables à la réplication (aussi des séquences avec add-sec) :

[pgsql] % londiste.py postfix.ini provider add public.alias
[pgsql] % londiste.py postfix.ini subscriber add public.alias

Et voilà la réplication lancée, il y a un tas d'autres commandes sympa, notamment repair/compare qui permet une resynchronisation.

% man pgqadm
% man londiste

Et un tuto bien sympa ainsi qu'une publication dans GNU/Linux magazine.

installation serveur git avec gitosis, lighttpd et cgit

date
11 / 8 / 2010
comments
1

Pour faire revivre un peu ce blog déserté (en particulier par moi même), un petit billet sur la config de mon serveur git.

Les dépôts git peuvent se trouver sous deux formes :

  • Une forme pour le développeur/contributeur, c'est ce qu'on obtient avec git clone
  • Une forme pour le serveur, que l'on a avec git clone --bare

En fait on clone toujours depuis un dépôt bare, de la même manière on push sur un dépôt bare.

% git clone --bare git://git.wmfs.info/wmfs.git
Initialized empty Git repository in /tmp/wmfs.git/
remote: Counting objects: 5011, done.
remote: Compressing objects: 100% (1096/1096), done.
remote: Total 5011 (delta 3909), reused 5011 (delta 3909)
Receiving objects: 100% (5011/5011), 844.06 KiB, done.
Resolving deltas: 100% (3909/3909), done.

% ls wmfs.git
branches  config  description  HEAD  hooks  info  objects  packed-refs  refs

% git clone ./wmfs.git
Initialized empty Git repository in /tmp/wmfs/.git/

% ls wmfs
CMakeLists.txt  cmake_uninstall.cmake.in  COPYING  rc  README  src  TODO  wmfs.1  wmfs.desktop  wmfs.doxygen.in  wmfsrc.in

Dans ce dépôt bare il y a tout un tas de choses, notamment les hooks qui permettent de lancer des commandes avant/après un push et tout autre sortes d'événements.

Git permet de cloner/pusher depuis/vers des dépôts bare d'au moins trois manières différentes.

  • Par ssh
  • Par le protocole git (sur le port 9418 par défaut)
  • Par http

Pour ssh, si la sécurité nécessite d'avoir des droits par utilisateurs on peut utiliser gitosis qui va créer un utilisateur git avec un ~git/.ssh/authorized_keys qui limite l'accès aux utilisateurs.

L'install et l'utilisation est simplisme, voyez la doc de gitosis pour faire des choses plus velues :

% make -C /usr/ports/devel/py-gitosis install
% sudo -H -u git gitosis-init < /votre/clef/ssh/publique/id_rsa.pub
% git clone git@serveur:gitosis-admin.git
% cd gitosis-admin    
% >> gitosis.conf
[repo foo]
daemon = yes
^D
% git commit -a -m "Ajout depot foo" && git push
% mkdir /tmp/foo && cd /tmp/foo
% echo "foo" > bar
% git init
% git add bar
% git remote add origin git@serveur:foo.git
% git push origin master:refs/heads/master

Et voilà votre premier dépot git.

Bon là par ssh, seuls les utilisateurs autorisés pourront cloner. Il nous faut un moyen public pour permettre à quiconque de cloner.

Par le protocole git c'est très facile :

% >> /etc/rc.conf
git_daemon_enable="YES"
git_daemon_directory="--base-path=/usr/local/git/repositories"
^D
% /usr/local/etc/rc.d/git_daemon start
# Testons par le protocole git
% git clone git://serveur/foo.git

Mais si vous voulez offrir un service un peu plus complet comme github, il faut permettre le clone par http parce que si les utilisateurs sont derrière un firewall nazi qui laisse passer que le port 80 sortant ils ne pourrons pas contribuer à vos softs.

Pour celà il suffit de configurer le serveur web qui sert /usr/local/git/repositories, ainsi nos amis nazis pourront faire du git clone http://serveur/foo.git.

Allons y avec lighttpd puisqu'il faut faire un choix.

% >> /usr/local/etc/lighttpd.conf
$HTTP["host"] == "serveur" {
    server.document-root = "/usr/local/git/repositories"
}
% /usr/local/etc/rc.d/lighttpd reload
% git clone http://serveur/foo.git
# Arf ça marche pas, permission denied
% cd /usr/local/git/repositories
% chmod o+rx foo.git
% git clone http://serveur/foo.git
# Arf, ça marche toujours pas, il me cause d'un git update-server-info
% cd /usr/local/git/repositories/foo.git
% git update-server-info
% cp hooks/post-update.sample hooks/post-update
% git clone http://serveur/foo.git
\o/

Bon et pour faire un peu plus "pro", il nous faut un gitweb celui par défaut en perl me plait pas trop, je préfère nettement cgit

% make -C /usr/ports/devel/cgit
% >> /usr/local/etc/cgitrc
clone-prefix=git://serveur
virtual-root=/
snapshots=tar.gz tar.bz2

repo.url=foo.git
repo.path=/usr/local/git/repositories/foo.git
^D

Comme on est malins, on va le mettre sur le même virtualhost. Et on va faire même encore plus fort, on va le mettre le gitweb ET le clone sur http://serveur/foo.git.

Pour ça il faut matcher le client git dans la conf lighttpd :

$HTTP["host"] = "serveur" {
    server.document-root = "/usr/local/git/repositories"
    # Et si ce n'est pas le client git
    $HTTP["useragent"] !~ "^git" {
         server.document-root = "/usr/local/www/cgit/"
         index.file.name = ("cgit.cgi")
         cgi.assign ("cgi.git" => "")
         server.error-handler-404 = "cgit.cgi"
         url.rewrite-once = ("^/([^?/]+/[^?]*)?(?:\?(.*))?$" => "/cgit.cgi?url=$1&$2")
     }
}

(D'ailleurs je me demande si y'a pas un morceau de code qui fait ça tout seul dans cgit... à voir).

Voilà, mettez vos petits softs sur le nainternet et attendez les contributions ;)

Si vous voulez aller encore plus loin, mettez un bot irc qui parle quand ça push sur le dépôt git.