SolrCloud setup, quelques notions importantes pour débuter

Solr (prononcé comme le mot solar en anglais) est un moteur de recherche s’appuyant sur la bibliothèque de recherche Lucene, créée par la Fondation Apache et distribuée et conçue sous licence libre. Je vous présente dans cet article quelques notions de la version SolrCloud, annoncée fault tolerant and high availability.

Solr Standalone

Dans sa version standard standalone, on définit un solr_node « master » et n solr_nodes « slave ». A l’aide d’une configuration dans solrconfig.xml, on déclare le master, les slaves et la stratégie de réplication. C’est alors Solr qui prendra en charge lui-même la synchronisation.

Sur le master

<requestHandler name="/replication" class="solr.ReplicationHandler">
  <lst name="master">
    <str name="replicateAfter">optimize</str>
    <str name="backupAfter">optimize</str>
    <str name="confFiles">schema.xml,stopwords.txt,elevate.xml</str>
    <str name="commitReserveDuration">00:00:10</str>
  </lst>
  <int name="maxNumberOfBackups">2</int>
  <lst name="invariants">
    <str name="maxWriteMBPerSec">16</str>
  </lst>
</requestHandler>

Sur les slaves

<requestHandler name="/replication" class="solr.ReplicationHandler">
  <lst name="slave">
    <str name="masterUrl">http://remote_host:port/solr/core_name/replication</str>
    <str name="pollInterval">00:00:20</str>
    <str name="compression">internal</str>
    <str name="httpConnTimeout">5000</str>
    <str name="httpReadTimeout">10000</str>
    <str name="httpBasicAuthUser">username</str>
    <str name="httpBasicAuthPassword">password</str>
  </lst>
</requestHandler>

Plus de détails ici : https://lucene.apache.org/solr/guide/6_6/index-replication.html#IndexReplication-SettingUpaRepeaterwiththeReplicationHandler

Avantages / Inconvénients

  • Solution native simple à mettre en place
  • Les slaves se synchronisent eux-mêmes en réplicant le master.
  • Avec les écritures sur le master et un load-balancer sur les slaves, on obtient une haute-disponibilité en lecture tant qu’il reste au-moins un slave en fonctionnement.
  • En revanche, pas de haute-dispo pour l’écriture.
  • Si un slave down est remis en service, il sera en retard par rapport au master et aux autres slaves. Il faudra alors le mettre à jour manuellement.

SolrCloud

Avec SolrCloud, on change de stratégie pour viser la haute-dispo en lecture et en écriture. SolrCloud est disponible nativement dans une installation standard Solr. Un nouveau composant, Zookeeper, entre en jeu. Nous en parlerons plus en détails dans un autre article.

Quelques concepts à définir

  • cluster : c’est le groupe de serveurs (nodes) sur lequel tourne le service SolrCloud
  • node : un node est un serveur qui héberge le service Solr. Un groupe de nodes composent un cluster.
  • collection : elle représente un index logique dans son intégralité, fourni par le cluster SolrCloud
  • shard : c’est une subdivision de la collection
  • replica : c’est un index physique détenu par un node du cluster

Exemple 1 : SolrCloud avec 3 shards

  • 1 cluster de 3 nodes
  • 1 collection
  • 3 shards (on découpe l’index en 3 parties)
  • 2 replicas (= 2 cores par node)

Dans cette configuration, l’index (la collection) découpé en 3 parties (les shards) sera répliqué 1 fois (2 replicas). En conséquence, on obtient la répartition ci-dessous :

  • solr_node_1
    • shard1_replica1
    • shard3_replica2
  • solr_node_2
    • shard2_replica2
    • shard3_replica1
  • solr_node_3
    • shard1_replica2
    • shard2_replica1

Pour finir d’illustrer la mécanique de répartition de SolrCloud, si on déploie une telle configuration sur un seul solr_node, il détiendra alors les 6 replicas dans 6 cores.

