Elasticsearch —— 分布式日志存储、检索与分析底座

运维监控软件手册 04-日志监控与分析 22.4 KB 原文

Elasticsearch —— 分布式日志存储、检索与分析底座

分类:04 日志监控与分析 适用:Linux/容器/K8s;单节点到数百节点集群;日均 GB~TB 级日志、指标、APM 数据 一句话:基于 Lucene 的近实时分布式搜索分析引擎,是 ELK/EFK 链路中负责"存得下、查得快"的存储检索层 官方:https://www.elastic.co/elasticsearch/ 授权:Elastic License 2.0(源码可见、可自建商用但不得作为托管服务转售;基础安全、ILM、快照、监控免费;机器学习高级功能、CCR、可搜索快照等属付费订阅。另有 Apache 2.0 的 OpenSearch 分支)

1. 它解决什么问题

海量日志"写文件容易、查起来难":grep 跨主机跨天几乎不可用;关系库全文检索慢且难扩展;原始日志无结构、难聚合。ES 把日志解析成 JSON 文档,建立倒排索引 + 列式 doc_values,提供:

  • 近实时检索:写入后经 refresh(默认 1s)即可被搜到,秒级定位某 trace_id / 错误码。
  • 任意维度聚合:按状态码、URL、主机、时间桶做 terms/date_histogram,替代离线脚本。
  • 水平扩展与高可用:分片 + 副本,节点故障自动重分配。
  • 生命周期治理:索引模板 + ILM + 冷热分层 + 快照,控制成本与留存。
  • 统一观测底座:日志、指标、APM 链路共用一套存储查询。

它不采集、不解析(由 Beats/Logstash/Fluentd/Fluent Bit 完成),也不做界面(由 Kibana 完成)。

2. 核心概念与架构

术语表

术语 含义
Document 文档 一条 JSON 日志,最小存储单元
Index 索引 逻辑命名空间,一类文档集合(类似"表")
Data stream 数据流 面向时序日志、仅追加的抽象,由多个自动后备索引组成
Shard 分片 Lucene 实例;Primary 主分片(写入单元,数量创建后不可改)+ Replica 副本(读与 HA)
Mapping 映射 字段类型与索引方式,类比表结构
Inverted index term → 文档列表,支撑全文检索;doc_values 列存支撑排序聚合
Segment 段 Lucene 不可变小索引,refresh 生成、forcemerge 合并
Node 节点 一个 ES 进程;角色 master / data_hot / data_warm / data_cold / ingest / coordinating
Cluster 集群 相同 cluster.name 的节点集合
Ingest pipeline 写入前在 ingest 节点做轻量解析(grok / date / rename)
Index template 新建索引自动套用 settings/mappings/别名
ILM / SLM 索引生命周期(hot→warm→cold→delete)/ 快照生命周期

两套链路中的位置与完整数据流(ASCII)

【ELK / EFK 链路 —— 全文索引,ES 是"存储检索层"】
  应用写日志文件 / 容器 stdout
        │ ① 采集:读文件、尾部追踪、记录 offset
        ▼
  Filebeat(轻量) / Fluentd(F) / Fluent Bit ─────┐
  Logstash 也可直接采集并做重解析              │ ② 可选缓冲:Kafka/Redis 削峰
                                              ▼
  [ Beats/Fluent ] ──────────────────► Logstash( input → filter → output )
        (或直接)                              grok / mutate / date / geoip
                                              │ ③ 结构化 JSON
                                              ▼
                                       ★ Elasticsearch ★
                                       写入 → refresh(1s) 生成 segment → 倒排索引可检索
                                       索引模板定 mappings;ILM 管 rollover/分层/删除
                                              │ ④ Query DSL / _search / 聚合
                                              ▼
                                       Kibana: Discover / Dashboard / Alerting

