Skip to content
Learni
Voir tous les tutoriels
Elasticsearch

Comment déployer et optimiser Elasticsearch en 2026

Read in English

Introduction

En 2026, Elasticsearch reste le moteur de recherche leader pour les applications scalables, gérant des milliards de documents avec une latence sub-milliseconde. Ce tutoriel expert vous guide dans le déploiement d'un cluster haute disponibilité via Docker, la définition de mappings dynamiques pour des données structurées/non-structurées, des queries avancées comme les nested booléens, les aggregations hiérarchiques pour analytics, et l'optimisation via ILM et sharding. Contrairement aux bases NoSQL classiques, Elasticsearch excelle en full-text search grâce à Lucene, avec des features modernes comme vector search et security intégrée. Vous apprendrez à éviter les pièges de performance (over-sharding, mauvaise analyse) pour des clusters production-ready. À la fin, vous bookmerez ce guide pour scaler vos logs, e-commerce ou AI embeddings. (128 mots)

Prérequis

  • Docker et Docker Compose 2.20+ installés
  • curl 7.80+ ou Postman pour tester les APIs
  • Connaissances avancées en JSON, YAML et Lucene (analyzers, tokenizers)
  • Machine avec 8GB RAM minimum (pour cluster 3 nœuds)
  • Bases en scaling distribué (shards, replicas)

Déployer le cluster via Docker Compose

docker-compose.yml
version: '3.8'
services:
  elasticsearch1:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
    container_name: es1
    environment:
      - node.name=es1
      - cluster.name=learni-cluster
      - discovery.seed_hosts=es2,es3
      - cluster.initial_master_nodes=es1,es2,es3
      - bootstrap.memory_lock=true
      - 'ES_JAVA_OPTS=-Xms2g -Xmx2g'
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=false
      - xpack.security.transport.ssl.enabled=true
    ulimits:
      memlock: -1
    volumes:
      - es1_data:/usr/share/learni/data
    ports:
      - 9200:9200
    networks:
      - elastic
  elasticsearch2:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
    container_name: es2
    environment:
      - node.name=es2
      - cluster.name=learni-cluster
      - discovery.seed_hosts=es1,es3
      - cluster.initial_master_nodes=es1,es2,es3
      - bootstrap.memory_lock=true
      - 'ES_JAVA_OPTS=-Xms2g -Xmx2g'
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=false
      - xpack.security.transport.ssl.enabled=true
    ulimits:
      memlock: -1
    volumes:
      - es2_data:/usr/share/learni/data
    networks:
      - elastic
  elasticsearch3:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
    container_name: es3
    environment:
      - node.name=es3
      - cluster.name=learni-cluster
      - discovery.seed_hosts=es1,es2
      - cluster.initial_master_nodes=es1,es2,es3
      - bootstrap.memory_lock=true
      - 'ES_JAVA_OPTS=-Xms2g -Xmx2g'
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=false
      - xpack.security.transport.ssl.enabled=true
    ulimits:
      memlock: -1
    volumes:
      - es3_data:/usr/share/learni/data
    networks:
      - elastic
volumes:
  es1_data:
    driver: local
  es2_data:
    driver: local
  es3_data:
    driver: local
networks:
  elastic:
    driver: bridge

Ce docker-compose déploie un cluster 3 nœuds sécurisé avec X-Pack, 2GB heap par nœud et discovery automatique. Les volumes persistent les données ; ajustez ES_JAVA_OPTS pour votre RAM. Lancez avec docker compose up -d puis vérifiez curl -u elastic:changeme http://localhost:9200/_cat/health.

Vérification et setup initial

Après docker compose up -d, attendez 2-3 minutes pour l'élection master. Utilisez elastic:changeme comme creds par défaut (changez-les en prod via elasticsearch-setup-passwords). Testez la santé : vert signifie cluster stable. Ce setup évite les single-point-of-failure, contrairement à un nœud solo qui crash sous charge.

Générer mots de passe sécurisés

setup-passwords.sh
#!/bin/bash
docker exec es1 /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto \
  --batch \
  --url https://es1:9200 \
  > /tmp/elasticsearch-passwords.txt
cat /tmp/elasticsearch-passwords.txt
docker exec es1 curl -u elastic:$(grep elastic /tmp/elasticsearch-passwords.txt | cut -d ' ' -f4) -X GET "localhost:9200/_cluster/health?pretty"

Ce script génère des mots de passe aléatoires pour elastic/kibana et teste la connectivité. Exécutez chmod +x setup-passwords.sh && ./setup-passwords.sh. Notez les creds pour les queries suivantes ; en prod, stockez en secrets Docker ou Vault.

Créer index avec mapping avancé

create-index.json
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2,
    "analysis": {
      "analyzer": {
        "french_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "asciifolding", "french_stemmer", "stop"]
        }
      },
      "filter": {
        "french_stemmer": {
          "type": "stemmer",
          "language": "light_french"
        }
      }
    },
    "index.lifecycle.name": "warm_delete_policy"
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "french_analyzer",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "content": {
        "type": "text",
        "analyzer": "french_analyzer"
      },
      "tags": {
        "type": "keyword"
      },
      "nested_tags": {
        "type": "nested",
        "properties": {
          "name": { "type": "keyword" },
          "score": { "type": "float" }
        }
      },
      "vector": {
        "type": "dense_vector",
        "dims": 768,
        "index": true,
        "similarity": "cosine"
      },
      "timestamp": {
        "type": "date",
        "format": "strict_date_optional_time||epoch_millis"
      }
    }
  }
}

Ce mapping optimise pour du contenu français (stemming, stop words), nested objects, et dense vectors pour AI search. 3 shards/2 replicas assurent scalabilité ; ILM hooke la policy. Curl : curl -u elastic:PASS -X PUT "localhost:9200/learni_articles" -H 'Content-Type: application/json' -d @create-index.json.

