Thématiques principales

samedi 8 février 2020

Postgres : Cluster Citus

Dans le logiciel, la sauvegarde des données métiers et applicatives est une problématique incontournable. Tôt où tard il faudra stocker et choisir une stratégie pérenne dans le temps afin de garantir que les données ne puissent être perdu tout en garantissant un accès facile et rapide à ces dernières

Pour cela deux techniques peuvent être mise en place, la mise en haute disponibilité [HD] qui permet de garantir que les données sont conservés intégré dans le temps et accessible malgré une panne éventuelle et le load balancing [LB] qui lui va permettre de garantir l’accessibilité dans un temps limité de ces données qu’elle qu’en soit le contexte d’exploitation

En terme de stockage, un outil couramment utilisé est postgres [postgres]. autour de celui ci, différentes outils et stratégies [sev-approach] permettent la mise en oeuvre de la haute dispo, pouvant éventuellement être complété par un système de load balancing. Ces approches sont portées entre autre par:
  • citus [citus] (anciennement [pg_shard])
  • pgpool-II [pgpool, pgpool-II]
  • postgres-XL [postgresxl]
  • repmgr [repmgr]
Mais il en existe bien d’autres encore comme :
  • postgresxc [postgresxc]
  • bucardo [bucardo]
  • Cluster Control [clustercontrol]
  • ...

Nous ne pourrons tout essayer ni comparer mais intéressons nous au moins au 4 premiers et du moins dans le cadre de cet article au premier, Citus [citus]!

Citus


Principe


Citus est une solution permettant la mise en oeuvre de la HD. Pour cela, Citus va mettre en oeuvre un cluster de machine virtuelle ou physique dans lesquelles vont vivre des instances postgres. On distingue alors une instance particulière qui sera le maître, et le reste sont des workers.

Comme avec une base de données classique, le maître sert d’interlocuteur principa pour la réalisation des tâches de gestion de la base de données. Les workers ont pour rôle à l’inverse de gérer de façon complètement caché la logique de stockage ici à proprement parler. Dans le cadre de citus, la stratégie implémentée est du sharding [sharding]. Encore un terme vulgaire derrière lequel se cache un concept en réalité très simple de stratégie de répartition des données au travers du cluster. Pour y voir plus clair je vous invite à lire cet article de Octo [shard-octo] mais ce qu’il faut en retenir c’est que cette approche permet de répartir les données sur l’ensemble du cluster tout en garantissant un certain degré de duplication afin de prévenir des crash éventuels.

Pour faire exemple, considérons une base de données applicatives qu’il va falloir déployer en haute dispo. On ne va pas être ambitieuse on ne va considérer qu’une seule table. Pour ce qui sera de notre cluster, comme nous ne disposons pas d’un parc de machine à dispo, nous allons faire cela avec docker. Cela va largement nous simplifier la vie puisqu une image docker de citus est disponible sur le docker-hub [citus-docker].

Mise en oeuvre et preparatif de l'infrastructure


On va donc mettre en oeuvre un docker-compose très simple identifiant deux types de services: le master et les workers:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
version: '2.0'

services:
  master:
    image: 'citusdata/citus:nightly'
    ports: 
      - "5432:5432"
     
  worker:
    image: 'citusdata/citus:nightly'


Jusque la tout va bien. il reste alors à déployer les services et à les scaler:
1
2
3
4
5
6
7
8
$ docker-compose -f tc-citus.yml up -d
Creating postgres-cluster_master_1 ... done
Creating postgres-cluster_worker_1 ... done
$ docker-compose -f tc-citus.yml scale worker=3
WARNING: The scale command is deprecated. Use the up command with the --scale flag instead.
Starting postgres-cluster_worker_1 ... done
Creating postgres-cluster_worker_2 ... done
Creating postgres-cluster_worker_3 ... done


Ok on vérifie que l’on a notre maitre et nos esclaves:

1
2
3
4
5
6
$ docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                   PORTS                    NAMES
dcd27f9d1338        citusdata/citus:nightly        "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes (healthy)   5432/tcp                 postgres-cluster_worker_3
b6f41db0b6e8        citusdata/citus:nightly        "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes (healthy)   5432/tcp                 postgres-cluster_worker_2
9db7cdff6012        citusdata/citus:nightly        "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes (healthy)   5432/tcp                 postgres-cluster_worker_1
4b663db24c09        citusdata/citus:nightly        "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes (healthy)   0.0.0.0:5432->5432/tcp   postgres-cluster_master_1


Ok parfait maintenant avant de pouvoir utiliser tout ce beau petit monde il va falloir les faire se parler entre eux. Pour cela il faut enregistrer les worker auprès du maître:


1
2
3
4
5
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT * from master_add_node('postgres-cluster_worker_1', 5432);"
 master_add_node
-----------------
               1
(1 row)


Ici on à un peut feinté au lieu de se connecter au conteneur docker maitre en passant par un /bin/sh pour ensuite exécuter la commande psql, on a réalisé celle ci directement. On fait alors la même commande avec le worker 2 et 3.