【PLG 链路 —— 标签索引,无 ES;Loki 承担"存储检索层"】
  文件 / stdout / journal ─► Alloy(推荐) 或 Promtail(已 EOL) ─► Loki(仅索引标签+压缩块) ─► Grafana(LogQL)
  对照:ES=全字段索引(重、查询强);Loki=仅标签索引(轻、成本低)。ES 不在 PLG 链路中,二者是替代关系。

日志从文件到可检索的完整路径(ELK):

① 文件产生 → ② Beats/Fluent 采集(记录位移) → ③ [可选]Kafka 缓冲
→ ④ Logstash/Ingest pipeline 解析成结构化 JSON(@timestamp、status_code…)
→ ⑤ _bulk 批量写入协调节点 → ⑥ 路由到主分片,写 translog + 内存 buffer
→ ⑦ refresh(默认 1s) 生成 segment,文档进入可搜索状态(近实时)
→ ⑧ flush 落盘、segment 合并、副本同步;ILM 按年龄滚动/降温/删除
→ ⑨ 用户经 Kibana/REST 以 Query DSL 检索、聚合、告警

3. 安装与部署

版本差异:自 8.0 起安全特性默认开启——首次启动自动生成 elastic 密码、HTTP/Transport 层 TLS 证书与 CA(http_ca.crt)、Kibana enrollment token(有效 30 分钟)。因此 curl 必须带 -u elastic:<密码> 与 --cacert,否则 401/连接失败;7.x 默认无安全,勿照抄旧教程。具体版本号以官方最新稳定版为准。

3.1 环境准备

项 要求 / 命令
Java 8.x 自带 bundled OpenJDK,无需单独装 JDK
内存 生产 ≥8GB;JVM 堆 -Xms=-Xmx,≤物理内存 50%、≤~31GB(压缩指针阈值)
vm.max_map_count sudo sysctl -w vm.max_map_count=262144;写入 /etc/sysctl.conf 持久化
文件句柄 /etc/security/limits.conf:elasticsearch - nofile 65535 / - nproc 4096
交换分区 关闭或 bootstrap.memory_lock: true(配 systemd LimitMEMLOCK=infinity)
端口 9200 HTTP(HTTPS)、9300 Transport(集群内部)、Kibana 5601
磁盘 SSD/NVMe,path.data 独立挂载;磁盘水位 85%(low)/90%(high)/95%(flood 只读)
用户 包安装自动建 elasticsearch 用户,禁止 root 启动

3.2 多种安装方式

A. 官方 APT/YUM 仓库(生产首选)

# Debian/Ubuntu
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update && sudo apt install -y elasticsearch
# RHEL/Rocky/AlmaLinux
sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
cat >/etc/yum.repos.d/elasticsearch.repo <<'EOF'
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF
sudo dnf install -y elasticsearch   # 老系统用 yum

B. Docker / docker compose(开发、单机)

docker network create elastic
docker run --name es01 --net elastic -p 9200:9200 -it \
  -e "discovery.type=single-node" -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
  docker.elastic.co/elasticsearch/elasticsearch:<版本>   # tag 以官方最新稳定版为准
# 首次启动打印 elastic 密码与 Kibana enrollment token,务必保存
docker cp es01:/usr/share/elasticsearch/config/certs/http_ca.crt .
docker exec -it es01 bin/elasticsearch-reset-password -u elastic   # 忘记密码时重置

C. 二进制 tar.gz + systemd

tar -xzf elasticsearch-<版本>-linux-x86_64.tar.gz -C /opt
useradd -r elastic && chown -R elastic:elastic /opt/elasticsearch-*
sudo -u elastic /opt/elasticsearch-*/bin/elasticsearch -d -p pid     # 后台启动
# 生产写 /etc/systemd/system/elasticsearch.service:User=elastic、LimitNOFILE=65535、LimitMEMLOCK=infinity

