Cédric Temple

Administrateur système en logiciels libres

ElasticSearch, utilisation de l'API par un adminSys

2018-11-22 » notes pour plus tard

Ce contenu est fourni sous licence CC BY-NC-SA 4.0

Notes sur ElasticSearch

Introduction

En tant qu’AdminSys, on doit souvent intervenir sur les applications pour les installer, les mettre à jour, analyser ou purger les logs, les redémarrer, changer la configuration, … On doit aussi intervenir sur les données pour les sauvegarder, les purger, les voir, les analyser, les transformer pour en tirer une certaine intelligence, …

Ceci sont mes notes sur les éléments de l’API ElasticSearch que j’utilise.

Dans la suite, je considère que le serveur ElasticSearch est configuré dans la variable ELASTICSEARCH_HOST de la façon suivante :

cedric@portable:~ export ELASTICSEARCH_HOST="http://monserveur.tld:9200"

Utilisation dans un terminal, sans le poids du JSON

ElasticSearch fournit une entrée API qui est _cat. Cette entrée est très pratique pour être utilisée dans un terminal et être afffichée en mode “plat”, pas en mode JSON. Exemple pour afficher les indices :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/indices"
green open index1   Ip1vYDhYTOKLk0_kMGaNjQ 1 0  65469 0  12.8mb  12.8mb
green open index2   piVclkiqQY-IeFCQ-eMlbg 1 0   1693 0  40.5mb  40.5mb
green open index3   1Se8IjIHQi2XZfytb11K7A 1 0  14638 0   2.8mb   2.8mb
green open index4   iEuoNPPKRIifBx38DT4Jag 1 0  10717 0   1.7mb   1.7mb
green open index5   TVLj8qJEToOMiCsa4khcxw 1 0   2046 0  71.8mb  71.8mb
green open index6   K4d4aINvStOK5EBxdq_Sjg 1 0   3444 0  89.8mb  89.8mb
green open index7   wQuuFxj1S5Ker69IzZ9isw 1 0   3319 0  80.4mb  80.4mb
green open index8   f2WpxYmYSXm02GCXzUWcVQ 1 0   3472 0 135.6mb 135.6mb

On peut passer un argument pour afficher le nom des colonnes :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/indices?v"
health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   index1   Ip1vYDhYTOKLk0_kMGaNjQ 1   0   65469      0            12.8mb      12.8mb
green  open   index2   piVclkiqQY-IeFCQ-eMlbg 1   0    1693      0            40.5mb      40.5mb
green  open   index3   1Se8IjIHQi2XZfytb11K7A 1   0   14638      0             2.8mb       2.8mb
green  open   index4   iEuoNPPKRIifBx38DT4Jag 1   0   10717      0             1.7mb       1.7mb
green  open   index5   TVLj8qJEToOMiCsa4khcxw 1   0    2046      0            71.8mb      71.8mb
green  open   index6   K4d4aINvStOK5EBxdq_Sjg 1   0    3444      0            89.8mb      89.8mb
green  open   index7   wQuuFxj1S5Ker69IzZ9isw 1   0    3319      0            80.4mb      80.4mb
green  open   index8   f2WpxYmYSXm02GCXzUWcVQ 1   0    3472      0           135.6mb     135.6mb

On peut choisir aussi les colonnes que l’on souhaite afficher avec l’option h (pour header) :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/indices?v&h=index,docs.count,store.size,pri.store.size"
index            docs.count store.size pri.store.size
monindex1                15    182.4kb        182.4kb
monindex2             65469     12.8mb         12.8mb
monindex3              1693     40.5mb         40.5mb

On peut trier selon une colonne avec l’option s (pour sort). Dans l’exemple, je trie selon la colonne docs.count avec un tri “descendant” (le plus grand nombre est en premier) :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/indices?v&h=index,docs.count,store.size,pri.store.size&s=docs.count:desc"
index     docs.count store.size pri.store.size
monindex      816000    330.7mb        330.7mb
monindex      425021     93.6mb         93.6mb
monindex      379366      5.2gb          5.2gb
monindex      342819     81.3mb         81.3mb
monindex      340211     78.2mb         78.2mb
monindex      235439     50.3mb         50.3mb
monindex      157330     48.7mb         48.7mb
monindex      137939      4.1gb          4.1gb
monindex      122784     26.8mb         26.8mb