Avantages / Inconvénients

  • Une telle configuration est haute-disponibilité en lecture et en écriture. Si on perd un solr_node, les 2 autres possèdent encore la totalité de l’index (shards 1 2 3).
  • Elle assure aussi une répartition de charge car chaque solr_node a la responsabilité de seulement 1/3 de l’index, en lecture (requêtes) et en écriture (indexation).
  • Il n’y a plus ni master ni slave. Tous les solr_nodes ont le même rôle et c’est Zookeeper qui se charge de gérer le cluster en fonction de l’état des solr_nodes (leader, active, down, recovering…).
  • Complexité de mise en œuvre et de compréhension
  • Nécessite au-moins 3 nodes

Exemple 2 : SolrCloud avec 1 shard

Avec SolrCloud, on peut mettre en place une architecture sur 2 solr_nodes, comme en mode standalone, mais avec les bénéfices de la haute-dispo en écriture.

  • 1 cluster de 2 nodes
  • 1 collection
  • 1 shard (l’index n’est pas découpé)
  • 2 replicas (= 1 core par node)

Dans cette configuration, on obtient la répartition ci-dessous :

  • solr_node_1
    • shard1_replica1
  • solr_node_2
    • shard1_replica2

Avantages / Inconvénients

  • Haute-disponibilité en lecture et en écriture. Si on perd un solr_node, l’autre possède encore la totalité de l’index.
  • Plus « standard » et plus facile à appréhender que la version 3 nodes / 3 shards
  • Il n’y a plus ni master ni slave. Tous les solr_nodes ont le même rôle et c’est Zookeeper qui se charge de gérer le cluster en fonction de l’état des solr_nodes (leader, active, down, recovering…).
  • On peut sécuriser d’avantage cette installation en ajoutant des nodes.
  • Pas de répartition de la charge (un seul leader). L’unique core leader doit gérer la totalité de l’index (requête + indexation).

Conclusion

SolrCloud offre un niveau de service supérieur à un Solr standalone master/slave. Si votre application en dépend fortement, SolrCloud est clairement à privilégier. De plus, si la version 3 nodes / 3 shards vous paraît complexe à mettre en œuvre, vous pouvez vous reposer sur la version avec 1 seul shard, plus simple. Vous pouvez tester SolrCloud facilement avec les images Docker officielles : https://github.com/docker-solr. Je vous parlerai de Zookeeper dans un futur article.

Sources

https://github.com/docker-solr
https://stackoverflow.com/a/31773632/243996
https://lucene.apache.org/solr/guide/8_8/
https://lucene.apache.org/solr/guide/8_8/solrcloud.html

Docker sur Windows 10 : problème avec Hyper-V

Docker dispose désormais d’une mouture sérieuse pour Windows 10 : Docker Desktop.

Il y a quelques pré-requis à vérifier mais l’installeur fait tout le nécessaire à votre place :

  • activation de Hyper-V
  • activation de WSL 2
  • activation des containers Windows / Linux

Malgré ce joli package, j’ai rencontré une difficulté pour faire fonctionner Docker. Son installation se passe bien mais Docker ne démarre jamais. C’est Hyper-V qui dysfonctionne à cause d’un paramètre de sécurité « Exploit protection » de Windows : Code flow guard (CFG).

Voici l’erreur rencontrée dans le manager Hyper-V lorsqu’on demande la connexion au serveur local :

Ce paramètre « Exploit protection » semble empêcher le programme vmcompute.exe de fonctionner alors que Hyper-V en a besoin pour contrôler ses VMs. Son état d’activation dépend des mises à jour installées sur votre Windows.

Sur un Windows en français, vous trouverez le paramètre à désactiver ici (à travers la recherche du menu Démarrer) :

  • Sécurité Windows
  • Contrôle des applications et du navigateur
  • Exploit protection > Paramètres d’Exploit protection
  • Paramètres du programme
  • \System32\vmcompute.exe > Modifier
  • Protection du flux de contrôle > Désactiver

Une fois ce paramètre désactivé, redémarrez vmcompute dans un PowerShell en admin :

  • net start vmcompute

Vous devriez alors pouvoir faire fonctionner Hyper-V et obtenir une connexion à votre votre « ordinateur local » dans le manager Hyper-V. A partir de là, Docker devrait démarrer sans difficultés !

Voici la source de ma solution.

Warmup : préparez les caches de votre site

Une commande toute simple pour préparer les caches de votre site avant son ouverture en production :

wget -r -np -p -k [URL]