D. K8s(Helm / Operator):官方推荐 ECK(Elastic Cloud on Kubernetes)Operator 声明式管理,自动处理证书/密码/PVC/滚动升级(https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html);也可用 elastic/helm-charts。具体 chart 版本以官方文档为准。

3.3 首次启动与验证

sudo systemctl daemon-reload && sudo systemctl enable --now elasticsearch
sudo systemctl status elasticsearch --no-pager
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic   # 取回/重置密码
export CACERT=/etc/elasticsearch/certs/http_ca.crt
curl --cacert $CACERT -u elastic:<密码> https://localhost:9200                 # 返回 version.number / tagline
curl --cacert $CACERT -u elastic:<密码> "https://localhost:9200/_cluster/health?pretty"
curl --cacert $CACERT -u elastic:<密码> -X POST "https://localhost:9200/logs-test/_doc" \
  -H 'Content-Type: application/json' -d '{"@timestamp":"2026-01-01T00:00:00Z","message":"hello","level":"INFO"}'
curl --cacert $CACERT -u elastic:<密码> "https://localhost:9200/logs-test/_search?pretty"
# Kibana 接入:bin/elasticsearch-create-enrollment-token -s kibana → 在 Kibana 首次访问页粘贴

4. 配置文件详解

主配置 /etc/elasticsearch/elasticsearch.yml;JVM /etc/elasticsearch/jvm.options.d/*.options;日志 /var/log/elasticsearch/;数据 /var/lib/elasticsearch。

关键项 说明
cluster.name 集群标识,同网段同名自动成团
node.name / node.roles 节点名与角色(master/data_hot/data_warm/ingest 等)
path.data / path.logs 数据与日志目录(⚠️ data 别放系统盘)
network.host / http.port 监听地址与端口,默认 localhost:9200
discovery.seed_hosts / cluster.initial_master_nodes 集群引导(仅首次)
xpack.security.enabled 8.x 默认 true
xpack.security.http.ssl.enabled / transport.ssl.enabled 8.x 默认 true(自签证书自动生成)
action.destructive_requires_name 建议 true,禁止 DELETE /_all ⚠️
cluster.routing.allocation.disk.* 磁盘水位阈值

最小可运行示例(单节点):

# /etc/elasticsearch/elasticsearch.yml
cluster.name: ops-es
node.name: node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node          # 单节点;多节点改用 seed_hosts + initial_master_nodes
xpack.security.enabled: true          # 8.x 默认,显式写出便于审计
action.destructive_requires_name: true
# /etc/elasticsearch/jvm.options.d/heap.options
-Xms4g
-Xmx4g

5. 核心使用方法(CLI / REST API / Query DSL / Kibana)

ES="https://localhost:9200"; AUTH="-u elastic:<密码>"
CACERT="--cacert /etc/elasticsearch/certs/http_ca.crt"
alias escurl='curl -s $CACERT $AUTH "$ES"'   # 下文中 escurl/'...' 表示 escurl 加路径

A. _cat 系列(排障第一入口,?v 显示表头、?help 看列名)

escurl/'_cat/health?v' ; escurl/'_cat/master?v' ; escurl/'_cat/nodes?v&h=ip,heap.percent,cpu,disk.used_percent,node.role'
escurl/'_cat/indices?v&s=store.size:desc' ; escurl/'_cat/shards?v&s=store:desc' ; escurl/'_cat/allocation?v'
escurl/'_cat/count/logs-*/' ; escurl/'_cat/aliases?v' ; escurl/'_cat/templates?v' ; escurl/'_cat/fielddata?v'
escurl/'_cat/thread_pool/write?v' ; escurl/'_cat/segments?v' ; escurl/'_cat/recovery?v' ; escurl/'_cat/pending_tasks?v'
escurl/'_cat/plugins?v' ; escurl/'_cat/repositories?v' ; escurl/'_cat/snapshots/<repo>?v'

B. 文档与索引 CRUD

