Movim: Essai d’installation

Dernièrement, j’ai décidé de voir ce que propose les réseaux sociaux alternatifs libres. Ayant eu l’occasion d’assister à une conférence sur Movim et de d’échanger rapidement avec son concepteur, je me suis donc décidé à tenter l’installation complète. Pour cela, j’ai choisi le serveur xmpp Prosody, qui embarque un serveur Bosh nécessaire pour traduire les requêtes entre les protocoles http et xmpp. Nous allons donc commencer par là.

Prosody

L’installation s’effectue via le gestionnaire de paquets (ici, aptitude) :

aptitude install prosody

Une fois Prosody installé, on se tourne vers la configuration de la bête en modifiant le fichier /etc/prosody/prosody.cfg.lua. Les modifications intéressantes sont les suivantes, à adapté selon vos besoins bien sûr.

"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
allow_registration = false;

VirtualHost "domain.com"
        enabled = true

cross_domain_bosh = true

Le mode d’authentification doit rester en internal_plain, le changer empêche de se logger correctement. Nous pouvons dès à présent redémarrer le service pour que la configuration soit appliquer :

service prosody restart

A présent, ajoutons un utilisateur :

prosodyctl adduser user@domain.com

Pour supprimer un utilisateur si besoin :

prosodyctl deluser user@domain.com

Les comptes utilisateurs relatifs aux différents domaines sont stockés dans /var/lib/prosody/. Voilà pour la partie relative à Prosody, à ce stade, il est normalement possible de se connecter à un salon de discussion xmpp en utilisant l’utilisateur ajouté tout à l’heure.

Movim

Nous passons ensuite à l’installation de Movim à proprement parler. J’ai d’abord essayer de récupérer directement les sources à partir de la branche master, mais un fichier de dépendance était manquant et empêchait le fonctionnement de l’application. Je me suis donc tourné vers la version 7.2.

On change ensuite le propriétaire de l’arborescence  du dossier movim pour que celui-ci soit utilisable par Apache.

chown -R www-data:www-data movim

Il est maintenant temps de modifier la configuration de movim, soit le fichier movim/config/conf.php, et notamment les champs suivants :

'environment' => 'production',
'dbType' => 'mysql',
'dbUsername' => 'movim',
'dbPassword' => 'password',
'dbHost' => 'localhost',
'dbPort' => '3306',
'dbName' => 'movim',
'boshUrl' => 'http://domain.com:5280/http-bind',

On aura bien entendu pris soin de créer au préalable l’utilisateur et la base de donnée MySQL qui convient.

mysql -p -u root
  mysql> CREATE DATABASE movim;
  mysql> GRANT ALL PRIVILEGES ON `movim`.* TO 'movim'@'localhost' \
    IDENTIFIED BY 'password';

Apache

Pour rendre tout cela accessible, il nous faut maintenant configurer Apache. Soit le fichier movim dans /etc/apache2/sites-available/.

<VirtualHost *:80>
      ServerAdmin webmaster@localhost
      ServerName movim.domain.com
      DocumentRoot /var/www/movim
      <Directory />
              Options FollowSymLinks
              AllowOverride None
      </Directory>
      <Directory /var/www/movim>
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from ALL
      </Directory>
</VirtualHost>

Puis les commandes habituelles :

a2ensite movim
service apache2 restart

Suite et fin

Movim est désormais accessible sur movim.domain.com. Un petit tour dans l’interface d’admin du nœud est nécessaire pour s’assurer que tout fonctionne correctement et initialiser la base de donnée. Les identifiants de connexion sont ceux configurés dans le fichier de configuration de movim.

Résultat, nous pouvons nous connecter à notre nœud movim grâce au compte xmpp que nous avons créé précédemment. L’ensemble est bien vide, puisque nous sommes seuls sur le nœud. En essayant diverses fonctionnalités, j’obtiens de temps à autre des messages d’erreur sur la gauche de mon écran. Comme je n’utilise pas la dernière version, je n’ai pas vraiment creusé pour savoir si cela provient de movim ou de mon serveur Prosody. J’arrive par contre à faire communiquer deux utilisateurs entre eux via le chat intégré.

