Cela fait quelques mois que j’utilise MongoDB au boulot dans le cadre du développement d’applications Web et ça marche plutôt pas mal. Dans cet article, on va voir comment mettre en place une architecture redondée qui permettra de profiter des joies de la scalabilité horizontale : il suffit d’ajouter des serveurs à la volée et comme par magie notre application est capable d’absorber plus de charge.

Note : je préfère travaillé avec le paquet 10gen qui est celui qui est le plus à jour. Vous trouverez la procédure d’installation ici.

$ mongo --version

MongoDB shell version: 1.8.2

On commence par mettre en place la réplication

Afin d’assurer la redondance et la haute disponibilité, MongoDB possède une fonctionnalité appelée « replica set » qui permet aux données d’être dupliquées de manière transparente pour le développeur. Le concept est de créer un groupe de serveur (set) qui possédera un nœud principal (primary) et n serveurs de backup (secondary). Si a un moment donné le nœud principal est inopérant, automatiquement l’un des serveurs de backup deviendra serveur primaire.

On commence par arrêter le daemon :

# /etc/init.d/mongodb stop

Puis on crée les dossiers où seront stockées les bases :

# mkdir /data/r0
# mkdir /data/r1
# mkdir /data/r2

Puis on lance les instances :

# mongod --port 27017 --dbpath /data/r0 --replSet foo
# mongod --port 27018 --dbpath /data/r1 --replSet foo
# mongod --port 27019 --dbpath /data/r2 --replSet foo

Ici, on a appelé notre replica set « foo ». Libre à vous de l’appeler comme vous voulez et de mettre autant de serveurs que souhaité.

Ensuite on va configurer le replica set. On se connecte :

$ mongo

Puis on écris notre configuration :

> cfg = {
_id : "foo",
members : [
{ _id : 0, host : "localhost:27017"},
{ _id : 1, host : "localhost:27018"},
{ _id : 2, host : "localhost:27019"},
] }

Et on initialise :

> rs.initiate(cfg)
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}

Après quelques secondes, l’invite changera lorsque vous taperez « entrée » :

foo:PRIMARY>

C’est bon : La réplication est active. A partir de maintenant vous pouvez connecter votre application sur l’instance MongoDB lancée sur le port 27017. Les données en insertions seront automatiquement répliquées sur les autres instances. Cependant, le failover n’est pas encore en place. Même si on constate que l’élection automatique du master est en place, notre application ne peut pas basculer seule d’une instance vers une autre.

Nous allons donc utiliser le sharding pour y parvenir de manière transparente pour l’application.

Mise en place du sharding

Une infrastructure typique MongoDB consiste en plusieurs replica set ainsi que de plusieurs instances mongo dédiées à la configuration du sharding. Enfin, il faudra également une instance mongos par serveur d’application. Mongos n’est ni plus ni plus ni moins qu’un loadbalancer servant de point d’entrée/sortie pour les applications.

Pour le vocabulaire, chaque replica set est aussi appelé shardn. Le sharding consiste en une distribution du stockage des données sur les différentes instances Mongo au sein de ce shard. Chaque machine stock donc un sous ensemble des données. Souvent, c’est la clef (id) de chaque document qui permet de définir le serveur qui stockera l’information.

Concrètement, il nous faudra simplement arrêter nos trois instances et leur rajouter le paramètre suivant permettant d’activer le sharding :

--shardsvr

Ensuite, on lance nos serveurs de configuration :

# mkdir /data/configdb0
# mkdir /data/configdb1
# mkdir /data/configdb2
# mongod --port 27020 --configsvr --dbpath /data/configdb0
# mongod --port 27021 --configsvr --dbpath /data/configdb1
# mongod --port 27022 --configsvr --dbpath /data/configdb2

C’est ce serveur qui va stocker les informations utiles au bon fonctionnement du sharding. On lance ensuite le routeur :

# mongos --port 27023 --configdb localhost:27020,localhost:27021,localhost:27022

puis on se connecte sur le routeur :

$ mongo --port 27023

Puis on ajoute les nœud au shard :

> use admin
> db.runCommand({addshard : "foo/localhost:27017,localhost:27018,localhost:27019"})

Vous noterez la syntaxe particulière qui commence par le nom du replica set (foo) suivi d’un slash et de la liste des serveurs. Si tout c’est bien passé vous devriez avoir ce message :

{ "shardAdded" : "foo", "ok" : 1 }

Testons notre infrastructure

Par défaut, le sharding n’est pas activé. On peut l’activer au niveau d’une base de donnée ou bien au niveau d’une collection. Ici nous activons le sharding sur toute la base de données.

> db.runCommand({enablesharding: "test"})

Puis nous définissons la clef à utiliser pour le sharding :

> db.runCommand({shardcollection: "test.hits", key:{"_id":1}})

On va commencer par charger un peu en données. J’ai pris ici l’exemple de hits de statistiques

> use test
> for(i=0;i<1000000;i++) {
db.hits.insert({tag: "search", ctx : ["blah"] });
db.hits.insert({tag: "video", ctx : [parseInt(Math.random() * 100), "play"] });
db.hits.insert({tag: "video", ctx : [parseInt(Math.random() * 1000), "play"] });
}

On insère donc 3 millions de hits en base avec notamment des tags « video » dont l’identifiant est le premier élément du tableau de contexte (ctx). L’insertion va prendre quelques minutes et on pourra observer l’utilisation des ressources. On s’aperçoit que les traitement sont bien paralléliser et que l’utilisation mémoire est constante

Une fois fini on peut constater que les différentes bases MongoDB utilisent 14Go. Attention : MongoDB pré-alloue les fichiers de stockage : 64Mo, 128Mo, 256Mo, 512Mo etc. jusqu’à créer des fichiers de 2Go. Les explications ici.

On peut avoir les statistiques de la collection :

> db.hits.stats()

Conclusion

La mise en place du sharding et de la réplication est assez simple. Ainsi on peut rapidement mettre en place une infrastructure fiable. Pour augmenter la capacité de montée en charge il suffira d’ajouter des des réplica set au shard. MongoDB s’occupera automatiquement de répartir le stockage des données et l’exécution des différentes requêtes. Pour augmenter la fiabilité, on pourra également augmenter le nombre de noeud dans chaque replica set. Dans un prochain article je montrerais l’utilisation de map reduce.