escurl -X PUT '/my-index' -H 'Content-Type: application/json' -d '{"settings":{"number_of_shards":1,"number_of_replicas":1}}'
escurl -X PUT '/my-index/_doc/1' -H 'Content-Type: application/json' -d '{"msg":"a"}'   # 指定 id
escurl '/my-index/_doc/1' ; escurl '/my-index/_mapping?pretty' ; escurl '/my-index/_settings?pretty'
escurl -X POST '/my-index/_refresh' ; escurl -X POST '/my-index/_flush' ; escurl -X POST '/my-index/_count'
escurl -X POST '/my-index/_close' ; escurl -X POST '/my-index/_open'   # ⚠️ 关闭后不可读写

C. _bulk 批量写入(NDJSON,末行必须换行)

escurl -X POST '/_bulk?refresh=wait_for' -H 'Content-Type: application/x-ndjson' --data-binary @- <<'EOF'
{"index":{"_index":"logs-test","_id":"1"}}
{"@timestamp":"2026-01-01T00:00:01Z","status":200,"msg":"ok"}
{"index":{"_index":"logs-test","_id":"2"}}
{"@timestamp":"2026-01-01T00:00:02Z","status":500,"msg":"err"}
EOF

D. Query DSL / _search

# match 全文 / term 精确 / range 范围 / bool 组合
escurl -X POST '/logs-test/_search?pretty' -H 'Content-Type: application/json' -d '{"query":{"match":{"msg":"err"}}}'
escurl -X POST '/logs-*/_search?pretty' -H 'Content-Type: application/json' \
  -d '{"query":{"bool":{"must":[{"match_phrase":{"message":"timeout"}}],"filter":[{"term":{"log.level":"ERROR"}},{"range":{"@timestamp":{"gte":"now-1h"}}}]}},"size":20,"sort":[{"@timestamp":"desc"}]}'
# 聚合:按状态码统计 + 时间趋势(实战 7.1 使用)
escurl -X POST '/logs-*/_search?size=0&pretty' -H 'Content-Type: application/json' \
  -d '{"query":{"range":{"@timestamp":{"gte":"now-24h"}}},"aggs":{"by_code":{"terms":{"field":"http.response.status_code","size":20}},"trend":{"date_histogram":{"field":"@timestamp","fixed_interval":"1h"}}}}'
# 其它常用端点
escurl -X POST '/logs-*/_delete_by_query' -d '{"query":{"range":{"@timestamp":{"lt":"now-90d"}}}}'   # ⚠️ 删数据
escurl -X POST '/_reindex' -d '{"source":{"index":"old"},"dest":{"index":"new"}}'
escurl -X GET '/_cluster/health?level=shards&pretty' ; escurl -X GET '/_nodes/stats'
escurl -X PUT '/_cluster/settings' -d '{"transient":{"cluster.routing.allocation.disk.watermark.low":"90%"}}'

E. 索引模板 + ILM(日志治理核心)

# 1) ILM:7d 或 50gb rollover,7d→warm,30d→cold,90d 删除
escurl -X PUT '/_ilm/policy/logs-policy' -H 'Content-Type: application/json' \
  -d '{"policy":{"phases":{"hot":{"actions":{"rollover":{"max_primary_shard_size":"50gb","max_age":"7d"}}},"warm":{"min_age":"7d","actions":{"forcemerge":{"max_num_segments":1},"shrink":{"number_of_shards":1},"allocate":{"require":{"data":"warm"}}}},"cold":{"min_age":"30d","actions":{"allocate":{"require":{"data":"cold"}}}},"delete":{"min_age":"90d","actions":{"delete":{}}}}}}'
