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
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: bridgeCe 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
#!/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é
{
"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
#!/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
{
"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
{
"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
{
"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
_analyzeAPI avant mapping. - Monitoring : Activez
_cluster/statset 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: trueen 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.