Elasticsearch —— 分布式日志存储、检索与分析底座
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. 参考链接
- 官网:https://www.elastic.co/elasticsearch/
- 文档(当前版):https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
- Docker 安装 / 8.x 安全默认开启:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html
- RPM 安装 / 安全自动配置:https://www.elastic.co/guide/en/elasticsearch/reference/current/rpm.html
- ILM:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html
- 索引模板:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html
- 快照与恢复 / SLM:https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html
- cat API:https://www.elastic.co/guide/en/elasticsearch/reference/current/cat.html
- Query DSL:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
- ECK(K8s Operator):https://www.elastic.co/guide/en/cloud-on-k8s/current/index.html
- 源码:https://github.com/elastic/elasticsearch
- 包仓库:https://artifacts.elastic.co/packages/8.x/apt 、https://artifacts.elastic.co/packages/8.x/yum
- Kibana 告警:https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html
8.x 起安全/TLS 默认开启、内置用户密码自动生成;9.x 部分设置有调整。具体版本与参数以官方最新稳定版文档为准。