# 2) 可组合索引模板(composable,8.x 推荐),data stream 必须含 date 类型 @timestamp
escurl -X PUT '/_index_template/logs-tpl' -H 'Content-Type: application/json' \
  -d '{"index_patterns":["logs-*-*"],"data_stream":{},"priority":200,"template":{"settings":{"number_of_shards":1,"number_of_replicas":1,"index.lifecycle.name":"logs-policy"},"mappings":{"properties":{"@timestamp":{"type":"date"},"message":{"type":"text"},"host.name":{"type":"keyword"},"log.level":{"type":"keyword"},"http.response.status_code":{"type":"long"},"client.ip":{"type":"ip"}}}}}'
# 3) ILM 运维
escurl '/logs-*/_ilm/explain?pretty' ; escurl -X POST '/_ilm/stop' ; escurl -X POST '/_ilm/start'

F. Web UI(Kibana):Stack Management → Index Management / ILM Policies / Snapshot;Discover 检索;Dev Tools 直接跑上述 REST;Dashboard 可视化;Alerts 配规则。_cat 面向人读,_cluster/_nodes/* 面向监控;所有写/删命令均需认证,生产给应用最小权限而非 elastic 超管。

6. 告警与通知

Elastic 告警分两层:Kibana Alerting(规则 + Connector,基础版可用) 与 Watcher(付费)。

// Kibana es-query 规则:过去 5 分钟 5xx 日志 > 50 条触发
{
  "rule_type_id": "es-query",
  "params": {
    "index": ["logs-*"], "timeField": "@timestamp",
    "esQuery": "{\"query\":{\"bool\":{\"filter\":[{\"range\":{\"@timestamp\":{\"gte\":\"now-5m\"}}},{\"range\":{\"http.response.status_code\":{\"gte\":500}}}]}}}",
    "thresholdComparator": ">", "threshold": [50], "timeWindowSize": 5, "timeWindowUnit": "m", "size": 0
  },
  "actions": [{ "group": "threshold met", "id": "<connector-id>", "params": { "subject": "5xx 突增", "message": "{{context.message}}" } }]
}

通知渠道(Connectors):Email(SMTP)、Webhook、Slack、PagerDuty、Teams;企业微信/钉钉无内置 connector,用 Webhook 连接器指向各自机器人 URL 并按其 JSON 消息体拼装转发。

# 更通用的做法:Prometheus elasticsearch_exporter + Alertmanager 对下列信号告警
# status != green 持续 N 分钟 / disk.used_percent > 85 / _cat/thread_pool/write 出现 rejected / JVM heap > 85%

7. 实战场景

7.1 Nginx/应用日志接入并做状态码统计

目标:Nginx access log → 结构化 → 按状态码统计 + 时间趋势 → Kibana 展示。

步骤 + 命令/配置:

# ① 采集端启用 nginx 模块(自带 ingest pipeline,直接产出 ECS 字段)
filebeat modules enable nginx                         # 在 modules.d/nginx.yml 配 var.paths
filebeat setup --pipelines --index-management         # 装载 ingest pipeline 与索引模板
systemctl restart filebeat
# ② 服务端建模板与 ILM(见 5.E);pipeline 把 message 解析出 http.response.status_code
# ③ 状态码统计 + 5xx 时间趋势
escurl -X POST '/filebeat-*/_search?size=0&pretty' -H 'Content-Type: application/json' \
  -d '{"query":{"range":{"@timestamp":{"gte":"now-24h"}}},"aggs":{"status":{"terms":{"field":"http.response.status_code","size":10}},"trend":{"date_histogram":{"field":"@timestamp","fixed_interval":"1h"},"aggs":{"5xx":{"filter":{"range":{"http.response.status_code":{"gte":500}}}}}}}}'

验证:escurl '/_cat/indices/filebeat-*?v' 有数据;POST /filebeat-*/_search 用 term http.response.status_code:500 命中;Kibana Discover 可过滤该字段。 回滚:filebeat modules disable nginx;清理数据用 POST /filebeat-*/_close 或先做快照再 DELETE ⚠️(删数据不可恢复)。

7.2 日志暴增导致磁盘打满的处置

