wiki/HowtoElasticsearch.md

507 lines
15 KiB
Markdown
Raw Normal View History

2016-09-26 12:01:45 +02:00
---
2016-10-10 21:34:30 +02:00
title: Howto Elasticsearch
2016-10-11 00:16:48 +02:00
categories: web bdd nosql
2016-10-10 21:34:30 +02:00
...
2016-09-26 12:01:45 +02:00
2016-10-05 09:38:39 +02:00
* Documentation : <https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html>
2016-10-10 21:34:30 +02:00
* Forum : <https://discuss.elastic.co/c/elasticsearch>
2016-09-26 12:01:45 +02:00
2016-10-13 11:21:06 +02:00
[Elasticsearch](https://www.elastic.co/fr/products/elasticsearch) est un serveur de base de données écrit en Java disposant dune interface REST HTTP. Elasticsearch est notammment utilisé dans [la stack Elastic avec Logstash et Kibana](HowtoELK).
2016-10-11 00:16:48 +02:00
2016-09-26 12:01:45 +02:00
## Installation
2016-10-10 21:34:30 +02:00
Vu le développement actif d'Elasticsearch, nous préconisons l'installation des paquets Debian distribués par Elasticsearch :
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
# echo "deb http://packages.elastic.co/elasticsearch/2.x/debian stable main" >> /etc/apt/sources.list.d/elasticsearch.list
2016-09-26 12:01:45 +02:00
# wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | apt-key add -
# apt install elasticsearch openjdk-7-jre
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
On vérifie qu'on a bien Java >= 1.7 activé :
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
$ java -version
java version "1.7.0_111"
OpenJDK Runtime Environment (IcedTea 2.6.7) (7u111-2.6.7-1~deb8u1)
OpenJDK 64-Bit Server VM (build 24.111-b01, mixed mode)
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
Pour activer le démarrage automatique :
2016-09-26 12:01:45 +02:00
2016-10-11 00:16:48 +02:00
~~~
# systemctl enable elasticsearch
Synchronizing state for elasticsearch.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d elasticsearch defaults
Executing /usr/sbin/update-rc.d elasticsearch enable
Created symlink from /etc/systemd/system/multi-user.target.wants/elasticsearch.service to /usr/lib/systemd/system/elasticsearch.service.
# systemctl start elasticsearch
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
## Configuration de base
2016-09-26 12:01:45 +02:00
2016-10-11 00:16:48 +02:00
Les paramètres système (répertoires utilisés, configuration JVM) se trouvent dans le fichier `/etc/default/elasticsearch`,
les options applicatives (mémoire, réseau, cluster) se trouvent dans le fichier `/etc/elasticsearch/elasticsearch.yml`.
2016-09-26 12:01:45 +02:00
2016-10-12 16:08:59 +02:00
Il faut activer le redémarrage automatique en cas de mise à jour (classique sous Debian).
On peut aussi définir un *tmpdir* spécifique (utile quand `/tmp` est en _noexec_) dans `/etc/default/elasticsearch` :
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 16:08:59 +02:00
RESTART_ON_UPGRADE=true
2016-09-26 12:01:45 +02:00
ES_JAVA_OPTS="-Djava.io.tmpdir=/var/lib/elasticsearch/tmp"
~~~
2016-10-11 00:16:48 +02:00
Dans ce cas, assurez-vous de créer le répertoire avec les bons droits :
2016-09-26 12:01:45 +02:00
~~~
# mkdir /var/lib/elasticsearch/tmp
# chown elasticsearch: /var/lib/elasticsearch/tmp
2016-10-11 00:16:48 +02:00
# chmod 750 /var/lib/elasticsearch/tmp
~~~
Via le fichier `/etc/elasticsearch/elasticsearch.yml` vous devez au minimum configurer :
~~~{.yaml}
cluster.name: foo
node.name: bar
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
## Journaux
On conseille de ne pas activer le logging stdout vers la console, mais de conserver seulement les logs dans `/var/log/elasticsearch/`. On peut faire cela via le fichier `/etc/elasticsearch/logging.yml` :
~~~{.diff}
-rootLogger: ${es.logger.level}, console, file
+rootLogger: ${es.logger.level}, file
~~~
Les journaux intéressants sont dans `/var/log/elasticsearch/_cluster_name_.log`
## Configuration réseau
Par défaut, Elasticsearch écoute sur 127.0.0.1 sur TCP/9200 pour son interface REST HTTP.
La directive de configuration suivante peut être positionnée pour qu'il écoute sur plusieurs interfaces réseau :
~~~{.yaml}
network.host: ["192.0.2.42", "198.51.100.42", "127.0.0.1"]
~~~
## Plugins
Les plugins permettent différentes améliorations, notamment en terme de visualisation simple et conviviale d'une base de données.
On verra ici les plus utiles.
2016-09-26 12:01:45 +02:00
### Kopf - web admin interface for elasticsearch
2016-10-10 21:34:30 +02:00
<https://github.com/lmenezes/elasticsearch-kopf>
2016-09-26 12:01:45 +02:00
~~~
# /usr/share/elasticsearch/bin/plugin install lmenezes/elasticsearch-kopf
2016-10-11 00:16:48 +02:00
# chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/plugins/
2016-09-26 12:01:45 +02:00
~~~
2016-10-11 00:16:48 +02:00
*Note :* cela nécessite un accès réseau vers https://github.com/
L'interface **Kopf** est ainsi disponible sur http://127.0.0.1:9200/_plugin/kopf/
### Head - web front end for an elastic search cluster
<https://github.com/mobz/elasticsearch-head>
~~~
# /usr/share/elasticsearch/bin/plugin install mobz/elasticsearch-head
# chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/plugins/
~~~
L'interface **Head** est ainsi disponible sur http://127.0.0.1:9200/_plugin/head/
2016-09-26 12:01:45 +02:00
## Mode cluster
_Paragraphe à revoir car l'auto-découverte et le multicast ont changé._
Exemples avec 3 instances nommés **elastic1**, **elastic2** et **elastic3**.
2016-09-26 12:01:45 +02:00
2016-10-11 00:16:48 +02:00
Spécifier un nom de cluster commun dans `elasticsearch.yml` sur chaque machine.
2016-09-26 12:01:45 +02:00
~~~
cluster.name: mycluster
~~~
On donnera un nom aux nœuds avec la directive suivante :
~~~
node.name: "elastic1"
~~~
En analysant les logs de **elastic2** on verra ceci :
~~~
/var/log/elasticsearch/mycluster.log:
[2013-10-03 15:52:59,863][INFO ][transport ] [elastic2] bound_add
ress {inet[/0:0:0:0:0:0:0:0:9300]}, publish_address {inet[/192.168.4.205:9300]}
[2013-10-03 15:53:03,247][INFO ][cluster.service ] [elastic2] detected_
master [elastic1][3tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300]], added {[el
astic1][3tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300]],}, reason: zen-disco-
receive(from master [[elastic1][3tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300
]]])
[2013-10-03 15:53:03,338][INFO ][discovery ] [elastic2] mycluster
/aFsNsA7pRa2_injvAU5Hwg
[2013-10-03 15:53:03,362][INFO ][http ] [elastic2] bound_add
ress {inet[/0:0:0:0:0:0:0:0:9200]}, publish_address {inet[/192.168.4.205:9200]}
[2013-10-03 15:53:03,362][INFO ][node ] [elastic2] started
~~~
Indiquant que le master a été détecté.
Et sur **elastic1**, le master :
~~~
[2013-10-03 15:53:03,176][INFO ][cluster.service ] [elastic1] added {[e
lastic2][aFsNsA7pRa2_injvAU5Hwg][inet[/192.168.4.205:9300]],}, reason: zen-disco
-receive(join from node[[elastic2][aFsNsA7pRa2_injvAU5Hwg][inet[/192.168.4.205:9
300]]])
[2013-10-03 15:54:37,112][INFO ][cluster.service ] [elastic1] added {[e
lastic3][CLCB_hU5TeGXLziFcLjY8w][inet[/192.168.4.204:9300]],}, reason: zen-disco
-receive(join from node[[elastic3][CLCB_hU5TeGXLziFcLjY8w][inet[/192.168.4.204:9
300]]])
~~~
Indique que les deux slaves ont été ajoutés.
2016-10-11 00:16:48 +02:00
On peut consulter le *status* en faisant des requêtes de types RESTFul. :
2016-09-26 12:01:45 +02:00
2016-10-11 00:16:48 +02:00
~~~{.bash}
$ curl 'http://localhost:9200/_nodes?pretty=true'
2016-09-26 12:01:45 +02:00
~~~
Si le master passe en DOWN, un autre est élu.
~~~
[2013-10-03 16:15:53,892][INFO ][discovery.zen ] [elastic2] master_le
ft [[elastic1][3tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300]]], reason [shut
_down]
[2013-10-03 16:15:53,920][INFO ][cluster.service ] [elastic2] master {n
ew [elastic3][CLCB_hU5TeGXLziFcLjY8w][inet[/192.168.4.204:9300]], previous [elas
tic1][3tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300]]}, removed {[elastic1][3
tn7uHSHR8-donXO6JXFRA][inet[/192.168.4.201:9300]],}, reason: zen-disco-receive(f
rom master [[elastic3][CLCB_hU5TeGXLziFcLjY8w][inet[/192.168.4.204:9300]]])
~~~
Pour éviter de faire des requêtes multicast (mode auto-découverte). On spécifiera la liste des nœuds dans la configuration des instances.
~~~
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: ["elastic2", "elastic3"]
~~~
## Check Nagios
On check sur la page `/_cat/health` si le status n'est pas en **red**.
2016-09-26 12:01:45 +02:00
~~~
/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -u /_cat/health?h=st -p 9200 -r 'red' --invert-regex
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 17:34:44 +02:00
## Snapshost et sauvegardes
2016-09-26 12:01:45 +02:00
2016-10-12 17:34:44 +02:00
### Configuration des snapshots
2016-10-12 17:34:44 +02:00
* Documentation http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-snapshots.html
Il faut définir un répertoire pour stocker les snapshots :
~~~
# mkdir /home/backup-elasticsearch
2016-10-12 17:34:44 +02:00
# chmod 750 /home/backup-elasticsearch
# chown elasticsearch: /home/backup-elasticsearch
~~~
2016-10-12 17:34:44 +02:00
*Note :* en cas de cluster multi-nœuds, le répertoire de snapshots doit impérativement être partagé entre chaque nœud, classiquement via NFS, car chaque nœud ne gère que ses propres données.
2016-10-12 17:34:44 +02:00
On précise le répertoire des snapshots dans la configuration `/etc/elasticsearch/elasticsearch.yml` :
~~~
path.repo: ["/home/backup-elasticsearch"]
~~~
2016-09-26 12:01:45 +02:00
2016-10-12 17:34:44 +02:00
Il faut redémarrer Elasticsearch et créer un ensemble de snapshots (snapshots repository) ainsi :
~~~
$ curl -XPUT 'http://localhost:9200/_snapshot/foo' -d '{
"type": "fs",
"settings": {
"location": "/home/backup-elasticsearch",
"compress": true
}
}'
{"acknowledged":true}
~~~
On peut ainsi lister les infos :
~~~
$ curl -XGET 'http://localhost:9200/_snapshot/?pretty'
{
"foo" : {
"type" : "fs",
"settings" : {
"compress" : "true",
"location" : "/home/backup-elasticsearch/foo"
}
}
}
~~~
Si l'on veut supprimer un ensemble de snapshots :
~~~
$ curl -s -XDELETE "localhost:9200/_snapshot/foo?pretty"
{"acknowledged":true}
~~~
### Gestion des snapshots
Pour créer un snapshot dans l'ensemble **foo** :
~~~
$ curl -s -XPUT "localhost:9200/_snapshot/foo/snapshot_test?wait_for_completion=true"
$ ls -l /home/backup-elasticsearch
-rw-r--r-- 1 elasticsearch elasticsearch 34 Apr 11 01:35 index
drwxr-xr-x 22 elasticsearch elasticsearch 4096 Apr 11 01:35 indices
-rw-r--r-- 1 elasticsearch elasticsearch 3006 Apr 11 01:35 metadata-snapshot_test
-rw-r--r-- 1 elasticsearch elasticsearch 419 Apr 11 01:35 snapshot-snapshot_test
~~~
Si l'on tente de créer un snapshot déjà existant, on obtiendra :
~~~
{"error":"InvalidsnapshotNameException[[backup:snapshot_test] Invalid snapshot name [snapshot_test], snapshot with such name already exists]","status":400}
~~~
Lister les snapshots :
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 17:34:44 +02:00
$ curl -XGET "localhost:9200/_snapshot/foo/_all?pretty=true"
---
"snapshots" : [ {
"snapshot" : "snapshot_201403190415",
"indices" : [...],
"state" : "SUCCESS",
"start_time" : "2014-03-19T03:15:03.380Z",
"start_time_in_millis" : 1395198903380,
"end_time" : "2014-03-19T03:16:33.381Z",
"end_time_in_millis" : 1395198993381,
"duration_in_millis" : 90001,
...
"snapshot" : "snapshot_201403201222",
"indices" : [...],
"state" : "SUCCESS",
"start_time" : "2014-03-20T11:22:07.441Z",
"start_time_in_millis" : 1395314527441,
"end_time" : "2014-03-20T11:22:56.176Z",
"end_time_in_millis" : 1395314576176,
"duration_in_millis" : 48735,
"failures" : [ ],
"shards" : {
"total" : 86,
"failed" : 0,
"successful" : 86
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 17:34:44 +02:00
Pour supprimer un snapshot :
~~~
$ curl -s -XDELETE "localhost:9200/_snapshot/foo/snapshot_test"
~~~
### Sauvegarde via snapshots
On peut ainsi créer régulièrement des snapshots pour les sauvegardes.
Pour créer un snapshot toutes les heures, et en conserver 24 en permanence (notion de snapshots "roulants") :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ date=$(date +%H)
2016-10-12 17:34:44 +02:00
$ curl -s -X DELETE "127.0.0.1:9200/_snapshot/foo/h${date}" | grep -v acknowledged..true
$ curl -s -X PUT "127.0.0.1:9200/_snapshot/foo/h${date}?wait_for_completion=true" -o /tmp/es_snapshot_h${date}.log
~~~
Plus classiquement pour avoir un snapshot par jour :
~~~
$ date=$(date +%Y-%m-%d)
$ curl -s -XDELETE "localhost:9200/_snapshot/foo/snapshot_${date}" | grep -v acknowledged..true
$ curl -s -XPUT "localhost:9200/_snapshot/foo/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 17:34:44 +02:00
On peut ensuite purger les snapshots vieux de plus de 10 jours ainsi :
~~~
$ cd /home/backup-elasticsearch/foo
$ for i in $(ls -1d snapshot-* | head -n -10 | sed s'/snapshot-snapshot_//g'); do curl -s -XDELETE "localhost:9200/_snapshot/foo/snaps
hot_${i}"; done
~~~
2016-09-26 12:01:45 +02:00
## Principe de fonctionnement
_Basé sur le livre <http://exploringelasticsearch.com/book>_
2016-10-10 21:34:30 +02:00
On utilisera l'outil cURL pour faire les requêtes.
2016-09-26 12:01:45 +02:00
En plaçant à la fin d'une URI `?pretty=true` on pourra obtenir un JSON formaté, plus lisible pour les humains.
### Avec une base de données d'exemple
2016-10-12 17:34:44 +02:00
Nous allons utiliser une base de données d'exemple pour faire des tests.
2016-09-26 12:01:45 +02:00
Télécharger, <https://github.com/andrewvc/ee-datasets/archive/master.zip>, décompresser l'archive et exécuter le programme Java qui va injecter la BDD "movie_db" dans votre cluster ES.
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ java -jar elastic-loader.jar http://localhost:9200 datasets/movie_db.eloader
2016-09-26 12:01:45 +02:00
~~~
La BDD movie_db contient quelques noms de films, avec leurs informations associés (genre, date, acteurs, …).
Pour consulter tout son contenu :
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl http://localhost:9200/movie_db/_search?pretty=true
2016-09-26 12:01:45 +02:00
~~~
2016-10-12 17:34:44 +02:00
### En créant une base de données
2016-09-26 12:01:45 +02:00
#### Opérations CRUD
2016-10-10 21:34:30 +02:00
Créer un index (équivalent d'une base de données) nommé *planet* :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl -X PUT localhost:9200/planet
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
Créer un type de donnée nommé « hacker » :
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
$ curl -X PUT localhost:9200/planet/hacker/_mapping -d '
2016-09-26 12:01:45 +02:00
{
"hacker": {
"properties": {
"handle": {
"type": "string"
},
"age": {
"type": "long"
}
}
}
}
'
~~~
2016-10-10 21:34:30 +02:00
Créer un document de type hacker avec l'ID 1 :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl -X PUT localhost:9200/planet/hacker/1 -d '{"handle": "jean-michel", "age": 18}'
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
Voir son contenu :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl localhost:9200/planet/hacker/1?pretty=true
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
Mise à jour du champ âge :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl -X POST localhost:9200/planet/hacker/1/_update -d '{"doc": {"age": 19}}'
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
Suppression du document :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ curl -X DELETE localhost:9200/planet/hacker/1
2016-09-26 12:01:45 +02:00
~~~
#### Recherche basique
Recréons un index de test :
~~~
2016-10-10 21:34:30 +02:00
$ curl -X DELETE localhost:9200/planet
$ curl -X PUT localhost:9200/planet -d '
2016-09-26 12:01:45 +02:00
{
"mappings": {
"hacker": {
"properties": {
"handle": {
"type": "string"
},
"hobbies": {
"type": "string",
"analyzer": "snowball"
}
}
}
}
}
'
~~~
2016-10-10 21:34:30 +02:00
Ajoutons quelques documents :
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
$ curl -X PUT localhost:9200/planet/hacker/1 -d '
2016-09-26 12:01:45 +02:00
{
"handle": "mark",
"hobbies": ["rollerblading", "hacking", "coding"]
}
'
2016-10-10 21:34:30 +02:00
$ curl -X PUT localhost:9200/planet/hacker/2 -d '
2016-09-26 12:01:45 +02:00
{
"handle": "gondry",
"hobbies": ["writing", "skateboarding"]
}
'
2016-10-10 21:34:30 +02:00
$ curl -X PUT localhost:9200/planet/hacker/3 -d '
2016-09-26 12:01:45 +02:00
{
"handle": "jean-michel",
"hobbies": ["coding", "rollerblades"]
}
'
~~~
2016-10-10 21:34:30 +02:00
Recherchons ceux qui ont pour hobby *rollerblading* :
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
$ curl -X POST localhost:9200/planet/hacker/_search?pretty=true -d '
2016-09-26 12:01:45 +02:00
{
"query": {
"match": {
"hobbies": "rollerblading"
}
}
}
'
~~~
On obtiens 2 résultats, _jean-michel_ et _mark_. Pourtant le hobby de _jean-michel_ n'est pas _rollerblading_ mais _rollerblades_, alors comment Elastic Search l'a trouvé ?
C'est parce quil comprend que _rollerblading_ et _rollerblades_ sont très similaires ! Cela grâce à l'analyseur de type « snowball » que nous avons indiqué lors de la création du type _hobbies_. Cela indique à ES qu'il s'agit non pas d'une chaîne de caractère banale mais du texte Anglais (Gestion des autres langues ?).
2016-10-11 00:16:48 +02:00
## FAQ
### Erreur "failed to map segment from shared object: Operation not permitted"
Si vous obtenez une erreur du type :
~~~
[2016-06-15 14:53:05,714][WARN ][bootstrap ] unable to load JNA native support library, native methods will be disabled.
java.lang.UnsatisfiedLinkError: /tmp/jna--1985354563/jna3461912487982682933.tmp: /tmp/jna--1985354563/jna3461912487982682933.tmp: failed
to map segment from shared object: Operation not permitted
~~~
C'est peut-être que vous avez votre partition `/tmp` en _noexec_, il faut alors changer le chemin comme indiqué sur [#configuration-de-base]()