wiki/HowtoElasticsearch.md

521 lines
16 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-11 00:16:48 +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.
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
~~~
## Backup
La sauvegarde se fait par snapshots, dans un ou plusiers dépôts.
On crée le dépôt sur le filesystem :
~~~
# mkdir /home/backup-elasticsearch
# chown elasticsearch: /home/backup-elasticsearch
~~~
2016-10-10 21:34:30 +02:00
*Note :* en cas de cluster multi-nœuds, il faut penser que le filesystem doit être partagé (via NFS par exemple) car chaque nœud ne gère que ses propres données.
Le dépôt doit être référencé dans la configuration `/etc/elasticsearch/elasticsearch.yml` :
~~~
path.repo: ["/home/backup-elasticsearch"]
~~~
2016-09-26 12:01:45 +02:00
Après restart d'Elasticsearch on lui fait créer le snapshot **evobackup** :
2016-09-26 12:01:45 +02:00
~~~
2016-10-10 21:34:30 +02:00
$ curl -XPUT 127.0.0.1:9200/_snapshot/evobackup -d '{"type": "fs", "settings": {"location": "/home/backup-elasticsearch/evobackup", "compress": true}}'
2016-09-26 12:01:45 +02:00
~~~
Enfin, dans un script qui tourne régulièrement, on provoque la fixation d'un snapshot :
2016-09-26 12:01:45 +02:00
2016-10-10 21:34:30 +02:00
~~~{.bash}
$ date=$(date +%H)
$ curl -s -X DELETE "127.0.0.1:9200/_snapshot/evobackup/h${date}" | grep -v acknowledged..true
$ curl -s -X PUT "127.0.0.1:9200/_snapshot/evobackup/h${date}?wait_for_completion=true" -o /tmp/es_snapshot_h${date}.log
2016-09-26 12:01:45 +02:00
~~~
Ici, on crée un snapshot portant le numéro de l'heure courante. Il est supprimé avant d'être recréé afin d'avoir des snapshots "roulants".
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
Nous allons utiliser une BDD d'exemple pour faire des tests.
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
~~~
### En créant une BDD
#### 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 ?).
## snapshots
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-snapshots.html
Création d'un répertoire pour accueillir les snapshots :
~~~
# mkdir /home/backup-elasticsearch
# chown elasticsearch:elasticsearch /home/backup-elasticsearch
# chmod 750 /home/backup-elasticsearch
# vim /etc/elasticsearch/elasticsearch.yml
path.repo: ["/home/backup-elasticsearch"]
$ curl -XPUT 'http://localhost:9200/_snapshot/backup' -d '{
"type": "fs",
"settings": {
"location": "/home/backup-elasticsearch",
"compress": true
}
}'
{"acknowledged":true}
~~~
ATTENTION, dans le cadre d'un cluster, chaque noeud écrira ses données
dans son répertoire local… et ne les effacera pas en cas de DELETE.
Il faut donc centraliser ce répertoire via un montage NFS !
Vérifier les infos du répertoire pour les snapshots :
~~~
$ curl -XGET 'http://localhost:9200/_snapshot/?pretty'
---
{
"backup" : {
"type" : "fs",
"settings" : {
"compress" : "true",
"location" : "/home/backup-elasticsearch"
}
}
}
~~~
Supprimer les infos sur le répertoire des snapshots :
~~~
$ curl -s -XDELETE "localhost:9200/_snapshot/backup?pretty"
---
{"acknowledged":true}
~~~
Créer un snapshot :
~~~
$ curl -s -XPUT "localhost:9200/_snapshot/backup/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
~~~
Supprimer un snapshot :
~~~
$ curl -s -XDELETE "localhost:9200/_snapshot/backup/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 :
~~~
$ curl -XGET "localhost:9200/_snapshot/backup/_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
~~~
Pour faire des backups on pourra utiliser un script comme celui-ci :
~~~
# Clean old snapshots (keep 10 snaps).
cd /home/backup-elasticsearch
for i in $(ls -1d snapshot-* | head -n -10 | sed s'/snapshot-snapshot_//g'); do curl -s -XDELETE "localhost:9200/_snapshot/backup/snaps
hot_${i}"; done
# Take a snapshot on master node.
date=$(date +%Y-%m-%d)
curl -s -XDELETE "localhost:9200/_snapshot/backup/snapshot_${date}" | grep -v acknowledged..true
curl -s -XPUT "localhost:9200/_snapshot/backup/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log
~~~
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]()