目标:止血 → 恢复可写 → 根治。

# ① 定位谁在涨、哪些节点到水位
escurl '/_cat/allocation?v' ; escurl '/_cat/indices?v&s=store.size:desc'
# ② 止血:把最旧索引副本临时降为 0,立即省空间
escurl -X PUT '/logs-2025-*/_settings' -d '{"index.number_of_replicas":0}'
# ③ 手动删最旧索引(或让 ILM delete 提前生效)⚠️ 不可恢复
escurl -X DELETE '/logs-2025-01-*'
# ④ flood stage 导致只读时,先腾空间再解除只读块
escurl -X PUT '/_all/_settings' -d '{"index.blocks.read_only_allow_delete":null}'
# ⑤ 根治:扩容磁盘/加 data 节点;收紧 ILM delete.min_age;采集端限速;大 message 字段评估 index:false

验证:_cat/allocation 使用率 < 85%;_cluster/health 回 green;新写入成功。 回滚:PUT /logs-*/_settings {"index.number_of_replicas":1} 恢复副本;⚠️ 已删索引只能从快照恢复(见 10)。

7.3 修正高基数字段映射

目标:user_id 被动态映射成 text 导致聚合报错,改为 keyword。

escurl '/logs-*/_mapping?pretty'                       # 确认现状
escurl -X PUT '/logs-v2' -d '{"mappings":{"properties":{"user_id":{"type":"keyword","ignore_above":256}}}}'
escurl -X POST '/_reindex?wait_for_completion=false' -d '{"source":{"index":"logs-old"},"dest":{"index":"logs-v2"}}'
escurl '/_tasks/<task_id>'                             # 查看进度

验证:对 user_id 做 terms 聚合正常返回;回滚:用 POST /_aliases 把读别名切回 logs-old。

8. 日常运维与故障排查

项 命令 / 路径
服务管理 systemctl start|stop|restart|status elasticsearch;滚动重启逐节点执行
日志路径 /var/log/elasticsearch/<cluster.name>.log、.json、GC 日志
健康检查 curl ... /_cluster/health?pretty;_cat/nodes?v
慢查询 设 index.search.slowlog.threshold.query.warn: 5s
配置校验 bin/elasticsearch -V;改 yml 后需 systemctl restart(无 reload)

故障「现象 → 原因 → 处置」表

现象 原因 处置
启动报 max virtual memory areas too low vm.max_map_count 过小 sysctl -w vm.max_map_count=262144 并持久化
启动报 can not run elasticsearch as root 用 root 启动 切 elasticsearch 用户 / systemd User=elastic
curl 返回 401 / SSL 错误 8.x 安全默认开启 加 -u elastic:<密码> --cacert http_ca.crt
集群 red、有 UNASSIGNED 分片 节点宕机/磁盘满/副本数>节点数 _cat/shards 定位,恢复节点/扩容/调副本数,_cluster/reroute?retry_failed=true
写入 429 es_rejected_execution_exception write 线程池队列满 降 bulk 并发与批量大小、加节点、谨慎调 thread_pool.write.queue_size
索引只读 read-only-allow-delete 触发 flood watermark 95% 清空间后 PUT /_all/_settings {"index.blocks.read_only_allow_delete":null}
JVM 堆 >85%、频繁 GC 堆过大/fielddata/大聚合 堆 ≤31GB、聚合字段用 keyword+doc_values、限制 size
节点加入不了集群 cluster.name/seed_hosts/证书不一致 统一配置与 CA、检查 9300 连通

9. 性能、容量与高可用

容量估算公式:

总存储 ≈ 原始日增量 R × 留存天数 × (1 + 副本数) × 1.1(索引开销) / 压缩收益
分片数目标:单分片 10–50GB(日志类取 30–50GB);单节点分片数 ≤ 20×堆GB(经验值)
堆 = min(物理内存/2, 31GB),其余留给 Lucene 文件缓存
维度 建议
分片/副本 data stream + ILM 按 max_primary_shard_size=50gb 自动切;默认 1 主 1 副;≥3 个 master-eligible 节点防脑裂
JVM 堆 -Xms=-Xmx、≤31GB、≤内存 50%;GC 默认 G1
冷热分层 node.roles: [data_hot/data_warm/data_cold] + index.routing.allocation.require.data,ILM 自动迁移
写入优化 _bulk 5–15MB/批;日志索引 refresh_interval: 30s;批量导入期副本临时设 0 再恢复
查询优化 用 filter 上下文(可缓存)、避免深 size、深分页用 search_after、大聚合加 size:0
集群/HA ≥3 master 节点、cluster.initial_master_nodes 仅首次;跨机架 awareness;CCR 为付费
自监控 Stack Monitoring(免费)或 elasticsearch_exporter+Grafana;盯 health、rejected、磁盘水位、heap

10. 安全加固、备份与升级

认证鉴权 / TLS / 最小权限

bin/elasticsearch-reset-password -u kibana_system
escurl -X POST '/_security/role/logs_writer' -d '{"indices":[{"names":["logs-*"],"privileges":["create_index","write","view_index_metadata"]}]}'
escurl -X POST '/_security/user/logstash_svc' -d '{"password":"<强密码>","roles":["logs_writer"]}'
# 8.x TLS 默认开;生产换企业 CA,network.host 勿裸暴露公网,前置防火墙/反向代理

快照备份恢复(SLM):path.repo 须先在 yml 声明并所有节点挂载同一共享存储。

escurl -X PUT '/_snapshot/ops_repo' -d '{"type":"fs","settings":{"location":"/mnt/es_backup"}}'
escurl -X PUT '/_slm/policy/daily-snap' -d '{"schedule":"0 30 1 * * ?","name":"<daily-snap-{now/d}>","repository":"ops_repo","config":{"indices":["logs-*",".security*"],"ignore_unavailable":true},"retention":{"expire_after":"30d","min_count":5,"max_count":50}}'
escurl -X POST '/_slm/policy/daily-snap/_execute'                       # 立即执行
escurl -X POST '/_snapshot/ops_repo/<snap>/_restore' -d '{"indices":"logs-2025-*","rename_pattern":"(.+)","rename_replacement":"restored_$1"}'  # ⚠️ 恢复前先 _close 目标

滚动升级顺序与回滚:① 快照备份 → ② 停 ILM/SLM → ③ 禁用分配 PUT /_cluster/settings {"transient":{"cluster.routing.allocation.enable":"primaries"}} → ④ 逐节点(先 data 后 master)停机/升级/启动,等 _cat/health 回 green 再下一台 → ⑤ 恢复分配与 ILM → ⑥ 验证。回滚:不支持降版本,只能从快照恢复到旧版本集群,故务必先升测试集群。

11. 常见坑

现象 原因 解决 / 备注
⚠️ 分片过多、集群变慢、master 压力大 每天一索引 + 默认多分片、按小时切 data stream + ILM rollover,单分片 30–50GB;小索引 _shrink/_reindex 合并
⚠️ 映射爆炸 Limit of total fields [1000] 动态映射把每个 JSON key 建字段 模板 dynamic: strict/false、设 index.mapping.total_fields.limit,高基数字段不建索引
时区差 8 小时、Kibana 时间不对 日志无时区被当 UTC;@timestamp 按 UTC 存 Logstash date 加 timezone => "Asia/Shanghai",采集端写带 +08:00 时间,Kibana 设时区
日志重复 Beats 重传/位移文件被删、Logstash 无幂等、重放 fingerprint 生成 _id、保留 registry、检查重放窗口与 pipeline.workers
聚合报 Fielddata is disabled on text 把 text 字段拿去 terms 聚合 用 .keyword 子字段或映射改 keyword
改 number_of_shards 不生效 主分片数创建后不可变 新建索引 + _reindex/_shrink,靠模板约束新索引
DELETE /_all 误删全库 危险 API 未限制 设 action.destructive_requires_name: true,生产禁把 elastic 给业务
磁盘到 95% 集群只读 flood watermark 85% 提前告警、ILM 删除、扩容;解除只读块前须先腾空间
yellow 长期不恢复 副本数 ≥ 节点数(单节点却设 1 副本) 单节点设 number_of_replicas: 0,或加节点
8.x 装完 curl 不通 沿用旧的 http:// 无认证教程 用 https:// + -u + --cacert,确认 9200/9300 放通