Pour verifier que tout les noeuds du cluster sont bien enregistrer, on pourra realiser:

1
2
3
4
5
6
7
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT * FROM master_get_active_worker_nodes();"
         node_name         | node_port
---------------------------+-----------
 postgres-cluster_worker_1 |      5432
 postgres-cluster_worker_2 |      5432
 postgres-cluster_worker_3 |      5432
(3 rows)


Creation du schema


Bien maintenant on va pouvoir créer notre table [postgres] et y injecter des données:

1
2
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "CREATE TABLE  utilisateurs ( id SERIAL, email varchar( 60 ) NOT NULL , mot_de_passe varchar( 32 ) NOT NULL , nom varchar( 20 ) NOT NULL , date_inscription date NOT NULL);"
CREATE TABLE


Notes: attention à ce que votre table ne contienne pas de clef primaire ou d'éléments définis comme unique, sinon citus ne pourra pas la prendre en charge.
Voila on à créer notre table mais avant d’y injecter des données, il va nous falloir d’abord la faire prendre en charge par citus pour que celui ci la propage au travers du cluster!

1
2
3
4
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT master_create_distributed_table('utilisateurs', 'id', 'hash');"
 master_create_distributed_table
---------------------------------
(1 row)


Voila nous venons de lui dire de la prendre en charge mais pas comment. Entre autre nous voudrions que citus déploie notre table au sein de 3 shards différents (un dans chaque noeud en somme) tout en garantissant que 2 copies de chaque données sera toujours en ligne. Pour cela:

1
2
3
4
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT master_create_worker_shards('utilisateurs', 3, 2);"
 master_create_worker_shards
-----------------------------
(1 row)


Et voilà pour vérifier tout cela:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT * from pg_dist_shard_placement;"
 shardid | shardstate | shardlength |         nodename          | nodeport | placementid
---------+------------+-------------+---------------------------+----------+-------------
  102008 |          1 |           0 | postgres-cluster_worker_1 |     5432 |       1
  102010 |          1 |           0 | postgres-cluster_worker_1 |     5432 |       6
  102008 |          1 |           0 | postgres-cluster_worker_2 |     5432 |       2
  102009 |          1 |           0 | postgres-cluster_worker_2 |     5432 |       3
  102009 |          1 |           0 | postgres-cluster_worker_3 |     5432 |       4
  102010 |          1 |           0 | postgres-cluster_worker_3 |     5432 |       5
(6 rows)

Voila on voit donc que nous disposons de 3 shard dupliqués une fois (donc en doublons) déployer de façon homogène sur le cluster de noeud.

Utilisation de la base

Maintenant nous pouvons utiliser cette table en réalisant les insertions et select sur le service maître comme on le ferait pour une base postgres classique.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "INSERT INTO utilisateurs (id, email, mot_de_passe, nom, date_inscription) VALUES (1,'moi@la.com', MD5('tutu'), 'BaMoi', NOW());"
INSERT 0 1

$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "INSERT INTO utilisateurs (id, email, mot_de_passe, nom, date_inscription) VALUES (2,'toi@pas.la', MD5('utut'), 'EtToi', NOW());"
INSERT 0 1

$ docker exec -it postgres-cluster_master_1 psql -U postgres -c "SELECT * FROM utilisateurs;"
 id |   email    |           mot_de_passe           |  nom  | date_inscription
----+------------+----------------------------------+-------+------------------
  2 | toi@pas.la | 5c587bce24972d7a92e6cf1200f2001a | EtToi | 2020-02-07
  1 | moi@la.com | bdb8c008fa551ba75f8481963f2201da | BaMoi | 2020-02-07
(2 rows)


Voila pour Citus, dans les prochains articles nous tâcherons de nous intéresser au 3 suivants (pgpool-II, repmgr et postgres-XL) en essayant d’adopter une approche similaire afin de voir ce qui sera le plus user-friendly!

Références:

[sev-approach] https://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling
[HD] https://fr.wikipedia.org/wiki/Haute_disponibilit%C3%A9
[LB] https://fr.wikipedia.org/wiki/R%C3%A9partition_de_charge
[pg_shard] https://www.citusdata.com/product/pg_shard
[citus] https://www.citusdata.com/download/
[citus-docker] https://hub.docker.com/r/citusdata/citus
[pgpool] www.pgpool.net
[pgpool-II] https://wiki.postgresql.org/wiki/Pgpool-II
[postgresxl] https://www.postgres-xl.org/
[bucardo] https://bucardo.org/
[postgresxc] https://postgresxc.fandom.com/wiki/Postgres-XC_Wiki
[clustercontrol] https://severalnines.com/product/clustercontrol/for_postgresql
[repmgr] https://wiki.postgresql.org/wiki/Repmgr
[sharding] https://en.wikipedia.org/wiki/Shard_(database_architecture)
[shard-octo] https://blog.octo.com/sharding/
[postgres] https://un-est-tout-et-tout-est-un.blogspot.com/2017/09/postgres-commandes-de-base.html

Aucun commentaire:

Enregistrer un commentaire