Remarque : il est possible de trier sur plusieurs colonnes :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/indices?v&h=index,docs.count,store.size,pri.store.size&s=docs.count:desc,store.size:desc"

On peut voir les alias :

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_cat/aliases
alias                        index         filter routing.index routing.search
monjolialias1        monjoliindex1              -      -             -
monjolialias2        monjoliindex2              -      -             -
monjolialias3        monjoliindex3              -      -             -

Gestion des index

Création d’un index

cedric@portable:~$ curl -XPUT "${ELASTICSEARCH_HOST}/<leNomDeMonIndex>"
# exemple : je crée un index contenant des méta-datas sur des images que je nomme "mages-metadatas"
cedric@portable:~$ curl -XPUT "${ELASTICSEARCH_HOST}/images-metadatas"

Suppression d’un index

cedric@portable:~$ curl -XDELETE "${ELASTICSEARCH_HOST}/<leNomDeMonIndex>"
# exemple : j'efface un index contenant des méta-datas sur des images que je nomme "mages-metadatas"
cedric@portable:~$ curl -XDELETE "${ELASTICSEARCH_HOST}/images-metadatas"

Voir les index

Gestion des alias

Intérêt d’un alias

Un alias permet de donner un autre nom à un index. Un alias identique peut être donné à plusieurs index : dans ce cas, tous les documents de cet index sont rechercher. Différents cas d’utilisation :

  • j’ai plein d’index et je ne veux pas que mon application sache lequel elle doit utiliser. Je crée donc un alias qui permet d’identifier l’index à utiliser. Dès lors, le code applicatif est plus simple.
  • je crée plusieurs index (pour des raisons techniques par exemple) et j’indique à mon application les index qu’elle doit utiliser en utilisant le même alias pour tous les index. Là encore, le code applicatif est plus simple.
  • mes données proviennent de l’extérieur et j’importe tous les jours une mise à jour complète de ces données dans un nouvel index. J’indique à mon application le bon index à utiliser en changeant l’alias. L’avantage est que la création d’un alias est ultra-rapide, comparitivement à un import de données. S’il y a beaucoup de données, la bascule d’un alias d’un index vers un autre est ultra-rapide et ne nécessite pas de redémarrer mon application.

Voir les alias

cedric@portable:~$ curl "${ELASTICSEARCH_HOST}/_aliases"
{
  "<nomDeMonIndexe>": {
    "aliases": {
      "<NomDeMonAlias>": {}
    }
  },
  "<AutreNomDIndex>": {
    "aliases": {
      "<AutresNomDAlias>": {}
    }
  },
  ...
}

Trouver les index qui n’ont pas d’alias

Pour notre utilisation, nous créons un index par “site web” pour environ 120 sites web. Chaque index correspond à une version des données à un jour J. Pour mettre à jour un site web, nous créons un nouvel index daté du jour J (“Jour J” == “date des données”) puis nous basculons les alias. Ce qui a pour conséquence : tout index n’ayant pas d’alias est un index “outdated”. Cet index devrait être supprimé. Donc, pour réaliser l’action “purge des données inutiles car outdated”, nous avons besoin d’identifier “les index qui n’ont pas d’alias”.

J’ai donc fait un script (un peu bourrin, il faut bien se l’avouer) d’identification des index qui n’ont pas d’alias :

# récupération des index
curl -s ${ELASTICSEARCH_HOST}/_cat/indices/?h=index&s=index > indices.txt
# récupération des alias
curl -s ${ELASTICSEARCH_HOST}/_cat/aliases/?h=index > aliases.txt
# vérification que pour chaque index, on dispose d'un alias, sinon afficher le nom de cet index
cat indices.txt | while read a
do
   NB=`grep $a aliases.txt | wc -l `
   if [ ${NB} -eq 0 ]
      then
          echo "Indexe sans alias : $a"
   fi
done