12. 速查表

项 值 / 命令
默认端口 9200(HTTP/HTTPS)、9300(Transport)、Kibana 5601
主配置 / JVM /etc/elasticsearch/elasticsearch.yml、jvm.options.d/*.options
数据 / 日志 /var/lib/elasticsearch、/var/log/elasticsearch
CA 证书 /etc/elasticsearch/certs/http_ca.crt(8.x)
内置工具 bin/elasticsearch-reset-password、-create-enrollment-token、-reconfigure-node
集群健康 GET /_cluster/health?pretty
节点/分片 GET /_cat/nodes?v、/_cat/shards?v、/_cat/allocation?v
索引大小 GET /_cat/indices?v&s=store.size:desc
搜索 / 批量写 POST /<idx>/_search(Query DSL)、POST /_bulk(NDJSON)
文档 CRUD PUT/GET/DELETE /<idx>/_doc/<id>
映射/设置/刷新 GET /<idx>/_mapping、GET/PUT /<idx>/_settings、POST /<idx>/_refresh、_forcemerge?max_num_segments=1
模板 / ILM PUT /_index_template/<n>、PUT /_ilm/policy/<n>、GET /<idx>/_ilm/explain
快照 / SLM PUT /_snapshot/<repo>、PUT /_slm/policy/<n>、POST /_snapshot/<repo>/<s>/_restore
集群设置 / 任务 GET/PUT /_cluster/settings、GET /_tasks、GET /_nodes/stats
危险开关 action.destructive_requires_name: true(务必开启)

13. 选型建议与同类对比

维度 ELK(ES+Logstash+Kibana) PLG(Loki+Alloy+Grafana) Graylog OpenSearch
索引方式 全字段倒排索引 仅标签索引 + 压缩块 基于 ES(旧) ES 7.10 分叉
资源/成本 高(JVM、磁盘、运维) 低(无全文索引) 中 高
全文任意检索 ★★★ 强 ★ 弱(靠行过滤) ★★ ★★★
聚合分析 ★★★ 极强 ★★ LogQL 够用 ★★ ★★★
K8s/云原生亲和 中 ★★★ 高 中 中
与 Prometheus/Grafana 一体 弱 ★★★ 原生 弱 弱
授权 Elastic License 2.0(受限) Apache 2.0 部分 GPL/企业 Apache 2.0
采集器 Beats/Logstash Alloy/Promtail(EOL) Sidecar/Beats Beats/Logstash

何时选 ES:需复杂全文检索、多字段聚合、统一存日志+指标+APM、已有 Kibana 生态、资源与运维人力充足。何时不选:只做"按标签查日志 + 成本敏感 + 已用 Grafana/Prometheus"→ 选 Loki;不接受 Elastic License → OpenSearch;要开箱即用轻后台 → Graylog。边界:ES 在 ELK/EFK 链路是存储检索层,PLG 链路不含 ES,二者是替代而非串联。

14. 参考链接

8.x 起安全/TLS 默认开启、内置用户密码自动生成;9.x 部分设置有调整。具体版本与参数以官方最新稳定版文档为准。

Elasticsearch —— 分布式日志存储、检索与分析底座 - 知识库 - 起航社区