Bilan mitigé, « I have mixed feelings » comme disent les anglais. Movim a un potentiel certain, néanmoins, l’installation de sa propre instance complète Movim et serveur xmpp n’est pas de tout repos. La procédure d’installation est très limitée en ce qui concerne la partie xmpp. J’avais pourtant le souvenir d’une documentation beaucoup plus complète des différentes solutions disponibles lors de mon premier passage sur le wiki il y a de cela plusieurs mois. Je partage donc les quelques étapes que j’ai suivi pour l’installation de Movim. L’ensemble est plus ou moins complet et pourrait servir de base à toute personne cherchant à tester l’application.

Parsing mongostat data with Logstash

On my way to a complete MongoDB monitoring solution, I’ve been playing with mongostat to see what I can achieve with it. So I tested mongostat on a simple architecture made of two shards, each shard being a replica set composed of tree members. Of course, we also have tree configuration routers and one query router.

First, I discovered a few bugs in the tool when using it with the option discover. This parameter can be used to automatically retrieve statistics from all members of a replica set or a sharded cluster. Using it with version 2.4.9 of mongostat causes some other parameters to be ignored: rowcount and noheaders. So I dove in the code on Github to find that this bugs had been already corrected. We just need modifications to come to the stable version.

mongostat --host localhost:24000 --discover --noheaders -n 2 30 > mongostat.log

Here I’m connecting to my query router, asking mongostat to find other mongoDB instances by itself. Options noheaders and n don’t work at the moment but that’s not a problem. With this setup, I will receive logs every 30s.

There are two types of log: the ones coming from the mongos and the ones coming from the other.

localhost:21000        *0     *0     *0     *0       0     1|0       0   800m  1.04g    30m      0 local:0.0%          0       0|0     0|0   198b   924b    13 rs0  PRI   15:36:55
 localhost:21001        *0     *0     *0     *0       0     1|0       0   800m  1.01g    29m      0  test:0.0%          0       0|0     0|0   138b   359b     6 rs0  SEC   15:36:55
 localhost:21002        *0     *0     *0     *0       0     1|0       0   800m  1.01g    29m      0  test:0.0%          0       0|0     0|0   138b   359b     6 rs0  SEC   15:36:55
 localhost:21100        *0     *0     *0     *0       0     1|0       0   800m  1.05g    35m      0 local:0.0%          0       0|0     0|0   198b   924b    13 rs1  PRI   15:36:55
 localhost:21101        *0     *0     *0     *0       0     1|0       0   800m  1.01g    34m      0  test:0.0%          0       0|0     0|0   138b   359b     6 rs1  SEC   15:36:55
 localhost:21102        *0     *0     *0     *0       0     1|0       0   800m  1.01g    34m      0  test:0.0%          0       0|0     0|0   138b   359b     6 rs1  SEC   15:36:55
 localhost:24000         0      0      0      0       0       0                  174m     5m      0                                             2b    23b     2      RTR   15:36:55

Output from mongostat.

 

The last line coming from the mongos has empty fields. So we will need to deal with it when parsing the log. After having understood how mongostat works, it is now time to see if we can easily plug it in logstash. Let’s take a look at our logstash configuration file.

input {
  file {
    type => "mongostat"
    path => ["/path/to/mongostat.log"]
  }
}

We define where to find the log file.

 

filter {
  if [type] == "mongostat" {
    grok {
      patterns_dir => "./patterns"
      match => ["message", "%{HOSTNAME:host}:%{INT:port}%{SPACE}%{METRIC:insert}%{SPACE}%{METRIC:query}%{SPACE}%{METRIC:update}%{SPACE}%{METRIC:delete}%{SPACE}%{METRIC:getmore}%{SPACE}%{COMMAND:command}%{MONGOTYPE1}%{SIZE:vsize}%{SPACE}%{SIZE:res}%{SPACE}%{NUMBER:fault}%{MONGOTYPE2}%{SIZE:netIn}%{SPACE}%{SIZE:netOut}%{SPACE}%{NUMBER:connections}%{SPACE}%{USERNAME:replicaset}%{SPACE}%{WORD:replicaMember}%{SPACE}%{TIME:time}"]
    }
  }
  if [tags] == "_grokparsefailure" {
    drop { }
  }
  if [message] == "" {
    drop { }
  }
}