Il est très probable qu’on peut améliorer ce script :

  1. en utilisant un langage de plus haut niveau (Python, Perl) plus efficace en limitant les nombreux forks utilisés ici pour lancer différentes commandes
  2. en ne faisant qu’une seule requête à ElasticSearch sur /_aliases (en JSON) et en contrôlant qu’il n’y a rien dans la propriété “aliases” de l’index correspondant.

Cependant, ici, nous avons fait au plus vite et au plus efficace en temps de “développement” tout en vérifiant que les performances sont correctes au vu du but à atteindre (le script est lancé une seule fois par jour et ne prend que quelques secondes). Bref, une optimisation ici ne nous serait pas forcément utile (car nous avons assez peu d’index/alias au final). Dans un autre cadre, il serait peut-être pertinent d’optimiser ce script.

Créer un alias

Pour créer un alias sur un index, il faut juste indiquer le nom de l’index qui sera la cible de l’alias.

cedric@portable:~$ curl -X POST "${ELASTICSEARCH_HOST}/_aliases" -H 'Content-Type: application/json' -d 
 {
     "actions" : [
         { "add" : { "index" : "<leNomDeMonIndex>", "alias" : "<LeNomDeMonAlias>" } }
     ]
 }
'

Supprimer un alias

Supprimer un alias ne supprime pas l’index correspondant. On supprime juste l’alias.

cedric@portable:~$ curl -X POST "${ELASTICSEARCH_HOST}/_aliases" -H 'Content-Type: application/json' -d 
 {
     "actions" : [
         { "add" : { "index" : "<leNomDeMonIndex>", "alias" : "<LeNomDeMonAlias>" } }
     ]
 }
'

On peut créer un alias sur 2 index différents. Dès lors, la recherche de documents dans cet alias va en réalité chercher dans tous les index. Exemple, ici, on va créer un alias nommé “images-metadatas” qui “pointe” sur 2 index différents :

cedric@portable:~$ curl -X POST "${ELASTICSEARCH_HOST}/_aliases" -H 'Content-Type: application/json' -d 
 {
     "actions" : [
         { "remove" : { "index" : "images-metadatas-2018-01", "alias" : "images-metadatas" } },
         { "remove" : { "index" : "images-metadatas-2018-02", "alias" : "images-metadatas" } }
     ]
 }
'

“Déplacer” un alias

On peut “déplacer” un alias : c’est à dire que l’on peut faire en sorte qu’un alias qui pointait sur un index pointe maintenant sur un autre index. Cette opération étant ultra-rapide, l’application (si elle est développée correctement) ne sera pas touchée (pas de redémarrage nécessaire). Imaginons que nous ayons un index “images-metadatas” qui pointait vers un seul index “images-metadatas-2018-01” et que l’on veut le “déplacer” vers l’index “images-metadatas-2018-02” :

cedric@portable:~$ curl -X POST "${ELASTICSEARCH_HOST}/_aliases" -H 'Content-Type: application/json' -d 
 {
     "actions" : [
         { "remove" : { "index" : "images-metadatas-2018-01", "alias" : "images-metadatas" } },
         { "add" : { "index" : "images-metadatas-2018-02", "alias" : "images-metadatas" } }
     ]
 }
'

Gestion des documents

Erreurs déjà rencontrées

Result window is too large

Voici l’erreur complète :

Result window is too large, from + size must be less than or equal to: [10000] but was [10020]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter

Comme on ne pouvait pas régler définitivement le problème, pour une raison très précise, nous avons été contraint d’augmenter la limite, avec un impact sur les performances non négligeable. Pour cela, il faut modifier un paramètre au niveau de l’index, avant ou après l’import de données :

curl -XPUT "${ELASTICSEARCH_HOST}/<nomIndex>/_settings" -d '{"index" : {"max_result_window": <nbMaxDoc>}}

nbMaxDoc correspond au nombre maximum d’éléments. Exemple, si votre index se nomme “images-metadatas” et que vous souhaitez augmenter le chiffre jusqu’à 400 000 :

curl -XPUT "${ELASTICSEARCH_HOST}/images-metadatas/_settings" -d '{"index" : {"max_result_window": 400000}}

Ce contenu est fourni sous licence CC BY-NC-SA 4.0