---
title: Howto Elasticsearch
categories: web bdd nosql
...

* Documentation : <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/index.html>
* Forum : <https://discuss.elastic.co/c/elasticsearch>

[Elasticsearch](https://www.elastic.co/fr/products/elasticsearch) est un serveur de base de données écrit en Java disposant d’une interface REST HTTP. Elasticsearch est notamment utilisé dans [la stack Elastic avec Logstash et Kibana](HowtoELK).

## Installation

Vu le développement actif d'Elasticsearch, nous préconisons l'installation des paquets Debian distribués par Elasticsearch :

~~~
# apt install apt-transport-https
# echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" >> /etc/apt/sources.list.d/elastic.list
# wget -O /etc/apt/trusted.gpg.d/elastic.asc https://artifacts.elastic.co/GPG-KEY-elasticsearch
# dos2unix /etc/apt/trusted.gpg.d/elastic.asc
# chmod 644 /etc/apt/trusted.gpg.d/elastic.asc
# apt update
# apt install elasticsearch
~~~

Infos basiques sur le cluster (nom, version, etc.) :

~~~
$ curl 127.0.0.1:9200
{
  "name" : "my_node",
  "cluster_name" : "my_cluster",
  "cluster_uuid" : "RwTUfzym2fTq-URwSNGaeg",
  "version" : {
    "number" : "7.17.0",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "bee86328705acaa9a6daede7140defd4d9ec56bd",
    "build_date" : "2022-01-28T08:36:04.875279988Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
~~~


### Démarrage

Pour activer le démarrage automatique :

~~~
# systemctl enable elasticsearch.service 
Synchronizing state of elasticsearch.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable elasticsearch
Created symlink /etc/systemd/system/multi-user.target.wants/elasticsearch.service → /lib/systemd/system/elasticsearch.service.

# systemctl start elasticsearch
~~~


## Mise à jour

Selon la version de départ et la version d'arrivée, la procédure peut être triviale et sans coupure ou bien plus complexe et avec coupure. La grille des versions est disponible ici : [https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-upgrade.html]().

Lors de changements de versions, certaines fonctionnalités peuvent changer ou disparaître. Il faut donc consulter attentivement les [notes de versions](https://www.elastic.co/guide/en/elasticsearch/reference/current/es-release-notes.html), en particulier les sections [breaking changes](https://www.elastic.co/guide/en/elasticsearch/reference/current/breaking-changes.html).

Il est important de noter qu'il n'est pas possible (sauf exceptions) de revenir en arrière après une montée de version.

### Mise à jour depuis une version inférieure à 5.0

Si vous faites la mise à jour depuis une version inférieure à 5.0, il faut penser à supprimer tous les plugins de type "site" comme _head_ ou _kopf_ qu'il faudra réinstaller différemment. :

~~~
# rm -rf /usr/share/elasticsearch/plugins/{kopf,head}
~~~

La liste complète des changements est diponible sur <https://www.elastic.co/guide/en/elasticsearch/reference/5.0/breaking-changes.html>.


## Configuration de base

Les paramètres système se trouvent dans le fichier `/etc/default/elasticsearch`, les paramètres liés à la JVM sont dans `/etc/elasticsearch/jvm.options` et `/etc/elasticsearch/jvm.options.d/*.options` (*Note:* on privilégiera le deuxième - Attention, l'extention .options est importante pour que les réglages soient pris en compte) et les options applicatives (nom du cluster, nom du nœud, mémoire, réseau) se trouvent dans le fichier `/etc/elasticsearch/elasticsearch.yml`.

Il faut activer le redémarrage automatique en cas de mise à jour (classique sous Debian) dans `/etc/default/elasticsearch` :

~~~
RESTART_ON_UPGRADE=true
~~~

Via le fichier `/etc/elasticsearch/elasticsearch.yml` vous devez au minimum configurer :

~~~{.yaml}
cluster.name: foo
node.name: bar
~~~


### Changement du dossier temporaire

On peut aussi définir un **tmpdir** spécifique (important quand `/tmp` est en `noexec`).
Dans ce cas, assurez-vous de créer le répertoire avec les bons droits :

~~~
# mkdir /var/lib/elasticsearch/tmp
# chown elasticsearch: /var/lib/elasticsearch/tmp
# chmod 750 /var/lib/elasticsearch/tmp
~~~

#### Version >= 6.x

Le **tmpdir** se change dans `/etc/default/elasticsearch` : 

~~~
ES_TMPDIR=/var/lib/elasticsearch/tmp
~~~

Cette variable est ensuite utilisée par `/etc/elasticsearch/jvm.options` qui contient normalement la directive suivante :

~~~
-Djava.io.tmpdir=${ES_TMPDIR}
~~~

#### Version < 6.x

On force directement dans les options de la JVM via le fichier `/etc/elasticsearch/jvm.options.d/evolinux.options` :

~~~
ES_JAVA_OPTS="-Djava.io.tmpdir=/var/lib/elasticsearch/tmp"
~~~


### Mémoire allouée

La mémoire allouée au tas de la JVM pour ElasticSearch sont les lignes `-Xms` et `-Xmx` dans `/etc/elasticsearch/jvm.options.d/evolinux.options`.

Les deux valeurs doivent être égales.

Pour plus d'informations sur la mémoire allouée au tas de la JVM : [HowtoJava#gestion-de-la-mémoire]().


### Configuration réseau

Par défaut, Elasticsearch écoute sur 127.0.0.1 sur TCP/9200 pour son interface client 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", "10.0.42.21", "127.0.0.1"]
~~~

Il est possible d'utiliser des adresses virtuelles, telles que `_local_` (127.0.0.1 et ::1), `_site_` (IPs spécifiques à un réseau privé), `_global_` …

Tous les détails sont sur <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-network.html#network-interface-values>.


### Mode "production"

Lorsqu'Elasticsearch est configuré pour écouter sur une IP non locale, il passe en mode "production".

Il active alors un certain nombre de "bootstrap checks" qui bloquent le démarrage s'ils ne sont pas tous respectés. Les éventuels échecs sont lisibles dans le fichier de log (généralement dans `/var/log/elasticsearch/_cluster_name_.log`).

On peut aussi forcer l'exécution de ces contrôles en activant `es.enforce.bootstrap.checks` à *true* dans les options de la JVM


### Rotation et compression des logs

ElasticSearch fait de lui-même une rotation des logs en datant le fichier du jour et en créant un nouveau fichier. Par contre, aucune compression ni nettoyage n'est fait. Il est possible de déclencher une tâche tous les jours pour faire cela :

~~~{.bash}
#!/bin/sh

LOG_DIR=/var/log/elasticsearch
USER=elasticsearch
MAX_AGE=365

find ${LOG_DIR} -type f -user ${USER} \( -name "*.log.????-??-??" -o -name "*-????-??-??.log" \) -exec gzip --best {} \;
find ${LOG_DIR} -type f -user ${USER} \( -name "*.log.????-??-??.gz" -o -name "*-????-??-??.log.gz" \) -ctime +${MAX_AGE} -delete
~~~

En l'indiquant dans une crontab elle sera exécutée quand vous le souhaitez, exemple :

~~~
# crontab -l
10 1 * * * /path/to/rotate_elasticsearch_logs.sh
~~~

Le ramasse-miettes de la JVM produit aussi des logs, mais la JVM ne permet pas de faire leur rotation avec le jour dans le nom du fichier. A cause de cela, on ne peut pas juste mettre en place un simple cron de compression (car cela perturbe complètement la numérotation). A la place, on désactive la rotation par la JVM et on met en place un `logrotate` à la place :

Créer un fichier `/etc/elasticsearch/jvm.options.d/<MY_CUSTOM_OPTS>.options` et y ajouter :

~~~
-Xlog:gc*,gc+age=trace,safepoint:file=/var/log/elasticsearch/gc.log:utctime,pid,tags:filecount=0
~~~

Puis redémarrer le service `elasticsearch`.

Et créer `/etc/logrotate.d/elasticsearch` :

~~~
/var/log/elasticsearch/gc.log {
    su elasticsearch elasticsearch
    daily
    rotate 365
    compress
    nodelaycompress
    missingok
    copytruncate
    dateext
    dateformat .%Y-%m-%d
}
~~~

On peut ensuite faire le ménage et compresser les anciens `/var/log/elasticsearch/gc.log.??`, par exemple avec :

~~~
for f in $(ls -1 /var/log/elasticsearch/gc*[!.gz]); do gzip -f $f; done
~~~

Pour `logstash` :

~~~
LOG_DIR=/var/log/logstash/
USER=logstash
MAX_AGE=365
find ${LOG_DIR} -type f -user ${USER} -name "logstash-*-????-??-??.log" -exec gzip --best {} \;
~~~


## Configuration avancée

* Si on veut lancer Elasticsearch avec une JVM différente que celle par défaut sur le système, on peut définir JAVA_HOME dans `/etc/default/elasticsearch` :

~~~
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre/
~~~

Le wrapper qui lance Elasticsearch est `/usr/share/elasticsearch/bin/elasticsearch` voici les options possibles :

* `-Edefault.path.XXX=/foo/` : répertoire à utiliser (XXX peut être *conf*, *data*, *logs* etc.)
* `-d` : lancer en mode daemon
* `-p /var/run/elasticsearch/elasticsearch.pid` : chemin du fichier PID
* `--quiet` : mode silencieux

### Occupation disque

Elasticsearch prend en considération l'espace disque disponible avant d'allouer des shards sur un nœud (pour des nouveaux index ou des déplacements). Par défaut il stoppe les nouvelles allocations à 85% d'occupation ("low watermark"), tente de déplacer des shards vers d'autres nœuds à 90% ("high watermark") et enfin passe les index en lecture seule à 95% ("flood watermark"). Plus d'info sur https://www.elastic.co/guide/en/elasticsearch/reference/current/disk-allocator.html

Si le cluster (qu'il y ait un ou plusieurs nœuds) se trouve dans l'incapacité d'allouer des shards pour des données entrantes, il passera alors en état "RED" et les données de ces index ne seront pas écrites.

Les seuils peuvent être adaptés (en pourcentage ou en valeur absolue).
Il est conseillé de régler les niveaux d'alerte de l'occupation disque à des seuils cohérents avec les choix faits pour Elastisearch (par exemple 80% par défaut) afin d'avoir une alerte de monitoring avant d'avoir un cluster dégradé.
Il est également possible de désactiver complètement cette fonctionnalité, mais c'est à réserver à des situations très maîtrisées.

### Changer le thread_pool

<https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-threadpool.html>

À la manière d'une base de données tel que MySQL ou PostgreSQL, Elasticsearch dispose de plusieurs pools de connexions selon le type de requêtes. Par exemple, le pool pour les requêtes de type « search ». Par défaut il y a une auto-configuration qui est basé sur le nombre de CPU de la machine.

L'auto-configuration n'est pas toujours pertinente, car on peut saturer les pools alors qu'on ne sature pas les ressources de la machine.

Exemple de message indiquant une saturation (49 threads sur 49) :

~~~
[Running, pool size = 49, active threads = 49, queued tasks = 2279, completed tasks = 122546879]]
~~~

On peut définir nous-même les paramètres à la hausse ou à la baisse, exemple avec le pool « search » :

~~~
thread_pool:
    search:
        size: 128
        queue_size: 4096
~~~

## Monitoring

### Nagios

On check sur la page `/_cat/health` si le status n'est pas en **red**.

~~~
/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -u /_cat/health?h=st -p 9200 -r 'red' --invert-regex
~~~

**Attention** : Il faut compléter cette commande avec les options suivantes si les options de sécurisation d'Elasticsearch son activées ! (ie: Authentification HTTP & Chiffrement avec TLS) : 

* `--authorization 'remote_monitoring_user:xxxx'` - Pour l'authentification HTTP
* `-S` - Utilisation de TLS

On obtient ainsi :

~~~
/usr/lib/nagios/plugins/check_http -I 127.0.0.1 -S -u /_cat/health?h=st -p 9200 -r 'red' --invert-regex  --authorization 'remote_monitoring_user:xxxx'
~~~

### Munin

Des plugins pour Munin sont disponible sur ce dépot Github : https://github.com/y-ken/munin-plugin-elasticsearch/

Procedure d'installation :

~~~
# apt install libjson-perl
# git clone https://github.com/y-ken/munin-plugin-elasticsearch.git
# cp -a munin-plugin-elasticsearch /usr/local/src/
# chmod go+rx /usr/local/src/munin-plugin-elasticsearch
# chmod 755 /usr/local/src/munin-plugin-elasticsearch/elasticsearch_*
# ln -s /usr/local/src/munin-plugin-elasticsearch/elasticsearch_* /etc/munin/plugins/
# systemctl restart munin-node.service
~~~


Puis tester quelques sondes :

~~~
# munin-run elasticsearch_cache 
field_size.value 1410084
query_size.value 659968

# munin-run elasticsearch_jvm_memory
heap_max.value 2130051072
heap_used.value 1368633616
non_heap_committed.value 187817984
non_heap_used.value 175388648
heap_committed.value 2130051072
~~~

## Sécurisation

Par défaut, Elasticsearch n'est absolument pas sécurisé. Il a donc plusieurs opérations à faire pour sécuriser un nœud ou un cluster. 

Elastic définit 3 niveaux de sécurisation : 

* Minimal : Authentification HTTP
* Basique (Environnement de production) : Authentification HTTP + Chiffrement inter-nœuds
* Basique avec chiffrement des connexion clients : Authentification HTTP + Chiffrement inter-nœuds + HTTPS sur l'interface REST

Pour aller plus loin, c'est expliqué en détail dans la documentation d'Elasticsearch <https://www.elastic.co/guide/en/elasticsearch/reference/7.17/secure-cluster.html>

### Authentification HTTP

Il faut d'abord activer les fonctions de sécurité dans `/etc/elasticsearch/elasticsearch.yml` sur tous les membres du cluster : 

~~~
xpack.security.enabled: true
~~~

Après un redémarrage d'Elasticsearch pour la prise en compte de l'activation de xpack, on utilisera la commande `/usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto` pour créer un mot de passe pour tous les comptes par défaut. 

~~~
# /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto
Initiating the setup of passwords for reserved users elastic,apm_system,kibana_system,logstash_system,beats_system,remote_monitoring_user.
The passwords will be randomly generated and printed to the console.
Please confirm that you would like to continue [y/N]y


Changed password for user apm_system
PASSWORD apm_system = PASSWORD

Changed password for user kibana_system
PASSWORD kibana_system = PASSWORD

Changed password for user logstash_system
PASSWORD logstash_system = PASSWORD

Changed password for user beats_system
PASSWORD beats_system = PASSWORD

Changed password for user remote_monitoring_user
PASSWORD remote_monitoring_user = PASSWORD

Changed password for user elastic
PASSWORD elastic = PASSWORD
~~~

On conservera ces mots de passe avec précaution.
Les comptes ont tous des rôles (et donc des permissions) différents. 

Chacun son usage : 

* *elastic* : Super-utilisateur, administration du cluster
* *kibana_system* : Compte système pour le fonctionnement de Kibana
* *remote_monitoring_user* : Compte pour le monitoring du cluster
* etc

Par la suite, il faut s'assurer que **tous** les outils de la suite elastic (Kibana, logstash…), mais aussi le monitoring utilisent bien un compte pour s'authentifier, si non ils ne vont plus fonctionner !

Par la suite la gestion des comptes (ajout/suppression/…) se fait au travers de Kibana (après connexion) : Stack management > Users
On pourra ainsi créer des comptes applicatifs, avec 


#### Ajustement configuration monitoring

On ajoutera l'argument suivant à la commande de check_http pour s'authentifier `--authorization 'remote_monitoring_user:PASSWORD'`

#### Ajustement configuration Kibana

Dans `/etc/kibana/kibana.yml`, on spécifie le compte a utiliser :

~~~diff
--- a/etc/kibana/kibana.yml
+++ b/etc/kibana/kibana.yml
@@ -44,7 +44,7 @@ elasticsearch.hosts: ["https://localhost:9200"]
 # the username and password that the Kibana server uses to perform maintenance on the Kibana
 # index at startup. Your Kibana users still need to authenticate with Elasticsearch, which
 # is proxied through the Kibana server.
+elasticsearch.username: "kibana_system"
-#elasticsearch.username: "kibana_system"
 #elasticsearch.password: "pass"
 
 # Kibana can also authenticate to Elasticsearch via "service account tokens".
~~~

Pour le mot de passe, pour ne pas l'enregistrer en clair dans la configuration, on utilisera le keystore de Kibana

~~~
# /usr/share/kibana/bin/kibana-keystore add elasticsearch.password
Enter value for elasticsearch.password: PASSWORD
~~~

Puis redémarrer Kibana `systemctl restart kibana`

### Passage en HTTPS

Il y a plusieurs options pour. Le plus simple est de générer une PKI spéciale au cluster Elasticsearch avec les outils proposés.

#### Amorce de la PKI

~~~
# /usr/share/elasticsearch/bin/elasticsearch-certutil ca  --days 7300
This tool assists you in the generation of X.509 certificates and certificate
signing requests for use with SSL/TLS in the Elastic stack.

The 'ca' mode generates a new 'certificate authority'
This will create a new X.509 certificate and private key that can be used
to sign certificate when running in 'cert' mode.

Use the 'ca-dn' option if you wish to configure the 'distinguished name'
of the certificate authority

By default the 'ca' mode produces a single PKCS#12 output file which holds:
    * The CA certificate
    * The CA's private key

If you elect to generate PEM format certificates (the -pem option), then the output will
be a zip file containing individual files for the CA certificate and private key

Please enter the desired output file [elastic-stack-ca.p12]: 
Enter password for elastic-stack-ca.p12 : 
~~~

Le certificat est enregistré par défaut dans `/usr/share/elasticsearch/elastic-stack-ca.p12`. 
Comme ce n'est pas un dossier conventionnel, on peut choisir de le déplacer dans /etc/elasticsearch/

~~~
# mv /usr/share/elasticsearch/elastic-stack-ca.p12 /etc/elasticsearch/elastic-stack-ca.p12
~~~

#### Communication inter-nœuds

Pour chaque nœud Elasticsearch, il faut générer un certificat avec l'outil `/usr/share/elasticsearch/bin/elasticsearch-certutil`

On peut essayer, pour garder les choses au clair, d'avoir la nomenclature suivante pour les noms de certificats `$(hostname).$(service).p12`

~~~
# /usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca /etc/elasticsearch/elastic-stack-ca.p12  --days 7300
This tool assists you in the generation of X.509 certificates and certificate
signing requests for use with SSL/TLS in the Elastic stack.

The 'cert' mode generates X.509 certificate and private keys.
    * By default, this generates a single certificate and key for use
       on a single instance.
    * The '-multiple' option will prompt you to enter details for multiple
       instances and will generate a certificate and key for each one
    * The '-in' option allows for the certificate generation to be automated by describing
       the details of each instance in a YAML file

    * An instance is any piece of the Elastic Stack that requires an SSL certificate.
      Depending on your configuration, Elasticsearch, Logstash, Kibana, and Beats
      may all require a certificate and private key.
    * The minimum required value for each instance is a name. This can simply be the
      hostname, which will be used as the Common Name of the certificate. A full
      distinguished name may also be used.
    * A filename value may be required for each instance. This is necessary when the
      name would result in an invalid file or directory name. The name provided here
      is used as the directory name (within the zip) and the prefix for the key and
      certificate files. The filename is required if you are prompted and the name
      is not displayed in the prompt.
    * IP addresses and DNS names are optional. Multiple values can be specified as a
      comma separated string. If no IP addresses or DNS names are provided, you may
      disable hostname verification in your SSL configuration.

    * All certificates generated by this tool will be signed by a certificate authority (CA)
      unless the --self-signed command line option is specified.
      The tool can automatically generate a new CA for you, or you can provide your own with
      the --ca or --ca-cert command line options.

By default the 'cert' mode produces a single PKCS#12 output file which holds:
    * The instance certificate
    * The private key for the instance certificate
    * The CA certificate

If you specify any of the following options:
    * -pem (PEM formatted output)
    * -keep-ca-key (retain generated CA key)
    * -multiple (generate multiple certificates)
    * -in (generate certificates from an input file)
then the output will be be a zip file containing individual certificate/key files

Enter password for CA (elastic-stack-ca.p12) : 
Please enter the desired output file [elastic-certificates.p12]: HOSTNAME.elasticsearch.p12
Enter password for HOSTNAME.elasticsearch.p12 : 

Certificates written to /usr/share/elasticsearch/HOSTNAME.elasticsearch.p12

This file should be properly secured as it contains the private key for 
your instance.

This file is a self contained file and can be copied and used 'as is'
For each Elastic product that you wish to configure, you should copy
this '.p12' file to the relevant configuration directory
and then follow the SSL configuration instructions in the product guide.

For client applications, you may only need to copy the CA certificate and
configure the client to trust this certificate.
~~~

Par la suite on va `mv` le certificat généré vers `/etc/elasticsearch` (ou le scp vers les nœud distants)

~~~
# mv /usr/share/elasticsearch/HOSTNAME.elasticsearch.p12 /etc/elasticsearch/
# chgrp elasticsearch /etc/elasticsearch/HOSTNAME.elasticsearch.p12
# chmod g+r /etc/elasticsearch/HOSTNAME.elasticsearch.p12
~~~

Puis on ajout la configuration de Elasticsearch : 


~~~yaml
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: HOSTNAME.elasticsearch.p12
xpack.security.transport.ssl.truststore.path: HOSTNAME.elasticsearch.p12
~~~

Après, redémarrage avec `systemctl restart elasticsearch`


#### Communication client-serveur

Pour chaque nœud elastic, il faut générer un certificat avec l'outil `/usr/share/elasticsearch/bin/elasticsearch-certutil`

On peut essayer, pour garder les choses au clair, d'avoir la nomenclature suivante pour les noms de certificats `$(hostname).http.p12`
Dans le cas d'un déploiement de nombreux noeuds, on peut avoir recours a un certificat *wildcard* qu'on déploiera partout.

Ici exemple pour créer un seul certificat pour notre instance : 


~~~
# /usr/share/elasticsearch/bin/elasticsearch-certutil http

## Elasticsearch HTTP Certificate Utility

The 'http' command guides you through the process of generating certificates
for use on the HTTP (Rest) interface for Elasticsearch.

This tool will ask you a number of questions in order to generate the right
set of files for your needs.

## Do you wish to generate a Certificate Signing Request (CSR)?

A CSR is used when you want your certificate to be created by an existing
Certificate Authority (CA) that you do not control (that is, you don't have
access to the keys for that CA). 

If you are in a corporate environment with a central security team, then you
may have an existing Corporate CA that can generate your certificate for you.
Infrastructure within your organisation may already be configured to trust this
CA, so it may be easier for clients to connect to Elasticsearch if you use a
CSR and send that request to the team that controls your CA.

If you choose not to generate a CSR, this tool will generate a new certificate
for you. That certificate will be signed by a CA under your control. This is a
quick and easy way to secure your cluster with TLS, but you will need to
configure all your clients to trust that custom CA.

Generate a CSR? [y/N]n

## Do you have an existing Certificate Authority (CA) key-pair that you wish to use to sign your certificate?

If you have an existing CA certificate and key, then you can use that CA to
sign your new http certificate. This allows you to use the same CA across
multiple Elasticsearch clusters which can make it easier to configure clients,
and may be easier for you to manage.

If you do not have an existing CA, one will be generated for you.

Use an existing CA? [y/N]y

## What is the path to your CA?

Please enter the full pathname to the Certificate Authority that you wish to
use for signing your new http certificate. This can be in PKCS#12 (.p12), JKS
(.jks) or PEM (.crt, .key, .pem) format.
CA Path: /etc/elasticsearch/elastic-stack-ca.p12
Reading a PKCS12 keystore requires a password.
It is possible for the keystore's password to be blank,
in which case you can simply press <ENTER> at the prompt
Password for elastic-stack-ca.p12:

## How long should your certificates be valid?

Every certificate has an expiry date. When the expiry date is reached clients
will stop trusting your certificate and TLS connections will fail.

Best practice suggests that you should either:
(a) set this to a short duration (90 - 120 days) and have automatic processes
to generate a new certificate before the old one expires, or
(b) set it to a longer duration (3 - 5 years) and then perform a manual update
a few months before it expires.

You may enter the validity period in years (e.g. 3Y), months (e.g. 18M), or days (e.g. 90D)

For how long should your certificate be valid? [5y] 

## Do you wish to generate one certificate per node?

If you have multiple nodes in your cluster, then you may choose to generate a
separate certificate for each of these nodes. Each certificate will have its
own private key, and will be issued for a specific hostname or IP address.

Alternatively, you may wish to generate a single certificate that is valid
across all the hostnames or addresses in your cluster.

If all of your nodes will be accessed through a single domain
(e.g. node01.es.example.com, node02.es.example.com, etc) then you may find it
simpler to generate one certificate with a wildcard hostname (*.es.example.com)
and use that across all of your nodes.

However, if you do not have a common domain name, and you expect to add
additional nodes to your cluster in the future, then you should generate a
certificate per node so that you can more easily generate new certificates when
you provision new nodes.

Generate a certificate per node? [y/N]y

## What is the name of node #1?

This name will be used as part of the certificate file name, and as a
descriptive name within the certificate.

You can use any descriptive name that you like, but we recommend using the name
of the Elasticsearch node.

node #1 name: HOSTNAME.example.net

## Which hostnames will be used to connect to HOSTNAME.example.net?

These hostnames will be added as "DNS" names in the "Subject Alternative Name"
(SAN) field in your certificate.

You should list every hostname and variant that people will use to connect to
your cluster over http.
Do not list IP addresses here, you will be asked to enter them later.

If you wish to use a wildcard certificate (for example *.es.example.com) you
can enter that here.

Enter all the hostnames that you need, one per line.
When you are done, press <ENTER> once more to move on to the next step.

HOSTNAME.example.net
localhost

You entered the following hostnames.

 - HOSTNAME.example.net
 - localhost

Is this correct [Y/n]

## Which IP addresses will be used to connect to HOSTNAME.example.net?

If your clients will ever connect to your nodes by numeric IP address, then you
can list these as valid IP "Subject Alternative Name" (SAN) fields in your
certificate.

If you do not have fixed IP addresses, or not wish to support direct IP access
to your cluster then you can just press <ENTER> to skip this step.

Enter all the IP addresses that you need, one per line.
When you are done, press <ENTER> once more to move on to the next step.

192.0.2.10
2001:DB8::10
127.0.0.1
::1

You entered the following IP addresses.

 - 192.0.2.10
 - 2001:DB8::10
 - 127.0.0.1
 - ::1

Is this correct [Y/n]y

## Other certificate options

The generated certificate will have the following additional configuration
values. These values have been selected based on a combination of the
information you have provided above and secure defaults. You should not need to
change these values unless you have specific requirements.

Key Name: HOSTNAME.example.net
Subject DN: CN=HOSTNAME, DC=example, DC=net
Key Size: 2048

Do you wish to change any of these options? [y/N]
Generate additional certificates? [Y/n]n

## What password do you want for your private key(s)?

Your private key(s) will be stored in a PKCS#12 keystore file named "http.p12".
This type of keystore is always password protected, but it is possible to use a
blank password.

If you wish to use a blank password, simply press <enter> at the prompt below.
Provide a password for the "http.p12" file:  [<ENTER> for none]

## Where should we save the generated files?

A number of files will be generated including your private key(s),
public certificate(s), and sample configuration options for Elastic Stack products.

These files will be included in a single zip archive.

What filename should be used for the output zip file? [/usr/share/elasticsearch/elasticsearch-ssl-http.zip] 

Zip file written to /usr/share/elasticsearch/elasticsearch-ssl-http.zip
~~~

On récupère ainsi une archive zip contenant tous les fichiers nécessaires : 

~~~
~/tmp# unzip elasticsearch-ssl-http.zip 
~/tmp# tree .
.
├── elasticsearch
│   ├── http.p12
│   ├── README.txt
│   └── sample-elasticsearch.yml
├── elasticsearch-ssl-http.zip
└── kibana
    ├── elasticsearch-ca.pem
    ├── README.txt
    └── sample-kibana.yml

2 directories, 7 files
~~~

Le dossier `elasticsearch` contient le fichier certificat + clé au format *p12* pour Elasticsearch ainsi qu'un extrait de configuration d'exemple pour configurer le HTTPS client. Le dossier `kibana` contient le certificat de la CA (pour pouvoir valider le certificat donné par le serveur Elasticsearch) ainsi qu'un extrait de configuration d'exemple pour activer le tls client.


> **Note** On pourra ré-utiliser le certificat CA `kibana/elasticsearch-ca.pem` avec tous les services/logiciels clients du cluster Elasticsearch pour pouvoir vérifier la validité du certificat donné par le serveur.


On déplace les fichiers aux bons endroits
~~~
~/tmp# mv elasticsearch/http.p12 /etc/elasticsearch/HOSTNAME.http.p12 
~/tmp# chgrp elasticsearch /etc/elasticsearch/HOSTNAME.http.p12
~/tmp# chmod g+r /etc/elasticsearch/HOSTNAME.http.p12
~/tmp#                  
~/tmp# mv kibana/elasticsearch-ca.pem /etc/kibana/elasticsearch-ca.pem 
~/tmp# chgrp kibana /etc/kibana/elasticsearch-ca.pem
~/tmp# chmod g+r /etc/kibana/elasticsearch-ca.pem
~~~

Puis on ajoute l'extrait de configuration suivant pour elasticsearch : 

~~~yaml
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: HOSTNAME.http.p12
~~~

Après, redémarrage avec `systemctl restart elasticsearch`


#### Ajustement configuration monitoring

On ajoutera l'argument suivant à la commande de check_http pour passer en HTTPS `-S`

#### Ajustement configuration Kibana

Dans `/etc/kibana/kibana.yml`, on doit faire les changelents suivants :

~~~diff
--- a/kibana/kibana.yml
+++ b/kibana/kibana.yml
@@ -31,7 +31,7 @@ server.basePath: "/kibana"
 #server.name: "your-hostname"
 
 # The URLs of the Elasticsearch instances to use for all your queries.
-#elasticsearch.hosts: ["http://localhost:9200"]
+elasticsearch.hosts: ["https://localhost:9200"]
 
 # Kibana uses an index in Elasticsearch to store saved searches, visualizations and
 # dashboards. Kibana creates a new index if the index doesn't already exist.
@@ -65,7 +65,7 @@ server.basePath: "/kibana"
 
 # Optional setting that enables you to specify a path to the PEM file for the certificate
 # authority for your Elasticsearch instance.
-#elasticsearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ]
+elasticsearch.ssl.certificateAuthorities: [ "/etc/kibana/elasticsearch-ca.pem" ]
 
 # To disregard the validity of SSL certificates, change this setting's value to 'none'.
 #elasticsearch.ssl.verificationMode: full
~~~

Puis redémarrer Kibana `systemctl restart kibana`

## Snapshots et sauvegardes

### Configuration des snapshots

Documentation : <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-snapshots.html>

Il faut définir un répertoire pour stocker les snapshots :

~~~
# install -d -o elasticsearch -g elasticsearch -m 750 /home/backup-elasticsearch
~~~

*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.

Après avoir créé le répertoire des snapshots et chown(8) sur l’usager / groupe elasticsearch.

On précise le répertoire des snapshots dans la configuration `/etc/elasticsearch/elasticsearch.yml` :

~~~
path.repo: /home/backup-elasticsearch
~~~

Il faut redémarrer Elasticsearch et créer un ensemble de snapshots (snapshots repository) ainsi :

~~~
$ curl -H'Content-Type: application/json' -XPUT 'http://localhost:9200/_snapshot/snaprepo' -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 :

~~~
$ 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
~~~

Pour supprimer un snapshot :

~~~
$ curl -s -XDELETE "localhost:9200/_snapshot/foo/snapshot_test"
~~~

### Sauvegarde via snapshots

Il faut donc avoir une directive `path.repo: ["/home/backup-elasticsearch"]` prise en compte par Elasticsearch avec un répertoire existant.
Puis on crée 

Créons un snapshot repository nommé `snaprepo` :

~~~
$ curl -XPUT 'http://localhost:9200/_snapshot/snaprepo' -d '{
"type": "fs",
    "settings": {
        "location": "/home/backup-elasticsearch",
        "compress": true
    }
}'
{"acknowledged":true}
~~~

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") :

~~~
$ date=$(date +%H)
$ curl -s -X DELETE "127.0.0.1:9200/_snapshot/snaprepo/h${date}" | grep -v -Fx '{"acknowledged":true}'
$ curl -s -X PUT "127.0.0.1:9200/_snapshot/snaprepo/h${date}?wait_for_completion=true" -o /tmp/es_snapshot_h${date}.log
~~~

Plus classiquement pour avoir un snapshot par jour :

~~~
$ date=$(date +%F)
$ curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot_${date}" | grep -v -Fx '{"acknowledged":true}'
$ curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log
~~~

On peut ensuite purger les snapshots vieux de plus de 10 jours ainsi :

~~~
$ cd /home/backup-elasticsearch/snaprepo
$ for i in $(ls -1d snapshot-* | head -n -10 | sed s'/snapshot-snapshot_//g'); do curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snaps
hot_${i}"; done
~~~

Dans le cas de snapshot pour un ElasticSearsch clusterisé :

~~~
# if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2'
# then
#     curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" -o /tmp/es_delete_snapshot.daily.log
#     curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" -o /tmp/es_snapshot.daily.log
# else
#     echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
# fi
# for snapshot in $(curl -s -XGET "localhost:9200/_snapshot/snaprepo/_all?pretty=true" | grep -Eo 'snapshot_[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n -10); do
#     curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/${snapshot}" | grep -v -Fx '{"acknowledged":true}'
# done
# date=$(date +%F)
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" -o /tmp/es_snapshot_${date}.log
~~~

### Restauration d'un snapshot

Le snapshot doit être listé dans le snapshot repository.

Pour restaurer le snapshot `snapshot.daily` :

~~~
$ curl -XPOST "localhost:9200/_snapshot/snaprepo/snapshot.daily/_restore"
{"accepted":true}
~~~

> *Note* : si vous avez un message d'erreur du type `{"error":"SnapshotRestoreException[[snaprepo:snapshot.daily] cannot restore index [foo] because it's open]","status":500}` vous pouvez fermer l'index en faisant `curl -XPOST "localhost:9200/foo/_close`



## cluster

<https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-cluster.html>

Si l'on compare à d'autres services (MySQL, PostgreSQL, MongoDB…) la gestion d'un cluster Elasticsearch est vraiment simple.
Il faut lancer plusieurs instances Elasticsearch sur un réseau avec le même **cluster.name** et un **node.name** différent, et il suffit d'indiquer une (ou plusieurs) adresse(s) IP qui va permettre à l'instance de communiquer avec un (ou plusieurs) autre(s) nœud(s) :

~~~
cluster.name: foo
node.name: bar0
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["192.0.2.42"]
~~~

En démarrant un 2ème nœud **bar1** on verra ainsi dans les logs de l'instance master bar0 que le cluster passe de YELLOW à GREEN :

~~~
[INFO ][o.e.c.s.ClusterService   ] [bar0] added {{bar1}{_jwXmQsAQEyseSOc4pG2IA}{PTpsbMBAQEKTs_OFgW_RYw}{192.0.2.42}{192.0.2.42:9301},}, reason: zen-disco-node-join[{bar1}{_jwXmQsAQEyseSOc4pG2IA}{PTpsbMBAQEKTs_OFgW_RYw}{192.0.2.42}{192.0.2.42:9301}]
[WARN ][o.e.d.z.ElectMasterService] [bar0] value for setting "discovery.zen.minimum_master_nodes" is too low. This can result in data loss! Please set it to at least a quorum of master-eligible nodes (current value: [-1], total number of master-eligible nodes used for publishing in this round: [2])
[INFO ][o.e.c.r.a.AllocationService] [bar0] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-data-2][0]] ...]).
~~~


On peut consulter le statut du cluster via la requête :

~~~
$ curl 'http://localhost:9200/_nodes?pretty=true'

{
  "_nodes" : {
    "total" : 3,
    "successful" : 3,
    "failed" : 0
  },
  "cluster_name" : "foo",
  "nodes" : {
    "4Tt8FlV4TG2Hf_1T4EayQg" : {
      "name" : "bar0",
[...]
~~~

On voit ainsi qu'il y a 3 nœuds dans le cluster.

Si l'on coupe le master, un autre est élu :

~~~
[INFO ][o.e.d.z.ZenDiscovery     ] [bar2] master_left [{bar0}{4Tt8FlV4TG2Hf_1T4EayQg}{5nbXw3F5RWCWjUSiRzv9DA}{192.0.2.42}{192.0.2.42:9300}], reason [shut_down]
[WARN ][o.e.d.z.ZenDiscovery     ] [bar2] master left (reason = shut_down), current nodes: {{bar2}{5wUhAI79SsyY-DKv4va26Q}{_VQTiZXxTCi2KIsijyQBpg}{192.0.2.42}{192.0.2.42:9302},{bar1}{_jwXmQsAQEyseSOc4pG2IA}{_pQMtkFLSTe3p-eDHMkalw}{192.0.2.42}{192.0.2.42:9301},}
[INFO ][o.e.c.s.ClusterService   ] [bar2] removed {{bar0}{4Tt8FlV4TG2Hf_1T4EayQg}{5nbXw3F5RWCWjUSiRzv9DA}{192.0.2.133}{192.0.2.133:9300},}, reason: master_failed ({bar0}{4Tt8FlV4TG2Hf_1T4EayQg}{5nbXw3F5RWCWjUSiRzv9DA}{192.0.2.42}{192.0.2.42:9300})
[INFO ][o.e.c.r.a.AllocationService] [bar2] Cluster health status changed from [GREEN] to [YELLOW] (reason: [removed dead nodes on election]).
[INFO ][o.e.c.s.ClusterService   ] [bar2] new_master {bar2}{5wUhAI79SsyY-DKv4va26Q}{_VQTiZXxTCi2KIsijyQBpg}{192.0.2.133}{192.0.2.133:9302}, reason: zen-disco-elected-as-master ([0] nodes joined)
[INFO ][o.e.c.r.DelayedAllocationService] [bar2] scheduling reroute for delayed shards in [59.8s] (2 delayed shards)
[INFO ][o.e.c.r.a.AllocationService] [bar2] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-es-2-2016.11.06][0]] ...]).
~~~

## Plugins

Elasticsearch dispose d'un système de plugins, certains officiels et d'autres communautaires.
Un **redémarrage du service** est nécessaire pour prendre en comptes ces changements.

### Installation, suppression

Les plugins officiels peuvent être installés en indiquant simplement leur nom :

~~~
# /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-phonetic
~~~

Pour installer une nouvelle version d'un plugin, il faut d'abord le supprimer :

~~~
# /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-phonetic
# /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-phonetic
~~~

### Liste

~~~
# /usr/share/elasticsearch/bin/elasticsearch-plugin list
analysis-phonetic
~~~

### Blocage suite à mise à jour

**Attention** : certains plugins (ex. : _analysis-icu_ et _analysis-phonetic_) sont étroitement liés à une version d'Elasticsearch et peuvent bloquer son démarrage en cas d'incohérence. On aura alors une erreur de ce type dans les logs du cluster :

~~~
[2017-10-30T09:51:46,918][ERROR][o.e.b.Bootstrap          ] Exception
java.lang.IllegalArgumentException: plugin [analysis-phonetic] is incompatible with version [5.6.3]; was designed for version [5.6.2]
~~~

On peut utiliser ce script pour automatiser la mise à jour de tous les plugins et le redémarrage d'Elasticsearch :

~~~{.bash}
#!/bin/bash

set -e
set -u

PLUGIN_BIN=/usr/share/elasticsearch/bin/elasticsearch-plugin
NEED_RESTART=""

for plugin in $(${PLUGIN_BIN} list); do
    "${PLUGIN_BIN}" remove "${plugin}"
    "${PLUGIN_BIN}" install "${plugin}"
    NEED_RESTART="1"
done

if [ -n "${NEED_RESTART}" ]; then
    systemctl restart elasticsearch
fi

exit 0
~~~

## Principe de fonctionnement d'Elasticsearch

_Basé sur le livre <http://exploringelasticsearch.com/book>_

On utilisera l'outil cURL pour faire les requêtes.
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 base de données 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.

~~~
$ java -jar elastic-loader.jar http://localhost:9200 datasets/movie_db.eloader
~~~

La BDD movie_db contient quelques noms de films, avec leurs informations associés (genre, date, acteurs, …).

Pour consulter tout son contenu :

~~~
$ curl http://localhost:9200/movie_db/_search?pretty=true
~~~

### En créant une base de données

#### Opérations CRUD

Créer un index (équivalent d'une base de données) nommé *planet* :

~~~
$ curl -X PUT localhost:9200/planet
{"acknowledged":true,"shards_acknowledged":true}
~~~

Créer un type de donnée nommé « hacker » :

~~~
$ curl -X PUT localhost:9200/planet/hacker/_mapping -d '
{
    "hacker": {
        "properties": {
            "handle": {
                "type": "string"
            },
            "age": {
                "type": "long"
            }
        }
    }
}
'
~~~

Créer un document de type hacker avec l'ID 1 :

~~~
$ curl -X PUT localhost:9200/planet/hacker/1 -d '{"handle": "jean-michel", "age": 18}'
{"_index":"planet","_type":"hacker","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true
~~~

Voir son contenu :

~~~
$ curl localhost:9200/planet/hacker/1?pretty=true
{
  "_index" : "planet",
  "_type" : "hacker",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "handle" : "jean-michel",
    "age" : 18
  }
}
~~~

Mise à jour du champ âge :

~~~
$ curl -X POST localhost:9200/planet/hacker/1/_update -d '{"doc": {"age": 19}}'
{"_index":"planet","_type":"hacker","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":1,"failed":0}}
~~~

Suppression du document :

~~~
$ curl -X DELETE localhost:9200/planet/hacker/1
{"found":true,"_index":"planet","_type":"hacker","_id":"1","_version":3,"result":"deleted","_shards":{"total":2,"successful":1,"failed":0}}
~~~

#### Recherche basique

Recréons un index de test :

~~~
$ curl -X DELETE localhost:9200/planet
{"acknowledged":true}

$ curl -X PUT localhost:9200/planet -d '
{
    "mappings": {
        "hacker": {
            "properties": {
                "handle": {
                    "type": "string"
                },
                "hobbies": {
                    "type": "string",
                    "analyzer": "snowball"
                }
            }
        }
    }
}
'
~~~

Ajoutons quelques documents :

~~~
$ curl -X PUT localhost:9200/planet/hacker/1 -d '
{
    "handle": "mark",
    "hobbies": ["rollerblading", "hacking", "coding"]
}
'

$ curl -X PUT localhost:9200/planet/hacker/2 -d '
{
    "handle": "gondry",
    "hobbies": ["writing", "skateboarding"]
}
'

$ curl -X PUT localhost:9200/planet/hacker/3 -d '
{
    "handle": "jean-michel",
    "hobbies": ["coding", "rollerblades"]
}
'
~~~

Recherchons ceux qui ont pour hobby *rollerblading* :

~~~
$ curl -X POST localhost:9200/planet/hacker/_search?pretty=true -d '
{
    "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 qu’il 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 ?).

## Curator

Curator est un outil indépendant d'Elasticsearch qui permet de réaliser des opérations diverses sur un cluster, le plus souvent déclenchées par des taches cron, un peu à la manière de logrotate.

Documentation : https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html

### Installation

~~~
# echo "deb https://packages.elastic.co/curator/5/debian9 stable main" >> /etc/apt/sources.list.d/elastic.list
# apt update
# apt install curator
~~~

### Configuration

Curator s'appuie sur un fichier de configuration qui contient toutes les informations pour se connecter au cluster Elasticsearch (adresse, authentification, chiffrement…). 

Dans le de l'exécution via cron, il est conseillé d'envoyer les logs dans un fichier plutôt que dans la sortie standard.

~~~
[…]
logging:
  loglevel: INFO
  logfile: /var/log/curator/curator.log
[…]
~~~

Note : ne pas oublier le logrotate :

~~~
# cat /etc/logrotate.d/curator
/var/log/curator/*.log {
        monthly
        rotate 12
        compress
        delaycompress
        missingok
        notifempty
}
~~~

### Actions

Curator utilise également un fichier d'action (potentiellement différent à chaque appel). Il doit contenir les filtres permettant de déterminer quels index sont concernés (motif sur le nom, âge, taille ou nombre de documents…), puis une série d'actions (compression, déplacement, optimisation.)

## API "cat"

Elasticsearch expose une API depremier niveau appelée "cat". Elle permet d'obtenir des informations sur un cluster avec un formattage texte facile à manipuler en ligne de commande là où les sorties JSON le sont moins.

Toute "la documentation est ici":https://www.elastic.co/guide/en/elasticsearch/reference/current/cat.html

### État général du cluster

~~~
$ curl -s 127.0.0.1:9200/_cat/health
1545659505 14:51:45 cluster-name green 4 4 418 207 0 0 0 0 - 100.0%
~~~

Avec l'option `v` on peut avoir les en-têtes de colonnes :

~~~
$ curl -s 127.0.0.1:9200/_cat/health?v
epoch      timestamp cluster  status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1545659712 14:55:12  cluster-name green           4         4    418 207    0    0        0             0                  -                100.0%
~~~

### Liste des nœuds

~~~
$ curl -s 127.0.0.1:9200/_cat/nodes?v
ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
10.0.0.13           52          83   3    0.01    0.14     0.16 mdi       *      node3
10.0.0.11           32          92   0    0.09    0.07     0.09 mdi       -      node1
10.0.0.10           51          87   0    0.36    0.20     0.12 mdi       -      node0
10.0.0.12           29          87   9    0.31    0.38     0.31 mdi       -      node2
~~~

Dans la dernière colonne, l'étoire indique le nœud master. Cette info est aussi disponible par `_cat/master` :

~~~
$ curl -s 127.0.0.1:9200/_cat/master
C6blxAg5SrmkCx8C80qiig 10.0.0.13 10.0.0.13 node3
~~~

### Liste des index

On peut récupérer la liste de tous les index, classés par nom :

~~~
$ curl -s 127.0.0.1:9200/_cat/indices | sort -k 3
green open index1 vewTWN8WRB-2V6xIgE7oQQ  1 1  2190434      0      2gb      1gb
green open index2 k0a2yjwSS_CKKSydoVzcoQ  1 1  2164950      0    1.9gb 1020.5mb
green yellow index3 rMtU36wXSNS9q9w6BDp0sA  1 1  3156725      0    2.9gb    1.4gb
[…]
~~~

On peut limiter la liste selon la valeur de certaines colones (exemple :`health`) :

~~~
$ curl -s 127.0.0.1:9200/_cat/indices?health=yellow
green yellow index3 rMtU36wXSNS9q9w6BDp0sA  1 1  3156725      0    2.9gb    1.4gb
~~~

### Consulter l'état des shards

On peut voir si les shards sont correctement alloués (`STARTED`) ou pas.

~~~
$ curl -s 127.0.0.1:9200/_cat/shards
index3     1 p STARTED        0    191b 10.0.0.13  node3
index3     1 r STARTED        0    191b 10.0.0.12 node2
index3     1 r UNASSIGNED
~~~

Ici on voit que `index3` a un shard non alloué. Pour savoir pourquoi on eput interroger l'état de l'index à propos de ses chards :

~~~
$ curl 127.0.0.1:9200/index3/_shard_stores?pretty
{
  "indices" : {
    "index3" : {
      "shards" : {
        "1" : {
          "stores" : [
            {
              "wPZvU6MUQkmhSb4YrszYeA" : {
                "name" : "node3",
                "ephemeral_id" : "6EK7veXhQrKAi42UB_97Zw",
                "transport_address" : "10.0.0.13:9300",
                "attributes" : { }
              },
              "allocation_id" : "RkL8f2wNS5CVL-MJYVHDaA",
              "allocation" : "primary"
            },
            {
              "yuF0yDqHTeyj7Z3rDt9HXw" : {
                "name" : "node2",
                "ephemeral_id" : "sIkpMVcGTyCkXx4RDd_mgA",
                "transport_address" : "10.0.0.12:9300",
                "attributes" : { }
              },
              "allocation_id" : "-piepczfSEiyTV9vN-l5MQ",
              "allocation" : "replica"
            }
          ]
        }
      }
    }
  }
}
~~~

On voir qu'il y a bien un _primary_ mais un seul _replica_.
On peut alors interroger le cluster sur l'état des allocations :

~~~
$ curl '127.0.0.1:9200/_cluster/allocation/explain?pretty'
~~~

La sortie est en JSON et peut être assez verbeuse.
Elle contiendra des explications a priori assez claires sur d'éventuels soucis d'allocations de shards.
Dans cet exemple (tronqué) un shard _replica_ ne peut pas être alloué sur un nœud dont la version d'Elasticsearch n'est pas cohérente.

~~~.json
{
  "index" : "index3",
  "shard" : 1,
  "primary" : false,
  "current_state" : "unassigned",
  "unassigned_info" : {
    "reason" : "REPLICA_ADDED",
    "at" : "2019-07-31T07:04:15.845Z",
    "last_allocation_status" : "no_attempt"
  },
  "can_allocate" : "no",
  "allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes",
  "node_allocation_decisions" : [
    {
      "node_id" : "--e8PpGsSKifZ5lFlDkW3A",
      "node_name" : "node1",
      "transport_address" : "10.0.0.11:9300",
      "node_decision" : "no",
      "deciders" : [
        {
          "decider" : "node_version",
          "decision" : "NO",
          "explanation" : "target node version [5.6.6] is older than the source node version [5.6.15]"
        }
      ]
    }
  ]
}
~~~

## 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]()

### Lancer plusieurs instances sur un même système pour du test

Il faut définir 3 paramètres minimum pour lancer une instance Elasticsearch :
* default.path.conf (répertoire de configuration)
* default.path.data (répertoire pour les données)
* default.path.logs (répertoire pour les logs)

~~~
# cp -pr /etc/elasticsearch /usr/local/etc/elasticsearch0
# mkdir -p /srv/es-data/bar0 /srv/es-log/bar0
# chown elasticsearch: /srv/es-data/bar0 /srv/es-log/bar0
~~~

Configuration via `/usr/local/etc/elasticsearch0/elasticsearch.yml` :

~~~
cluster.name: foo
node.name: bar0
node.master: true
node.data: true
~~~

On peut ensuite lancer cette nouvelle instance en ligne de commande :

~~~
# su -s /bin/sh elasticsearch
$ /usr/share/elasticsearch/bin/elasticsearch -Edefault.path.conf=/usr/local/etc/elasticsearch0 \
 -Edefault.path.data=/srv/es-data/bar0 -Edefault.path.logs=/srv/es-log/bar0
~~~

Note : si Elasticsearch nécessite une version de Java différent (Java 8 pour Elasticsearch 5.0), il suffit d'ajouter la variable JAVA_HOME en début de ligne de commande :

~~~
$ JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre/ /usr/share/elasticsearch/bin/elasticsearch [...]
~~~

### Erreur "missing authentication token for REST request"

Si vous obtenez une erreur HTTP *401 Unauthorized* avec le détail "missing authentication token for REST request...", c'est probablement que le plugin [shield](https://www.elastic.co/guide/en/shield/current/installing-shield.html) est activé.

### Supprimer proprement des vieux index

Si on utilise Elasticsearch pour des logs, voici une solution pour supprimer d'anciennes données avec [Curator](https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html), mais lorsque ça n'est pas possible (à cause de compatibilité avec le système) on peut recourir à une approche manuelle:

Voici un exemple pour conserver que les 20 derniers index nommés `logstash-*`:

~~~{.bash}
#!/bin/bash

#garder les 10 plus récent
indices=$(curl http://127.0.0.1:9200/_cat/indices/logstash-*?h=i | sort | head -n -10)

for index in ${indices}; do
    # echo Delete ${index}
    curl --silent --fail --show-error -XDELETE http://127.0.0.1:9200/${index} > /dev/null
done
~~~


### Lister les index

Cette commande est pratique pour voir la taille que prennent les index

~~~
$ curl -XGET "http://localhost:9200/_cat/shards?v"
~~~

### Lister le statut des index

~~~
$ curl -XGET 'http://127.0.0.1:9200/_cluster/health?level=indices&pretty'
~~~

### Relancer l'allocation des shards

Dans certains cas le cluster ne parvient pas à allouer des shards (primaires ou réplicas).
Il est possible de forcer le reroutage avec cette commande :

~~~
$ curl -XPOST 'http://127.0.0.1:9200/_cluster/reroute?retry_failed'
~~~

### Modifier en masse le nombre de replica :

Par exemple sur un cluster avec un seul nœud, on ne veut pas de replica. On prend alors tous les index en état "yellow" et on passe le nombre de replica à "0".

~~~
# for index in $(curl 127.0.0.1:9200/_cat/indices/?h=index&health=yellow); do curl -X PUT 127.0.0.1:9200/$index/_settings -H 'Content-Type: application/json' -d '{ "index": {"number_of_replicas": 0} }'; done
~~~

### Nombre de replicas par défaut

On utilise un [template](https://www.elastic.co/guide/en/elasticsearch/reference/7.8/indices-templates.html) qui définira la valeur par défaut du nombre de replicas de tous les nouveaux index :

~~~
# curl -X PUT "localhost:9200/_template/template_1?pretty" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["*"],
  "settings": {
    "number_of_replicas": 0
  }
}'
~~~

### Consulter l'état du recovery

Cette requête donnera une vision sur l'état des synchro et d'éntuelles opérations de recovery :

~~~
# curl -s 127.0.0.1:9200/_cat/recovery?v
~~~

### Licence X-Pack

Le module propriétaire X-Pack nécessite une licence pour une utilisation avancée, voici la procédure :

~~~
$ curl -XPUT 'http://127.0.0.1:9200/_xpack/license' -H "Content-Type: application/json" -d @license.json
{"acknowledged":true,"license_status":"valid"}

$ curl -XGET 'http://127.0.0.1:9200/_xpack/license'
~~~

> *Note* : si l'authentification est activée, on ajoutera l'option pour préciser un utilisateur `-u jdoe`


### Pas de réponse au curl ou erreur "Empty reply from server"

Si lorsque on tente un curl pour voir l'état du cluster ES, et qu'on a pas de réponse ou alors si en mode verbose on a l'erreur suivante "Empty reply from server" :

~~~
# curl -vs 127.0.0.1:9200/_cat/indices
* Expire in 0 ms for 6 (transfer 0x55efb2682530)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55efb2682530)
* Connected to 127.0.0.1 (127.0.0.1) port 9200 (#0)
> GET /_cat/indices HTTP/1.1
> Host: 127.0.0.1:9200
> User-Agent: curl/7.64.0
> Accept: */*
> 
* Empty reply from server
* Connection #0 to host 127.0.0.1 left intact
~~~

C'est probablement que les requêtes au serveur ES doivent se faire en https, avec authentification, on peut se connecter comme ceci avec la commande suivante :

~~~
curl -s -k https://127.0.0.1:9200/_cat/indices --user user:password
~~~


### Réinitialiser le mot de passe de `elastic` (sans accès à un utilisateur `superuser`)

Pour réinitialiser le mot de passe de `elastic` (ou de tout autre utilisateur) lorsqu'on n'a pas déjà accès à un utilisateur ayant le rôle `superuser`, il faut créer un utilisateur ayant le rôle superuser dans les fichiers `/etc/elasticsearch/users` de chaque node (il doit être créé sur chaque node) :

~~~
# sudo -g elasticsearch /usr/share/elasticsearch/bin/elasticsearch-users useradd <admin> -r superuser
~~~

Il est ensuite possible d'utiliser cet utilisateur pour réinitialiser le mot de passe de n'importe quel utilisateur (par le biais de kibana ou de l'API). Dans le cas de kibana, la gestion des utilisateur est dans "[Hamburger Menu]">Management>"Stack Management">Security>Users. Pour [l'API](https://www.elastic.co/guide/en/elasticsearch/reference/7.15/security-api-change-password.html) il s'agit d'une requête `POST` vers `/_security/user/<username>/_password` avec un json contenant soit une entrée nommée "password" (mot de passe en clair) soit une entrée nommée `password_hash` (le hash du mot de passe).

Il est fortement conseillé de supprimer l'utilisateur créé après cette action :

~~~
# sudo -g elasticsearch /usr/share/elasticsearch/bin/elasticsearch-users userdel <admin>
~~~


### OutOfMemoryError: Java heap space

Solution : augmenter la mémoire allouée à la pile d'appel dans les arguments Xms et Xmx de la JVM dans `/etc/elasticsearch/jvm.options` :

~~~
# Xms represents the initial size of total heap space
-Xms3g
# Xmx represents the maximum size of total heap space
-Xmx3g
~~~

Les deux valeurs doivent être égales.