We apply filter on each message received if it comes from mongostat. If the message is empty or grok fails to parse it, we drop the log.

 

output {
  stdout { }
  elasticsearch_http {
    host => "127.0.0.1"
  }
}

Simple output. Logs are stored in Elasticsearch so that we can use Kibana to examine them later and are written to stdout for immediate debugging and verification.

Let’s consider a little bit the filter part. We give grok a directory for our personal patterns: ./patterns. This directory contains a file mongostat with the following patterns:

METRIC (\*%{NUMBER})|(%{NUMBER})
COMMAND (%{NUMBER}\|%{NUMBER})|(%{NUMBER})
SIZE (%{NUMBER}[a-z])|(%{NUMBER})
LOCKEDDB (%{WORD}\:%{NUMBER}%)
MONGOTYPE2 (%{SPACE}%{LOCKEDDB:lockedDb}%{SPACE}%{NUMBER:indexMissedPercent}%{SPACE}%{COMMAND:QrQw}%{SPACE}%{COMMAND:ArAw}%{SPACE})|%{SPACE}
MONGOTYPE1 (%{SPACE}%{NUMBER:flushes}%{SPACE}%{SIZE:mapped}%{SPACE})|%{SPACE}

The MONGOTYPE patterns are used to deal with empty fields from mongos log line.
The rest of the match directive is just about capturing each field from mongostat to produce a more readable and analysable output.

To create this, I used an online Grok debugger which is very useful because you needn’t to reload logstash every time you want to test your work. It also provides you instant feedback.

I’m know waiting for the bugs to be fixed in stable so that this solution could be more useful to monitor mongo and maybe use it in production.

List of available patterns on Github.

Découverte de la gestion de log avec ELK

Dans le cadre de mon stage, je m’intéresse actuellement au solution de monitoring et j’ai donc eu l’occasion de tester le triplet Elasticsearch Logstash Kibana connu sous l’abréviation ELK. Logstash permet d’agréger simplement des logs provenant de différentes sources, Elasticsearch s’occupe de les stocker et de les rendre disponibles et enfin Kibana les affiche sur un dashboard hautement personnalisable. Les instructions qui suivent m’ont donc permis d’avoir un rapide aperçu du fonctionnement de la solution ELK en local et dans un cas très simple de gestion de logs système.

Récupération des logiciels

wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.1.tar.gz
wget https://download.elasticsearch.org/logstash/logstash/logstash-1.3.3-flatjar.jar
wget https://download.elasticsearch.org/kibana/kibana/kibana-3.0.0milestone5.tar.gz

Extraction

tar xvf elasticsearch-1.0.1.tar.gz
tar xvf kibana-3.0.0milestone5.tar.gz

Elasticsearch

cd elasticsearch-1.0.1/

La configuration elasticsearch.yml se situe dans config/. Il n’est pas nécessaire d’y toucher pour un test en local, on pourrait toutefois modifier les paramètres cluster.name et node.name pour personnaliser l’installation.

Démarrer Elasticsearch:

./bin/elasticsearch

Logstash

Création d’un fichier de configuration logstash.conf:

touch logstash.conf

Nous allons lire les fichiers de log du système, de ce fait, il pourrait être nécessaire de lancer Logstash en root pour que celui-ci puisse lire les fichiers de logs. Cette solution n’est à utiliser que pendant la phase de test.

Contenu du fichier:

input {
    file {
        type => "linux-syslog"
        path => [ "/var/log/*.log", "/var/log/messages", "/var/log/syslog" ]
    }
}
output {
    stdout { }
    elasticsearch_http {
        host => "127.0.0.1"
    }
}