Indexer des données réalistes

Avec l'index créé, bulk-indexez 1000+ docs pour tester. Utilisez _bulk pour efficacité (15x plus rapide que single POST). Cela simule un dataset e-commerce/articles avec nested tags et embeddings.

Bulk indexing de documents

bulk-index.sh
#!/bin/bash
PASS=$(grep elastic /tmp/elasticsearch-passwords.txt | cut -d ' ' -f4)

cat > bulk-data.ndjson << EOF
{"index":{"_index":"learni_articles"}}
{"title":"Tutoriel Elasticsearch avancé","content":"Optimisez vos recherches full-text avec mappings nested et vectors.","tags":["elasticsearch","search"],"nested_tags":[{"name":"expert","score":9.5},{"name":"2026","score":10}],"vector":[0.1,0.2,0.3],"timestamp":"2026-01-01T10:00:00Z"}
{"index":{"_index":"learni_articles"}}
{"title":"Scaling clusters ES","content":"Sharding et replicas pour petabytes.","tags":["cluster","scale"],"nested_tags":[{"name":"prod","score":8.8}],"vector":[0.4,0.5,0.6],"timestamp":"2026-02-01T12:00:00Z"}
EOF

curl -u elastic:$PASS -X POST "localhost:9200/_bulk" -H "Content-Type: application/x-ndjson" --data-binary @bulk-data.ndjson
curl -u elastic:$PASS "localhost:9200/learni_articles/_count?pretty"

Ce script génère un NDJSON bulk (format natif ES) avec 2 docs exemples incluant tous les champs mappés. Exécutez après mapping ; scalez à des millions via generators. Vérifiez count pour confirmer ingestion sans erreurs de mapping.

Query booléenne nested avancée

advanced-query.json
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "optimise scaling",
            "fields": ["title^2", "content"],
            "type": "best_fields",
            "fuzziness": "AUTO"
          }
        },
        {
          "nested": {
            "path": "nested_tags",
            "query": {
              "bool": {
                "must": [
                  { "match": { "nested_tags.name": "expert" } },
                  { "range": { "nested_tags.score": { "gte": 9 } } }
                ]
              }
            }
          }
        }
      ],
      "filter": [
        { "term": { "tags": "elasticsearch" } },
        { "range": { "timestamp": { "gte": "2026-01-01" } } }
      ],
      "should": [
        {
          "script_score": {
            "query": { "match_all": {} },
            "script": {
              "source": "cosineSimilarity(params.query_vector, 'vector') + 1.0",
              "params": { "query_vector": [0.15, 0.25, 0.35] }
            }
          }
        }
      ]
    }
  },
  "size": 10,
  "sort": [{ "_score": "desc" }]
}

Cette query combine must (full-text fuzzy), nested filters, term exact, range et script_score pour KNN vectors. Boost title x2 ; fuzziness tolère typos. Curl : curl -u elastic:PASS -X GET "localhost:9200/learni_articles/_search" -H 'Content-Type: application/json' -d @advanced-query.json. Score hybride text+vector.

Aggregations et analytics

Passez aux aggs pour dashboards : termes nested, buckets date_histogram. Cela alimente Kibana sans SQL.

Aggregations hiérarchiques

aggregations.json
{
  "aggs": {
    "tags_agg": {
      "terms": {
        "field": "tags.keyword",
        "size": 10,
        "order": { "doc_count": "desc" }
      },
      "aggs": {
        "avg_nested_score": {
          "nested": { "path": "nested_tags" },
          "aggs": {
            "avg_score": { "avg": { "field": "nested_tags.score" } },
            "top_tags": {
              "terms": {
                "field": "nested_tags.name.keyword",
                "size": 5
              }
            }
          }
        },
        "date_buckets": {
          "date_histogram": {
            "field": "timestamp",
            "calendar_interval": "month",
            "format": "yyyy-MM"
          }
        }
      }
    }
  },
  "size": 0
}

Aggs imbriquées : termes sur tags, nested avg/top sur sous-tags, et histo temporelle. size:0 optimise pour pure aggs. Curl comme avant ; parfait pour metrics globales sans pagination.

Tuning performances et ILM policy

ilm-policy.json
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "set_priority": { "priority": 0 },
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}

Créez policy ILM : hot->warm (shrink/merge), cold (freeze), delete auto. Curl PUT / _ilm/policy/warm_delete_policy -d @ilm-policy.json. Appliquez à index settings ; réduit stockage 80% sur cold data.

Bonnes pratiques

  • Shard sizing : 20-50GB max par shard primaire ; calculez docs / (shards * replicas).
  • Analyzers custom : Toujours tester avec _analyze API avant mapping.
  • Monitoring : Activez _cluster/stats et intégrez Prometheus exporter.
  • Backups : Snapshot vers S3/MinIO avec PUT /_snapshot/my_repo.
  • Vectors : Indexez HNSW pour >dims=1024 ; cosine > dot_product pour normalisation.

Erreurs courantes à éviter

  • Over-sharding : >50 shards/nœud tue heap ; commencez bas et reshard.
  • Mapping explosion : Évitez dynamic: true en prod ; définissez strict mappings.
  • Yellow health : Unallocated replicas ? Augmentez nœuds ou PUT /index/_settings {\"number_of_replicas\":1}.
  • OOM kills : Heap >32GB ? Activez compressed oops off ; monitor GC logs.

Pour aller plus loin

Plongez dans Elastic Cloud pour managed clusters, ou intégrez avec LangChain pour RAG. Étudiez les docs officielles Elastic 8.15. Découvrez nos formations Learni expertes en Elasticsearch et OpenSearch pour certification ECE.