Détail des options

  • -r : suivre récursivement tous les liens de chaque page
  • -np : empêcher le robot de remonter l’arborescence du site
  • -p : télécharger toutes les ressources de la page (css, images, Javascript…)
  • -k : convertir les liens des pages pour utiliser les ressources locales téléchargées

Ajax et XSS : autoriser les requêtes grâce au CORS

Lorsqu’on développe une application RESTful exploitant des webservices disponibles sur un autre domaine, on est rapidement confronté à des questions de sécurité, notamment le cross-site scripting (XSS).

Le problème

Le cas typique dans lequel on rencontre cette problématique est celui d’un site mobile « m.domaine.com » qui récupère des données sur le site desktop « domaine.com ». Toutes les requêtes Ajax partant de « m.domaine.com » vers « domaine.com » seront bloquées par la règle de sécurité Same Origin Policy.

La solution

La solution à cette problématique est le mécanisme du Cross-origin resource sharing (CORS) spécifié par le W3C et utilisable dans Apache et dans la plupart des navigateurs modernes.

La configuration Apache

Le vhost du domaine cible des requêtes (« domaine.com » dans notre exemple) doit spécifier des directives « Access-Control-Allow » :

<VirtualHost *:80> 
    ServerName domaine.com

    Header set Access-Control-Allow-Origin "http://m.domaine.com"
    Header set Access-Control-Allow-Credentials true
    Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    Header set Access-Control-Allow-Headers "content-type, accept, set-cookie"
</VirtualHost>

Astuce

Grâce à la directive « Access-Control-Allow-Headers », on peut indiquer quels entêtes HTTP seront transmis. En autorisant « set-cookie », on peut récupérer, sur « m.domaine.com », le cookie d’une session ouverte sur « domaine.com ». Ainsi, on réalise facilement un petit SSO maison !

Les requêtes Ajax

Avec jQuery, la requête Ajax devra contenir le flag withCredentials à true dans les paramètres xhrFields (voir également la doc de XMLHttpRequest pour les autres implémentations).

$.ajax({
    url: 'http://domaine.com',
    xhrFields: {
        withCredentials: true
    }
});

Attention à la subtilité !

On trouve très souvent la syntaxe ci-dessous :

Header set Access-Control-Allow-Origin "*"

Cette directive ne fonctionne pas lorsqu’on passe le flag withCredentials à true dans les paramètres xhrFields de la requête Ajax. Dans ce cas, il faut préciser explicitement le domaine autorisé :

Header set Access-Control-Allow-Origin "http://m.domaine.com"

RequireJS et ParsleyJS : utiliser les shims pour traduire vos contrôles de formulaires

ParsleyJS est une librairie Javascript permettant de contrôler la saisie de ses formulaires. Elle est livrée avec de nombreuses traductions de ses messages d’erreurs. Si vous gérez les dépendances de votre projet avec RequireJS, voici comment charger élégamment les traductions de Parsley.

Configuration des dépendances avec RequireJS

On indique à RequireJS le path vers les modules « parsley », « parsleyfr » et « parsleyfrextra ». Puis, on utilise les shims pour définir 2 nouvelles dépendances du module « parsley » :

  • parsleyfr
  • parsleyfrextra
require.config({
    paths: {
        "jquery":           "libs/jquery/dist/jquery",
        "underscore":       "libs/underscore/underscore",
        "backbone":         "libs/backbone/backbone",
        "parsley":          "libs/parsleyjs/dist/parsley",
        "parsleyfr":        "libs/parsleyjs/src/i18n/fr",
        "parsleyfrextra":   "libs/parsleyjs/src/i18n/fr.extra",
    },
    shim: {
        "parsley": {
            deps: ["parsleyfr", "parsleyfrextra"]
        },
    }
});

Déclaration d’une dépendance à Parsley

Ensuite, dans nos modules qui dépendent de Parsley, on indique normalement la dépendance dans la fonction « define ».

define([
        "backbone",
        "underscore",
        "parsley"
    ],
    function(Backbone, _) {

    }
);

Grâce aux shims, RequireJS va charger les modules « parsleyfr » et « parsleyfrextra » avant le module « parsley ». Vous pourrez ensuite changer la locale de vos contrôles de formulaires en appelant la méthode « setLocale » de ParsleyJS.

window.Parsley.setLocale('fr');

Un peu de lecture