Documentation pour le paramètre elasticsearch_http.

Démarrer Logstash:

sudo java -jar logstash-1.3.3-flatjar.jar agent -f logstash.conf

Les nouveaux logs devraient donc maintenant être récupérés par Logstash et stockés par Elasticsearch. Nous donc pouvoir les visualiser avec Kibana.

Kibana

cd kibana-3.0.0milestone5/

Éditer le fichier config.js et changer la ligne:

elasticsearch: "http://"+window.location.hostname+":9200",

en

elasticsearch: "http://127.0.0.1:9200",

Cette modification nous permet d’ouvrir le fichier index.html directement dans notre navigateur pour accéder à Kibana sans avoir besoin de mettre en place un serveur comme Apache pour servir les fichiers.

Résultat

KibanaL’ajout d’un mécanisme d’authentification pour l’accès à Kibana peut être réalisé simplement en utilisant le projet fangli/kibana-authentication-proxy.

Mongo Dans Tous Ses Etats – Sharding

La suite de mon étude concernait le processus de sharding. Comme pour la réplication, les informations proviennent cette fois du guide sur le sharding.

Sharding

Le sharding divise la donnée et la distribue sur plusieurs machines, aussi appelé shard et résout ainsi le problème de scaling horizontal. Un cluster est constitué de shard, serveur de configuration et mongos. Le sharding réduit la quantité de donnée que chaque serveur doit stocker, ainsi que le nombre d’opérations à effectuer.

Sharded cluster = shards + query routers (mongos) + config servers

Shard (mongod / Replica Set)

Stock la donnée.
En production, pour des questions de haute disponibilité et de consistence des données, chaque shard est un replica set et le cluster est constitué de deux shards ou plus.

Query Router (mongos)

Sert d’interface avec l’application cliente et dirige les opérations vers le shard approprié. Un cluster peut contenir plusieurs routeurs pour distribuer la charge.

En production, un mongos ou plus.
Les curseurs et d’autres ressources étant spécifique à une instance de mongos, chaque client doit interagir avec seulement un mongos.

Config Server

Stock les métadatas du cluster.
Contient un mapping entre les données du cluster et les shards (Quelle donnée se trouve sur quel shard). En production, un cluster possède exactement 3 config servers.

Si un ou deux serveurs de configuration deviennent indisponible, les métadatas du cluster passe en lecture seule jusqu’à ce que les trois serveurs soient à nouveau disponible. Il est toujours possible de lire et d’écrire sur les shards mais aucune migration et aucun découpage n’aura lieu sur les chunks.

Les sauvegardes de ces serveurs sont critiques.
Si le nom ou l’adresse qu’un cluster utilise pour se connecter à un serveur de configuration change, il est nécessaire de redémarrer tous les mongod et les mongos! D’où l’utilité des CNAMEs pour identifier les serveurs de configuration.

Chaque serveur doit être sur une machine séparée.

Partitionnement des données

Le partitionnement s’effectue en utilisant une clé de sharding (sharding key).

Shard Key

Soit un champ indexé ou champ indexé composé existant dans tous les documents de la collection. MongoDB divise la shard key en morceaux (chunks) et les distribue de manière égale entre les shards. La division de la clé s’effectue soit avec un range based partitioning ou un hash based partitioning.

Pour sélectionner une shard key, déterminer les champs communément inclut dans les requêtes pour une application précise et déterminer quelles opérations demandent le plus de performance.

L’index sur la shard key ne peut pas être un multikey index.

Range Based Sharding

Séparation des données en plusieurs intervalles. Deux documents avec une clé de sharding proche ont de grandes chances d’être dans le même chunk. Néanmoins, il peut en résulter une distribution inégale des données sur les shards. Plus efficace en cas de requêtes sur des intervalles. Le router peut déterminé plus facilement vers quels shards diriger la requête.

Hash Based Sharding

MongoDB calcule un hash de la valeur du champ et utilise ces hashs pour créer les chunks. Deux documents avec une clé de sharding proche ont très peu de chance d’être dans le même chunk. Cela permet de s’assurer d’une distribution plus aléatoire des données au sein du cluster et assure une distribution régulière des données sur les shards. Une requête sur un intervalle à de grande chance de s’adresser à tous les shards.

Mécanismes

Mongo dispose de mécanismes pour éviter qu’un shard devienne trop grand: splitting et le balancer.

Splitting

Lorsqu’un chunk devient trop grand, mongo le divise en deux.
Ceci n’affecte pas les données ou les shards.

Balancing

Permet de migrer des chunks. Lorsque la distribution des données devient irrégulière, le balancer va migrer des chunks du shard avec le plus grande nombre de chunks à celui qui en possède le moins. Les données sont retirés du shard d’origine après une migration totale et réussie des données.

Mongo Dans Tous Ses Etats – Réplication

Cette semaine, j’ai eu l’occasion de commencer à étudier le fonctionnement de la réplication et du sharding avec MongoDB. Voici diverses informations provenant du guide sur la réplication.

Réplication

La réplication consiste à écrire la donnée sur plusieurs serveurs et répond ainsi au problématique de haute disponibilité et de redondance des données. Elle permet, dans certains cas, d’augmenter les capacités en lecture. Un replica set peut avoir jusqu’à 12 membres dont 7 voteront à la fois. Pour disposer de plus de 12 instances, il est nécessaire de passer à la réplication maître-esclave.

L’architecture standard pour un replica set en production se compose de trois membres. Si l’application se connecte à plusieurs replica set, chacun doit avoir un nom différent. Les membres s’envoient des ping toutes les 2 secondes.

Replica Set: Groupe de mongod disposant des mêmes (~~) données.

Membres

On distingue deux types principaux de mongod dans un replica set: les primaires et les secondaires.

Primaire (Primary)

Il est unique et reçoit toutes les opérations d’écriture. Tous les changements sont conservés dans son oplog. Une élection d’un nouveau mongod primaire à lieux, si l’actuel ne communique avec aucun membre du replica set pendant 10s.

Secondaire (Secondary)

Récupère le oplog et applique les opérations sur son jeu de données. En cas d’indisponibilité du primaire, un mongod secondaire sera élu à sa place.

Membre à priorité 0

Mongod secondaire qui ne peut pas devenir primaire et ne peut déclencher d’élections. Permet de s’assurer que seul les membres qui en ont les capacités deviendront primaire (distribution géographique, hardware). Il est conseillé de s’assurer que le datacenter principal contient les votants et les membres éligibles.

Membre caché

Invisible pour les applications clientes. Sont toujours des membres à priorité 0, ne peuvent devenir primaire mais peuvent voter. En cluster, mongos n’interagit pas avec les membres cachés.

Membre retardé

Reflète un état retardé de l’état du groupe.
Requis:
* Doit être un membre à priorité 0
* Devrait être un membre caché
Le délai doit être plus petit que la capacité de oplog. Cette fonctionnalité est utile pour pouvoir appliquer un rollback (retour en arrière) en cas d’erreur humaine.

Arbitre (Arbiter)

Sa fonction est de voter en cas d’élection d’un mongod primaire et permet d’obtenir une majorité en cas d’un nombre pair de membres.
Important: Ne pas ajouter d’arbitre sur les machines hébergeant déjà un primaire ou un secondaire.

Stratégies

Déployer un nombre impair de membres pour s’assurer qu’un primaire pourra toujours être élu. Ajouter un arbitre si besoin.
Tolérance aux fautes: Correspond au nombre de membres auquel on soustrait la majorité requise pour élire un nouveau mongod primaire.
Au moins un membre à priorité 0 dans un autre datacenter. Garder une majorité de membre au même endroit.

Le sharding est souvent une meilleur stratégie pour augmenter les capacités en lecture et en écriture.

Read Preference Mode

* primary
* primaryPreferred
* secondary
* secondaryPreferred
* nearest
Seul primary permet d’être sûr d’avoir des données totalement à jour.
Utiliser readPref() dans mongo shell pour accéder aux préférences.