Elasticsearch 部署运维
本文范围
部署 Elasticsearch,很多人第一反应是先找一条启动命令。命令当然要有,但 ES 不是一个“能启动就算装好”的组件。它后面跟着 JVM、磁盘、分片、副本、集群选主、快照仓库和恢复演练,任何一个没想清楚,最后都会在生产现场变成 red、磁盘满、分片不分配、恢复不回来。
所以这篇不从一堆概念开始背,而是按现场顺序走:先选版本和部署方式,再准备离线包和系统参数,然后写配置、启动验证、搭集群、规划分片副本,最后把快照恢复和常见故障排查补齐。你照着做,至少要能回答三个问题:服务有没有真正起来,数据有没有地方放,坏了以后能不能恢复。
先说清楚不讲什么。这里不展开 DSL 查询,不讲分词器调优,不讲相关性打分,不讲复杂搜索业务建模,也不讲 Java Client 代码。那些属于搜索建模和查询调优。这里专心把 ES 部署和运维这条线讲扎实。
前置条件
准备环境时按三种现场理解就行:
- 学习或测试环境:一台 Linux 服务器,先跑通单节点、配置文件、健康检查和快照恢复。
- 企业生产环境:至少 3 个 master-eligible 节点,独立数据目录,明确节点角色,启用安全配置,有快照仓库、容量监控、备份恢复演练和故障处理流程。
- 中国大陆公网、企业内网、完全离线环境:不能默认 Elastic 官网、Docker Hub、GitHub、国外软件源和外部下载服务都能稳定访问,要提前准备安装包、镜像、校验文件、私有仓库、插件包、证书和回退方案。
这里要注意版本口径。截至 2026-07-09,Elasticsearch 官方下载页显示当前版本是 9.4.3。新生产环境不要随手找一篇 7.x 老教程照着敲。7.x 时代的 node.master、node.data、安全默认值和集群发现配置,放到新版本里要重新校准。生产环境也不要只追最新版本,还要确认 Kibana、Beats、Logstash、客户端、插件、操作系统、JDK 和升级路径。
先把部署形态选清楚
装 ES 之前,先问一句:你是想临时跑起来,还是要让它在线上扛故障?这两个目标完全不是一回事。
如果只是学习,一台机器就够了。但只要说到生产,就不要把单节点当成“省机器版集群”。单节点能启动、能写入、能查询,但副本没有地方放,机器坏了服务就没了,磁盘满了也没人替它分担。
常见部署形态可以先按下面这张表理解:
| 形态 | 解决什么 | 不解决什么 | 适合场景 |
|---|---|---|---|
| 单节点 | 快速验证、开发测试、演示环境 | 没有高可用,副本无法分配,节点故障即服务不可用 | 本地学习、功能验证、小型临时环境 |
| 小型三节点 | 基础高可用,master 选举,副本可分配 | 容量、冷热分层和写入吞吐有限 | 中小系统、低到中等写入量 |
| 角色拆分集群 | master、data、ingest、coordinating 分离 | 运维复杂度更高,需要监控和容量治理 | 常规生产、日志检索、业务搜索 |
| 热温冷或多层数据节点 | 成本分层、生命周期管理、历史数据下沉 | 需要 ILM、存储差异和恢复策略配套 | 日志、审计、指标、时间序列类数据 |
这里要注意,不要把单节点测试配置扩成生产配置。单节点能跑起来,不代表它具备生产里的三个能力:
- 节点故障后服务继续可用。
- 磁盘、内存、分片数量可持续增长。
- 数据能从快照仓库恢复。
一般需要上生产时,我会先按这个保守起点规划:
| 节点类型 | 数量 | 角色 | 说明 |
|---|---|---|---|
| dedicated master | 3 | master | 负责集群状态和选举,不承载业务数据写入 |
| data hot / content | 3 起 | data_hot、data_content | 承载当前写入和常用查询 |
| ingest | 0 到 2 | ingest | 只有使用 pipeline、geoip、复杂预处理时再独立拆 |
| coordinating | 0 到 2 | node.roles: [] | 高并发查询或统一入口场景再拆 |
小团队也可以先用 3 台混合节点起步,每台同时有 master、data_hot、data_content、ingest,但要明确这是简化方案。数据量、查询量或写入量上来后,再把 master 和 data 角色拆开。
Elasticsearch 生产集群部署拓扑与访问边界
生产拓扑的核心是把客户端入口、集群选主、数据读写、快照仓库和运维访问分层。这样即使一开始用混合节点,也能提前知道后续拆 master、data、coordinating 节点时边界在哪里。
如果没有独立 coordinating 节点,入口也不要直连所有角色的节点随意访问,至少要明确哪些节点承接查询,哪些节点只负责选主和集群状态。
中国大陆、内网和离线环境先想清楚
Elasticsearch 部署最怕文章里一句 curl -O 或 docker pull,现场却没有外网。生产落地前先按网络形态选方案。
| 环境 | 推荐方式 | 风险点 | 验证方式 |
|---|---|---|---|
| 公网可达 | 官方包、官方仓库、Elastic 镜像仓库 | 下载慢、版本更新快、脚本写死旧地址 | curl -I、checksum、启动验证 |
| 中国大陆公网 | 外网下载机统一下载后入内网仓库 | 现场网络不稳定,临时拉包不可控 | 内部制品库、镜像 digest、校验文件 |
| 企业内网 | Nexus、Artifactory、文件服务器、Harbor | 依赖包漏带,插件漏带,证书不完整 | 离线机完整安装演练 |
| 完全离线 | tar/rpm/deb 包、Docker 镜像包、插件包、证书、脚本 | 没有外部 GeoIP、S3、仓库、DNS | 安装、启动、快照、恢复全链路演练 |
公网下载机先准备 tar 包。这里不要在每台生产机器上临时下载,最好只在一台出网机器上下载、校验、归档,再进入内网制品库。
export ES_VERSION=9.4.3
mkdir -p /opt/offline/elasticsearch
cd /opt/offline/elasticsearch
curl -fLO "https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz"
curl -fLO "https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz.sha512"
shasum -a 512 -c "elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz.sha512"
sha256sum "elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz" > "elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz.sha256"执行完 shasum -a 512 -c 后,应该看到类似 OK 的结果。这里解决的是“包有没有下载完整、有没有被替换”的问题。如果报 FAILED,不要强行安装,先重新下载并核对版本。如果系统没有 shasum,可以安装 Perl Digest 工具,或者按公司离线标准统一用 sha512sum 重新生成校验清单。
如果用 Docker,也不要指望现场机器能直接拉镜像。先在出网机器把镜像保存成离线包,并记录 image id 和 digest:
export ES_VERSION=9.4.3
docker pull "docker.elastic.co/elasticsearch/elasticsearch:${ES_VERSION}"
docker image inspect "docker.elastic.co/elasticsearch/elasticsearch:${ES_VERSION}" \
--format '{{.Id}} {{.RepoDigests}}'
docker save "docker.elastic.co/elasticsearch/elasticsearch:${ES_VERSION}" \
| gzip > "elasticsearch-${ES_VERSION}-image.tar.gz"
sha256sum "elasticsearch-${ES_VERSION}-image.tar.gz" \
> "elasticsearch-${ES_VERSION}-image.tar.gz.sha256"docker image inspect 这一步不是摆设,它用来记录你到底拿到了哪个镜像。以后排查“我本地能跑、服务器不能跑”时,第一件事就是对镜像 digest。
内网机器导入镜像:
sha256sum -c "elasticsearch-9.4.3-image.tar.gz.sha256"
gunzip -c "elasticsearch-9.4.3-image.tar.gz" | docker load
docker image ls | grep elasticsearch执行完 docker load 后,docker image ls 应该能看到 docker.elastic.co/elasticsearch/elasticsearch 和对应版本。如果看不到,先查压缩包是否传完整,再看 docker load 输出里有没有 Loaded image。
企业内网一般至少保留这些交付物。不要只拷一个安装包进去,过几个月再想恢复环境时,会发现缺证书、缺插件、缺校验文件,谁也说不清当时装的是哪一版。
| 交付物 | 用途 | 验证命令 |
|---|---|---|
| Elasticsearch 安装包或镜像 | 部署服务 | bin/elasticsearch --version、docker image inspect |
| Kibana 安装包或镜像 | 运维 UI 和 Dev Tools | bin/kibana --version |
| checksum 和版本清单 | 防篡改、防错包 | shasum -a 512 -c、sha256sum -c |
| 插件包 | analysis、repository 等扩展 | bin/elasticsearch-plugin list |
| TLS 证书和内部 CA | 安全访问、节点互信 | openssl x509 -in xxx.crt -noout -text |
| systemd 模板和配置模板 | 标准化启动 | systemctl status elasticsearch |
| 快照仓库配置 | 备份恢复 | _snapshot/_verify、_snapshot/_analyze |
| 恢复演练脚本 | 验证备份可用 | 隔离集群恢复并抽样查询 |
内网环境还要特别处理外部访问。比如 GeoIP 数据库下载、S3 仓库、容器镜像仓库、软件包仓库、证书吊销检查都可能访问外部地址。没有经过审批的外联,不要写进生产启动路径。确实不用 GeoIP enrichment 时,可以按版本文档关闭自动下载或改成内网可访问的端点。
安装 Elasticsearch
tar 包安装
tar 包适合离线环境、非标准发行版和需要自定义目录的场景。示例使用 /opt/elasticsearch 作为程序目录,/data/elasticsearch 作为数据目录,/var/log/elasticsearch 作为日志目录。
第一步先创建专用用户和目录。ES 不允许用 root 启动,数据目录和日志目录也不要放在程序目录里,后面升级或清理程序包时才不会误伤数据。
sudo groupadd --system elasticsearch
sudo useradd --system --gid elasticsearch --home-dir /opt/elasticsearch --shell /sbin/nologin elasticsearch
sudo mkdir -p /opt /data/elasticsearch /var/log/elasticsearch /etc/elasticsearch
sudo chown -R elasticsearch:elasticsearch /data/elasticsearch /var/log/elasticsearch /etc/elasticsearch这一步执行完一般没有输出,没报错就是好结果。可以用下面命令看一下目录属主:
ls -ld /data/elasticsearch /var/log/elasticsearch /etc/elasticsearch如果看到属主不是 elasticsearch,后面启动很容易报 data path 没权限或日志写不进去。这个问题现场非常常见,尤其是安装包用 root 解压、目录又忘了改属主的时候。
第二步解压安装包。这里用软链接 /opt/elasticsearch 指向具体版本目录,后面升级时可以保留旧版本目录,回滚也清楚。
export ES_VERSION=9.4.3
cd /opt
sudo tar -xzf "/opt/offline/elasticsearch/elasticsearch-${ES_VERSION}-linux-x86_64.tar.gz"
sudo ln -sfn "/opt/elasticsearch-${ES_VERSION}" /opt/elasticsearch
sudo chown -R elasticsearch:elasticsearch "/opt/elasticsearch-${ES_VERSION}"执行后看一下版本目录和软链接:
ls -ld "/opt/elasticsearch-${ES_VERSION}" /opt/elasticsearch如果软链接指错了,systemd 启动时会找不到 bin/elasticsearch。如果版本目录属主还是 root,后面安全自动配置、日志和插件安装也可能出问题。
第三步复制配置目录。我们把配置放到 /etc/elasticsearch,程序放 /opt/elasticsearch,数据放 /data/elasticsearch,这样目录职责比较清楚。
sudo rsync -a /opt/elasticsearch/config/ /etc/elasticsearch/
sudo chown -R elasticsearch:elasticsearch /etc/elasticsearch如果机器没有 rsync,可以用 cp -a 代替。复制完以后,至少应该能看到 elasticsearch.yml、jvm.options、log4j2.properties 这些文件。
ls -l /etc/elasticsearch第一次验证版本:
sudo -u elasticsearch ES_PATH_CONF=/etc/elasticsearch /opt/elasticsearch/bin/elasticsearch --version这条命令只验证程序能不能正常加载,不代表服务已经启动。输出里应该能看到 Elasticsearch 版本、构建信息和 JVM 信息。如果这里就失败,先不要继续写 systemd,先查安装包、权限和 ES_PATH_CONF。
systemd 托管
tar 包安装时建议交给 systemd 管理,不要长期用手工 shell 前台启动。前台启动适合第一次看日志,生产环境需要开机自启、统一日志、失败重启和资源限制。
sudo tee /etc/systemd/system/elasticsearch.service >/dev/null <<'EOF'
[Unit]
Description=Elasticsearch
Documentation=https://www.elastic.co
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=elasticsearch
Group=elasticsearch
Environment=ES_HOME=/opt/elasticsearch
Environment=ES_PATH_CONF=/etc/elasticsearch
Environment=PID_DIR=/var/run/elasticsearch
WorkingDirectory=/opt/elasticsearch
ExecStart=/opt/elasticsearch/bin/elasticsearch
StandardOutput=journal
StandardError=inherit
LimitNOFILE=65535
LimitNPROC=4096
LimitMEMLOCK=infinity
TimeoutStopSec=0
KillSignal=SIGTERM
KillMode=process
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch执行完 systemctl enable 后,应该看到创建开机启动链接,或者没有明显报错。这里先不急着 start,因为 ES 第一次启动会写入集群元数据。启动前先把 elasticsearch.yml 写好,不要先启动出一个临时集群,再反复修改 cluster.name、节点角色和数据目录。Elasticsearch 的数据目录里会记录集群元数据,乱改容易导致节点加入错误集群或带着旧 cluster UUID 起不来。
系统参数先过 bootstrap checks
生产模式下,Elasticsearch 会执行 bootstrap checks。网络绑定到非 loopback 地址后,很多检查失败会直接阻止启动。这个检查不是故意刁难你,它是在启动前把“线上一定会炸”的系统参数拦下来。
vm.max_map_count
先看当前值:
sysctl vm.max_map_count如果输出小于 262144,一般需要调大:
sudo tee /etc/sysctl.d/99-elasticsearch.conf >/dev/null <<'EOF'
vm.max_map_count = 262144
EOF
sudo sysctl --system
sysctl vm.max_map_count最后一行应该输出 vm.max_map_count = 262144。如果还是旧值,先看 /etc/sysctl.d/99-elasticsearch.conf 是否写对,再执行 sudo sysctl --system 看有没有报错。
典型报错:
max virtual memory areas vm.max_map_count [65530] is too low原因是 Lucene 使用 mmap 访问索引文件,默认虚拟内存映射数量不够。这个原理不用背,但要知道它和索引文件访问有关,不是 ES 随便要一个奇怪参数。修复后重新启动服务。
文件句柄和进程数
前面 systemd 服务里已经设置了:
LimitNOFILE=65535
LimitNPROC=4096服务启动后验证:
pid=$(pgrep -u elasticsearch -f org.elasticsearch.bootstrap.Elasticsearch | head -n 1)
cat "/proc/${pid}/limits" | grep -E 'open files|max user processes|locked memory'输出里 Max open files 应该接近或等于 65535,Max processes 应该不是很小的 1024。如果 pid 为空,说明 ES 进程没起来,先回到 journalctl -u elasticsearch 看启动错误。
常见报错:
max file descriptors [4096] for elasticsearch process is too low
max number of threads [1024] for user [elasticsearch] is too low这里要注意,不要只在当前 shell 里 ulimit -n 65535。systemd 启动的进程不吃你当前登录 shell 的临时限制,要写进 unit 或系统限制配置。
swap 和内存锁
生产环境尽量避免 Elasticsearch 使用 swap。可以选择关闭 swap,也可以配合 bootstrap.memory_lock: true 和 systemd 的 LimitMEMLOCK=infinity。
先查看 swap:
free -h
swapon --show如果 swapon --show 有输出,说明当前机器启用了 swap。测试环境可以先临时关闭:
sudo swapoff -a执行后再跑一次 swapon --show,没有输出就表示当前已经关闭。长期关闭要修改 /etc/fstab,不要只靠临时命令。是否完全禁用 swap 要结合公司操作系统基线,不要在不了解宿主机用途时直接改生产机器。
elasticsearch.yml 生产最小配置
单节点学习环境和生产集群配置不是一回事。下面先给一个三节点起步集群的配置框架。你可以先照这个结构理解,真正落地时把 IP、节点名、目录和角色换成自己的规划。
dedicated master 节点
/etc/elasticsearch/elasticsearch.yml:
cluster.name: prod-search
node.name: es-master-1
node.roles: [ master ]
path.data: /data/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 10.0.10.11
http.port: 9200
transport.port: 9300
discovery.seed_hosts:
- 10.0.10.11:9300
- 10.0.10.12:9300
- 10.0.10.13:9300
cluster.initial_master_nodes:
- es-master-1
- es-master-2
- es-master-3三台 master 节点只改 node.name 和 network.host。这里最容易踩坑的是 cluster.initial_master_nodes:它只用于全新集群第一次形成。集群已经形成后,要从所有节点配置中移除它,后续重启或扩容不要再设置。
data hot 节点
cluster.name: prod-search
node.name: es-data-hot-1
node.roles: [ data_hot, data_content, ingest ]
path.data: /data/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 10.0.10.21
http.port: 9200
transport.port: 9300
discovery.seed_hosts:
- 10.0.10.11:9300
- 10.0.10.12:9300
- 10.0.10.13:9300数据节点不要配置 cluster.initial_master_nodes。它只需要能通过 discovery.seed_hosts 找到 master-eligible 节点。如果数据节点启动日志里一直报找不到 master,先看 9300 通不通,再看 cluster.name 和 discovery.seed_hosts。
coordinating-only 节点
cluster.name: prod-search
node.name: es-coord-1
node.roles: []
path.data: /data/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 10.0.10.31
http.port: 9200
transport.port: 9300
discovery.seed_hosts:
- 10.0.10.11:9300
- 10.0.10.12:9300
- 10.0.10.13:9300node.roles: [] 表示这个节点不做 master、不存数据、不做 ingest,只负责协调请求、聚合结果和转发。高并发查询入口可以这样拆,但它不是免费的。coordinating 节点也会消耗堆内存和 CPU,聚合大结果集时照样会被打爆。
配置项解释
| 配置 | 作用 | 生产注意点 |
|---|---|---|
cluster.name | 集群名称 | 同一集群必须一致,不同环境不要复用 |
node.name | 节点名称 | 要稳定、可识别,故障排查靠它定位 |
node.roles | 节点角色 | 新版写法,避免继续使用旧的 node.master、node.data |
path.data | 数据目录 | 不要放在程序目录下,不要手工修改里面的文件 |
path.logs | 日志目录 | 要有轮转和采集策略 |
network.host | 绑定地址 | 生产不要随手 0.0.0.0 暴露到公网 |
discovery.seed_hosts | 发现种子节点 | 写 master-eligible 节点的 transport 地址 |
cluster.initial_master_nodes | 首次集群引导 | 只用于全新集群,形成后移除 |
启动服务并验证
配置写完以后再启动服务:
sudo systemctl start elasticsearch
sudo systemctl status elasticsearch --no-pagersystemctl status 里应该看到 active (running)。如果不是 running,不要先改一堆配置,先看最近日志:
sudo journalctl -u elasticsearch -n 200 --no-pager
sudo tail -n 200 /var/log/elasticsearch/prod-search.log如果这是 8.x 或 9.x 的第一次启动,日志里还会出现安全自动配置相关信息,例如密码、证书或 enrollment 提示。把这些信息放到安全的交付记录里,不要截图丢到聊天群里。
服务起来后,用 HTTP API 验证。启用了 HTTPS 和账号密码时,后面的命令统一使用这几个变量:
export ES_URL="https://127.0.0.1:9200"
export ES_USER="elastic"
export ES_PASS="Change_me_to_real_password"
export ES_CACERT="/etc/elasticsearch/certs/http_ca.crt"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/health?pretty"第一个命令应该返回集群名称、版本号和 tagline。第二个命令要重点看 status、number_of_nodes、active_primary_shards、unassigned_shards。单节点测试环境如果副本数不是 0,看到 yellow 不奇怪,因为副本没有第二台机器可放;生产环境看到 yellow 就要继续查原因。
安全和访问边界
Elasticsearch 8.x 和 9.x 默认会启用安全能力。第一次启动时会生成证书、密码和 enrollment 信息。正式生产里不要为了省事直接关闭安全,尤其不要把 9200 暴露到公网。
常见端口:
| 端口 | 用途 | 暴露范围 |
|---|---|---|
9200 | HTTP REST API | 应用、Kibana、运维网段,必须有认证和访问控制 |
9300 | transport 节点通信 | 只允许集群节点互通 |
5601 | Kibana | 运维入口或内网网关,不直接暴露公网 |
前面已经用 curl 验证过一次访问。这里再强调一句:生产里不要为了让命令少一个 --cacert 就把安全关掉。真正要省事,应该是把证书、账号和访问脚本标准化,而不是裸奔。
生产环境至少做到:
9200不对0.0.0.0/0开放。9300只允许节点之间互通。- Kibana 走内网域名、VPN、堡垒机或统一网关。
- 管理账号和业务账号分开。
- 快照仓库凭据放入 keystore,不写明文到配置文件。
- 证书、密码、enrollment token 进入公司密钥管理和交付清单。
JVM 参数怎么设
新版本 Elasticsearch 会根据节点角色和机器内存自动设置堆大小。一般情况下,先让它自动分配,不要一上来就手动拍一个 -Xmx31g。等你看到 heap、GC、查询、写入和分片压力以后,再决定要不要改。
如果确实要手动设置,不要直接改主 jvm.options 文件。新建 /etc/elasticsearch/jvm.options.d/heap.options,没有这个目录就新建:
sudo mkdir -p /etc/elasticsearch/jvm.options.d
sudo tee /etc/elasticsearch/jvm.options.d/heap.options >/dev/null <<'EOF'
-Xms16g
-Xmx16g
EOF
sudo chown -R elasticsearch:elasticsearch /etc/elasticsearch/jvm.options.d这个文件的内容就是:
-Xms16g
-Xmx16g原则很简单:
- 手动设置时
Xms和Xmx必须一致。 - 堆内存通常不超过机器内存的 50%,给文件系统缓存和 Lucene segment 留空间。
- 不要为了“内存越大越稳”把 64GB 机器全部给 JVM。
- 旧经验里常见 31GB 堆,是为了压在 compressed oops 边界附近;这不是所有版本和所有机器的万能值。
- master 节点、data 节点、coordinating 节点的内存压力来源不一样,要分角色观察。
建议保留 heap dump。内存溢出时,有 dump 才能继续分析;没有 dump,很多时候只能靠猜:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/elasticsearch改完 JVM 参数后需要重启 ES。重启后确认 JVM 和堆:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_nodes/jvm?filter_path=nodes.*.jvm.mem.heap_max_in_bytes,nodes.*.jvm.version&pretty"输出里要看两个东西:JVM 版本是不是预期版本,heap_max_in_bytes 是否接近你设置的堆大小。如果没有变化,先确认 ES_PATH_CONF 指向的是不是 /etc/elasticsearch,再确认 heap.options 权限和文件后缀。
再看节点当前堆使用率:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/nodes?v&h=name,roles,heap.current,heap.percent,heap.max,ram.percent,cpu,load_1m"这里要注意,如果 heap 经常超过 75% 并伴随长时间 GC,不要只加内存。先看分片数量、聚合查询、coordinating 节点压力、fielddata、bulk 写入、refresh、merge 和索引数量。很多 ES 内存问题,本质上是分片和查询把集群打穿了,不是单纯 JVM 给少了。
集群发现和 master 高可用
Elasticsearch 集群不是把几台机器的 cluster.name 写一样就完事。至少要看三件事:
- 节点之间 transport 端口
9300能互通。 discovery.seed_hosts指向 master-eligible 节点。- 全新集群首次启动时,
cluster.initial_master_nodes的节点名和实际node.name一致。
先验证网络。这里查的是 9300,不是 9200,因为节点之间发现和通信走 transport 端口:
nc -vz 10.0.10.11 9300
nc -vz 10.0.10.12 9300
nc -vz 10.0.10.13 9300如果输出 succeeded 或 open,说明端口能通。如果超时或拒绝连接,先查防火墙、安全组、监听地址和 ES 是否启动。不要在 9200 能访问时就以为集群发现也一定正常。
再查看当前 master:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/master?v"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/nodes?v&h=name,ip,roles,master,heap.percent,ram.percent,cpu,load_1m"_cat/master 应该只返回一个当前 master。_cat/nodes 里 master 列会标出当前 master,roles 列能看节点是否真的带了 m 角色。这里如果发现 data 节点也混着 master 角色,先确认是不是故意这么规划的。
生产建议至少 3 个 master-eligible 节点。不要只有 2 个 master 节点,因为任意一台故障后,剩下的一台很难形成可靠多数派。也不要一次停掉半数或更多投票节点,否则集群可能无法选主。
典型发现失败日志:
master not discovered yet排查顺序:
cluster.name是否一致。node.name是否和cluster.initial_master_nodes完全一致。discovery.seed_hosts是否写的是 transport 地址,不是 HTTP 地址。9300是否被防火墙、安全组、iptables、云安全策略拦截。- 新集群和旧数据目录是否混用。
- 已形成集群后是否还保留了
cluster.initial_master_nodes。
分片和副本怎么规划
Elasticsearch 的索引由 primary shard 和 replica shard 组成。通俗点讲,primary 是原件,replica 是副本。写入先落 primary,再复制到 replica。primary 数量在索引创建时确定,后面不能随便改;replica 可以动态调整。
先创建一个测试索引,感受一下分片和副本怎么写:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/logs-app-000001" \
-H 'Content-Type: application/json' \
-d '{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}'执行成功会返回 acknowledged: true。如果返回 index already exists,说明这个索引已经存在,换个名字或者先确认它能不能删。
查看分片:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/shards/logs-app-000001?v&s=shard,prirep"输出里 p 表示 primary,r 表示 replica。单节点环境里 replica 大概率是 UNASSIGNED,这就是为什么单节点经常 yellow。
调整副本:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/logs-app-*/_settings" \
-H 'Content-Type: application/json' \
-d '{
"index": {
"number_of_replicas": 1
}
}'执行后再看 _cat/shards。如果副本数改成 1 以后还是分配不上,先看节点数量、磁盘水位和 allocation explain,不要一上来怀疑 ES 坏了。
分片规划要避免两个极端:
- 分片太大:恢复慢、迁移慢、单个 shard 压力大。
- 分片太多:master 集群状态膨胀、heap 压力大、查询 fan-out 变多、恢复任务碎片化。
经验起点:
| 场景 | 起点建议 |
|---|---|
| 日志类按天索引 | 先按日写入量估算,让单个 primary shard 落在 30GB 到 50GB 左右,再压测修正 |
| 小业务索引 | 不要默认 5 分片,数据量小可以 1 到 3 个 primary |
| 副本数 | 生产至少 1 副本;单节点测试设 0,否则健康状态会 yellow |
| 数据节点数量 | 分片要能均匀分布,副本不能和 primary 在同一节点 |
| master 压力 | 控制索引数量和分片数量,不要无限按业务、租户、日期拆索引 |
官方有硬限制和建议,比如非 frozen shard 每个节点有默认上限,master 节点也受索引数量和堆内存约束。实际生产不要贴着上限跑。上限是防止集群被打穿,不是容量规划目标。
索引写入、分片、副本与快照恢复图
做 Elasticsearch 设计评审时,建议把“写入落到哪里、分片怎么复制、什么时候变 yellow/red、快照怎么恢复”画成一张图。这样评审不会只停留在 number_of_shards 和 number_of_replicas 两个参数上,而是能同时看到 JVM heap、磁盘水位、ILM 和 SLM 对生产稳定性的影响。
这张图用于上线检查时,可以直接拆成三组问题:第一,_cat/shards 里 primary 和 replica 是否按节点分散;第二,_cat/allocation 和 JVM heap 是否已经接近水位或高压;第三,_slm/stats、_cat/snapshots 和恢复演练是否证明快照真的能用。遇到 yellow 不要只把副本改成 0,遇到 red 也不要先删索引,先用 allocation explain 和快照恢复路径确认根因。
ILM 和 rollover
时间序列、日志、审计、指标类数据,不要无限往一个索引里写。现场最常见的情况是:刚上线时索引不大,没人管;半年后一个索引几百 GB,迁移慢、恢复慢、查询也慢。更稳的方式是用 ILM 管理生命周期,用 rollover 控制单个索引大小。
先创建生命周期策略。这个例子表示:写入索引达到 50GB primary shard 或 1 天后 rollover,30 天后删除。
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_ilm/policy/logs-30d-policy" \
-H 'Content-Type: application/json' \
-d '{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb",
"max_age": "1d"
}
}
},
"warm": {
"min_age": "2d",
"actions": {
"readonly": {}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}'执行成功后应该返回 acknowledged: true。如果报权限不足,说明当前账号没有管理 ILM 的权限,要换运维账号或补角色。
再创建模板。模板的作用是让后续 logs-app-* 这类索引自动带上分片、副本和 ILM 策略,不用每次手工配:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_index_template/logs-app-template" \
-H 'Content-Type: application/json' \
-d '{
"index_patterns": ["logs-app-*"],
"template": {
"settings": {
"index.lifecycle.name": "logs-30d-policy",
"index.lifecycle.rollover_alias": "logs-app",
"number_of_shards": 3,
"number_of_replicas": 1
}
}
}'然后创建第一个写入索引和别名。这里的 is_write_index: true 很关键,它告诉 ES 当前写入应该落到哪个具体索引:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/logs-app-000001" \
-H 'Content-Type: application/json' \
-d '{
"aliases": {
"logs-app": {
"is_write_index": true
}
}
}'查看 ILM 状态:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/logs-app-000001/_ilm/explain?pretty"输出里重点看 phase、action、step。如果卡住,先看是不是 rollover alias 写错、索引名是不是以数字结尾、当前索引是不是 write index。ILM 的价值不是“自动删数据”这么窄。它把容量治理变成规则:什么时候滚动,什么时候只读,什么时候下沉,什么时候删除。日志类系统没有 ILM,迟早会靠人工删索引救火。
磁盘水位和写保护
Elasticsearch 会根据磁盘水位影响分片分配。常见水位:
| 水位 | 影响 | 常见现象 |
|---|---|---|
| low watermark | 节点磁盘使用率超过阈值后,不再给它分配新的 shard | 新分片不往该节点放 |
| high watermark | Elasticsearch 尝试把 shard 从该节点迁走 | relocation 增多,恢复任务变多 |
| flood-stage watermark | 对受影响索引加写保护 | 写入报 cluster_block_exception |
先查看磁盘:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/allocation?v&h=node,shards,disk.indices,disk.used,disk.avail,disk.total,disk.percent"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/nodes?v&h=name,ip,roles,disk.used_percent,disk.avail,heap.percent,ram.percent,cpu"第一条看每个节点分了多少 shard、磁盘还剩多少;第二条把磁盘、heap、CPU 放在一起看。磁盘问题经常和分片迁移、GC、查询变慢一起出现,不要只盯一个指标。
再查看水位配置:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/settings?include_defaults=true&filter_path=*.cluster.routing.allocation.disk.watermark*&pretty"输出里会看到 low、high、flood-stage 的默认值或你配置过的值。如果所有数据节点都超过 low watermark,ES 就没有地方可以迁移 shard,这时调调参数救不了根因,必须扩容或清理。
遇到磁盘满,正确顺序是:
- 先确认哪个节点、哪个索引、哪个数据目录占用高。
- 扩容磁盘或新增数据节点。
- 用 ILM 或明确业务确认后删除过期索引。
- 确认 relocation 恢复完成。
- 再解除写保护。
解除写保护必须在磁盘降到安全水位后做:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/logs-app-*/_settings" \
-H 'Content-Type: application/json' \
-d '{
"index.blocks.read_only_allow_delete": null
}'这里要注意,不要把“调高水位”当成长期方案。水位只是最后保护,真正要解决的是容量、分片、保留周期和写入速度。临时调高水位只能给你争取处理时间,不能当容量规划。
快照仓库和备份策略
Elasticsearch 不能靠拷贝 path.data 做备份。数据目录里包含 Lucene 文件、事务日志、集群元数据和节点状态,在线拷贝很容易得到不可恢复的数据。生产备份使用 snapshot。这个点要记牢:备份 ES,不是备份目录,是注册仓库、创建快照、验证快照、演练恢复。
快照仓库必须在集群外部。常见选择:
| 仓库 | 适合场景 | 注意点 |
|---|---|---|
| 共享文件系统 | 小到中型内网、已有 NFS 或共享存储 | 所有 master/data 节点都要挂载同一路径 |
| S3 或兼容对象存储 | 云环境、私有对象存储、跨节点共享 | 要验证兼容性、权限、网络和吞吐 |
| 只读仓库 | 另一个集群恢复或演练 | 同一仓库不要多个集群同时写 |
共享文件系统仓库
所有 master 和 data 节点都要能访问同一个路径,例如 /mnt/es-snapshot。这里不是“某一台机器有这个目录”就行,而是每个相关节点都要挂载到同一个共享存储。然后在每个节点配置:
path.repo: ["/mnt/es-snapshot"]修改后滚动重启节点。重启完先确认目录可写:
sudo -u elasticsearch test -w /mnt/es-snapshot && echo ok能输出 ok 再注册仓库:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_snapshot/fs_repo" \
-H 'Content-Type: application/json' \
-d '{
"type": "fs",
"settings": {
"location": "/mnt/es-snapshot/prod-search",
"compress": true
}
}'注册成功后验证仓库:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X POST "$ES_URL/_snapshot/fs_repo/_verify?pretty"正常会返回各个节点的验证信息。如果这里失败,先查三件事:是不是所有 master/data 节点都配置了 path.repo,共享目录权限是不是 elasticsearch 可写,路径是不是每台机器都一致。
S3 或 MinIO 仓库
如果使用对象存储,凭据不要写进明文配置。使用 keystore。执行下面命令时会提示输入 access key 和 secret key:
sudo -u elasticsearch ES_PATH_CONF=/etc/elasticsearch \
/opt/elasticsearch/bin/elasticsearch-keystore add s3.client.default.access_key
sudo -u elasticsearch ES_PATH_CONF=/etc/elasticsearch \
/opt/elasticsearch/bin/elasticsearch-keystore add s3.client.default.secret_key添加完以后需要按版本文档 reload secure settings 或重启节点。然后注册仓库:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_snapshot/minio_repo" \
-H 'Content-Type: application/json' \
-d '{
"type": "s3",
"settings": {
"bucket": "es-snapshots",
"base_path": "prod-search",
"endpoint": "https://minio.example.com",
"path_style_access": true,
"compress": true
}
}'做仓库分析:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X POST "$ES_URL/_snapshot/minio_repo/_analyze?pretty"如果 _analyze 报网络、签名、权限、路径风格错误,先用对象存储客户端确认 bucket 能访问,再看 endpoint、证书、path-style、账号权限。旧版本如果没有对应 repository type,要按实际版本安装插件并滚动重启。新旧版本不要混着看文档。
手工快照
先手工打一份快照。生产里即使用 SLM,也建议先手工跑通一次,这样能确认仓库、权限、网络和集群状态都没问题。
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_snapshot/fs_repo/snapshot_2026_07_09?wait_for_completion=false" \
-H 'Content-Type: application/json' \
-d '{
"indices": "*",
"include_global_state": true
}'这里用 wait_for_completion=false,命令会很快返回,快照在后台继续跑。查看进度:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_snapshot/fs_repo/snapshot_2026_07_09?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/snapshots/fs_repo?v"状态应该从 IN_PROGRESS 变成 SUCCESS。如果是 FAILED 或 PARTIAL,先看具体 snapshot 详情。常见原因是 primary shard 不可用、仓库权限不足、对象存储网络不稳。
SLM 自动快照
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_slm/policy/daily-snapshot-policy" \
-H 'Content-Type: application/json' \
-d '{
"schedule": "0 30 2 * * ?",
"name": "<daily-snapshot-{now/d}>",
"repository": "fs_repo",
"config": {
"indices": "*",
"include_global_state": true
},
"retention": {
"expire_after": "30d",
"min_count": 7,
"max_count": 60
}
}'策略创建成功后查看:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_slm/policy/daily-snapshot-policy?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_slm/stats?pretty"_slm/stats 里要看执行次数、失败次数和最近一次失败原因。生产里不要只创建策略不看结果,快照失败没有告警,等于没有备份。
备份策略至少要回答:
- RPO 是多少,最多能接受丢多久数据。
- RTO 是多少,多久必须恢复可用。
- 仓库是否和集群磁盘隔离。
- 是否定期恢复演练。
- 快照失败是否告警。
- 仓库权限是否最小化。
- 同一仓库是否只有一个集群写入。
恢复演练
恢复不是等事故发生才第一次执行。建议至少准备一个隔离集群做演练。演练时不要一上来覆盖原索引,先恢复成新名字,确认数据和查询没问题,再决定是否切换业务。
先查看有哪些快照:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/snapshots/fs_repo?v&s=start_epoch:desc"选择一个 SUCCESS 状态的快照,恢复到新索引名,避免覆盖现有索引:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X POST "$ES_URL/_snapshot/fs_repo/snapshot_2026_07_09/_restore" \
-H 'Content-Type: application/json' \
-d '{
"indices": "logs-app-*",
"rename_pattern": "(.+)",
"rename_replacement": "restore-$1",
"index_settings": {
"index.number_of_replicas": 0
}
}'这里的 rename_pattern 和 rename_replacement 会把 logs-app-000001 恢复成 restore-logs-app-000001。index.number_of_replicas: 0 适合恢复演练集群节点较少的情况,避免副本放不下导致一直 yellow。
查看恢复:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/recovery?v&active_only=true"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/health?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/indices/restore-logs-app-*?v&s=index"_cat/recovery 没有 active 任务时,说明恢复任务基本结束。再看 _cluster/health 和恢复出来的索引文档数、大小是否符合预期。不要只看命令返回成功,至少抽样查询几条关键数据。
恢复注意点:
- 不能把快照恢复到更低版本的 Elasticsearch。
- 同名 open index 不能直接覆盖恢复。
- closed index 恢复时 primary shard 数必须匹配。
- 恢复到小集群时,可以临时把副本数降为 0。
- 整集群恢复前要确认安全配置、系统索引、feature states 和外部服务。
- 原集群仍在写同一个仓库时,恢复集群应该只读注册仓库。
集群健康和常用 API
运维 Elasticsearch,不要只看 Kibana 首页。Kibana 适合看图和执行 Dev Tools,但生产排障还是要把 API 记熟。下面这些命令,建议直接整理成巡检脚本。
健康状态
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/health?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/health?v"状态含义很简单:
| 状态 | 含义 | 影响 |
|---|---|---|
| green | primary 和 replica 都已分配 | 正常 |
| yellow | primary 已分配,replica 未完全分配 | 数据可读写,但高可用不足 |
| red | 至少一个 primary 未分配 | 部分数据不可用,必须尽快处理 |
这里要注意,程序或脚本要用 JSON API,不要解析 _cat 输出。_cat 更适合人眼排查。
节点
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/nodes?v&h=name,ip,roles,master,heap.percent,ram.percent,cpu,load_1m,disk.used_percent"索引
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/indices?v&s=store.size:desc"分片
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/shards?v&h=index,shard,prirep,state,docs,store,node,unassigned.reason&s=state,index"allocation explain
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X POST "$ES_URL/_cluster/allocation/explain?pretty" \
-H 'Content-Type: application/json' \
-d '{
"index": "logs-app-000001",
"shard": 0,
"primary": false
}'这是排查分片未分配最重要的 API。它会告诉你为什么不分配,是磁盘水位、节点角色、allocation disabled、同节点已有 primary、副本无处可放,还是过滤规则挡住了。现场遇到 yellow 或 red,不要先猜,先跑它。
恢复进度
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/recovery?v&s=index,shard"集群设置
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/settings?include_defaults=true&pretty"生产排障时先取这几份信息:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/health?pretty" > health.json
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/nodes?v" > nodes.txt
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/allocation?v" > allocation.txt
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/shards?v" > shards.txt
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/settings?include_defaults=true&pretty" > settings.jsonKibana 的边界
Kibana 在部署运维里很有用,但它不是 Elasticsearch 运维的全部。
适合用 Kibana 做:
- Dev Tools 执行和保存 API。
- Stack Management 里查看 Index Management、ILM、Snapshot and Restore。
- 查看集群状态、索引大小、生命周期状态。
- 管理用户、角色、API Key。
- 临时确认某个索引是否可查询。
不应该只靠 Kibana 做:
- 生产备份恢复演练。
- 自动化巡检。
- 大规模批量配置变更。
- 安全准入和网络隔离。
- 根因排查记录。
Kibana 自己也要版本匹配 Elasticsearch。Elastic Stack 组件不要跨大版本混用。Kibana 连接不上 Elasticsearch 时,先看版本、网络、证书、账号权限和 elasticsearch.hosts。
生成 Kibana enrollment token:
sudo -u elasticsearch ES_PATH_CONF=/etc/elasticsearch \
/opt/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana离线环境要提前准备 Kibana 包或镜像,证书和访问域名也要进入交付清单。
排障手册
排 ES 故障不要一上来就重启。先保留现场,再看健康状态、节点、分片、磁盘、日志。很多问题重启以后表面恢复了,但根因没处理,过一会儿还会回来。
启动失败
启动失败先看日志:
sudo systemctl status elasticsearch --no-pager
sudo journalctl -u elasticsearch -n 200 --no-pager
sudo tail -n 200 /var/log/elasticsearch/prod-search.logsystemctl status 看服务状态,journalctl 看 systemd 捕获到的启动错误,ES 自己的日志看更具体的 bootstrap、权限、发现和安全配置错误。三份日志结合起来看,不要只看一处。
常见问题:
| 现象 | 原因 | 处理 |
|---|---|---|
| 不能用 root 启动 | Elasticsearch 禁止 root 运行 | 创建专用用户,修正目录权限 |
vm.max_map_count 太低 | 系统 mmap 限制不足 | 设置 vm.max_map_count=262144 |
| 文件句柄太低 | systemd limit 不够 | 设置 LimitNOFILE=65535 |
| 线程数太低 | nproc 限制不足 | 设置 LimitNPROC=4096 |
| 端口被占用 | 9200 或 9300 已被使用 | ss -lntp 找进程 |
| data path 无权限 | 数据目录属主不对 | chown -R elasticsearch:elasticsearch |
| failed to obtain node locks | 同一数据目录被多个进程占用,或权限错误 | 停掉重复进程,修复权限 |
| master not discovered | 发现配置、网络或节点名错误 | 查 discovery.seed_hosts、9300、cluster.initial_master_nodes |
端口检查:
sudo ss -lntp | grep -E ':9200|:9300'
ps -ef | grep elasticsearch | grep -v grep如果 9200 没起来,但进程在,先看日志;如果进程都没有,再看 systemd 和 bootstrap checks。
目录权限检查:
namei -l /data/elasticsearch
namei -l /var/log/elasticsearch
sudo -u elasticsearch test -w /data/elasticsearch && echo ok这里应该输出 ok。如果没有,说明 elasticsearch 用户写不了数据目录,先修权限,不要改 ES 配置绕过去。
内存不足和 GC 压力
现象:
- 节点频繁掉线。
- 日志出现
OutOfMemoryError。 - 查询或写入出现 circuit breaker。
_cat/nodes里heap.percent长期很高。- GC 日志里停顿时间明显变长。
先看:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/nodes?v&h=name,roles,heap.percent,heap.current,heap.max,ram.percent,cpu,load_1m"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_nodes/stats/jvm,indices,breaker?pretty"第一条看节点当前 heap,第二条看 JVM、索引和 circuit breaker 统计。如果 breaker 频繁触发,说明请求已经被 ES 主动挡下来了,这通常不是“偶发慢”。
常见原因:
- 分片数量太多。
- 查询聚合太重,coordinating 节点扛不住。
- 字段映射失控,索引数量膨胀。
- bulk 写入太大,refresh 太频繁。
- 堆设置太小或太大,挤压了文件系统缓存。
- 单节点同时承担 master、data、ingest、coordinating,角色压力混在一起。
处理顺序:
- 先止血,限制重查询、暂停大批量写入或降低并发。
- 看 heap dump 和 GC 日志。
- 看分片数量、索引数量、最大索引和最大 shard。
- 优化 ILM、rollover、保留周期。
- 必要时扩 data 节点或拆 coordinating 节点。
- 最后再评估 JVM 堆大小。
磁盘满
现象:
- 写入报错
cluster_block_exception。 - 索引被加
read_only_allow_delete。 - 分片不再分配或一直 relocation。
- 集群从 green 变 yellow 或 red。
先看:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/allocation?v&h=node,shards,disk.indices,disk.used,disk.avail,disk.total,disk.percent"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/indices?v&s=store.size:desc"
df -h
du -sh /data/elasticsearch/* 2>/dev/null | sort -hES API 看逻辑上的索引和分片,df 看系统磁盘,du 看数据目录占用。三者要一起看。不要看到某个 Lucene 文件大就手工删除,删了可能直接把 shard 搞坏。
处理:
- 扩容或新增节点。
- 删除确认过期的索引。
- 缩短 ILM 保留周期。
- 快照成功后再删除历史索引。
- 磁盘降到安全水位后解除写保护。
不要先删数据目录里的文件。Elasticsearch 数据目录不能手工清理 Lucene 文件。
yellow 状态
yellow 表示 primary 已分配,但 replica 没有完全分配。单节点集群默认 1 副本时,yellow 是正常现象,因为副本不能和主分片放在同一个节点。
查看:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/health?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason&s=state,index"先看 unassigned.reason。如果是单节点副本放不下,测试环境可以改副本;如果是生产环境,就继续查磁盘、水位、节点角色和 allocation explain。
单节点测试环境可以把副本改成 0:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/*/_settings" \
-H 'Content-Type: application/json' \
-d '{
"index": {
"number_of_replicas": 0
}
}'生产不要为了让颜色变 green 就把副本改成 0。生产 yellow 要查为什么副本放不上去:节点数量、磁盘水位、allocation 规则、节点角色、故障节点还是恢复中。
red 状态
red 表示至少有 primary shard 未分配,部分数据不可用。
处理顺序:
- 查 red 的索引和 primary shard。
- 看是否有数据节点掉线。
- 看 allocation explain。
- 看磁盘水位和 allocation 设置。
- 如果 primary 数据确实丢失,从快照恢复。
命令:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason&s=state,index" \
| grep UNASSIGNED
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X POST "$ES_URL/_cluster/allocation/explain?pretty" \
-H 'Content-Type: application/json' \
-d '{
"index": "logs-app-000001",
"shard": 0,
"primary": true
}'primary: true 表示解释主分片。输出里的 can_allocate、allocate_explanation、deciders 是关键。它会直接告诉你“不分配”的原因,不要只盯 red 这个颜色。
如果是节点临时掉线,先恢复节点,不要急着删索引。如果是磁盘或机器彻底损坏,确认有可用副本或快照后再恢复。
分片未分配
分片未分配的常见原因:
| 原因 | 验证 |
|---|---|
| 副本无节点可放 | 节点数小于副本需求,或同节点已有 primary |
| 磁盘超过 low watermark | _cat/allocation |
| allocation 被关闭 | _cluster/settings |
| 节点角色不匹配 | _cat/nodes?h=name,roles |
| 过滤规则挡住 | index routing allocation settings |
| 节点掉线 | _cat/nodes、日志 |
| 快照恢复中 | _cat/recovery |
查看 allocation 是否被关:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
"$ES_URL/_cluster/settings?include_defaults=true&filter_path=*.cluster.routing.allocation.enable&pretty"恢复默认:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" \
-X PUT "$ES_URL/_cluster/settings" \
-H 'Content-Type: application/json' \
-d '{
"persistent": {
"cluster.routing.allocation.enable": null
},
"transient": {
"cluster.routing.allocation.enable": null
}
}'GeoIP 外联失败
内网环境可能看到类似外部地址解析失败、连接失败的日志。原因通常是 ingest geoip downloader 访问不到外部服务。
先判断业务是否使用 GeoIP enrichment。如果不用,可以按版本文档关闭自动下载;如果要用,要把 GeoIP 数据库下载、校验、更新做成内网流程,不要让生产节点临时访问外网。
落地工程深水区
Elasticsearch 的生产治理重点不是“能写入日志”,而是把 shard、mapping、磁盘水位、快照和查询成本压在可控范围内。
| 维度 | 运维约束 | 验证命令 |
|---|---|---|
| Bootstrap | 生产模式必须通过 bootstrap checks,不靠临时绕过参数上线 | 查看启动日志和 _cluster/health |
| 节点角色 | master、data、ingest、coordinating 角色按规模拆分,避免所有节点什么都干 | _cat/nodes?v&h=name,roles,heap.percent,ram.percent,cpu,load_1m |
| shard | 分片数量、大小、生命周期和 rollover 提前规划,避免小 shard 淹没集群 | _cat/shards?v&s=store:desc |
| mapping | dynamic mapping、字段数、keyword/text、日期格式由模板治理 | _index_template、_mapping |
| 恢复 | snapshot repository、恢复权限、索引关闭和演练耗时要落档 | _snapshot/_all |
上线前可以用这一组命令形成“集群体检快照”:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/health?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/nodes?v"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/indices?v&s=store.size:desc"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/shards?v&s=state,index"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/allocation/explain?pretty"工程上建议把责任切成三条线:
- 平台运维负责节点、证书、快照仓库、磁盘水位、升级和容量预警。
- 应用团队负责索引模板、字段设计、查询成本、写入峰值和保留周期。
- 数据治理负责索引生命周期、冷数据归档、删除审批和恢复验收。
如果查询慢,不要第一反应就加机器。先看 shard 是否过多、mapping 是否失控、查询是否扫全量、缓存是否命中、磁盘是否接近水位;这些问题不解决,扩容只会把失控状态放大。
生产巡检检查清单
每天或每次发布后至少检查:
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cluster/health?pretty"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/nodes?v"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/allocation?v"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/shards?v&s=state,index"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_cat/indices?v&s=store.size:desc"
curl --cacert "$ES_CACERT" -u "${ES_USER}:${ES_PASS}" "$ES_URL/_slm/stats?pretty"人工检查项:
- 集群状态是否 green,yellow 是否有明确原因。
- 是否有 unassigned shard。
- 节点 heap 是否长期过高。
- 磁盘是否接近 low watermark。
- 最大索引和最大 shard 是否超预期。
- ILM 是否正常推进。
- 快照是否按计划成功。
- master 节点是否稳定。
- 是否有节点频繁离线、GC 变长、写入拒绝或查询超时。
- 证书、账号、快照凭据是否临近过期或变更。
生产化落地建议
Elasticsearch 生产化不要只盯安装命令,至少把这些事情做完整:
- 版本固定:Elasticsearch、Kibana、Beats、Logstash 和客户端版本进入清单。
- 安装包固定:tar/rpm/deb、镜像、checksum、插件包都归档。
- 目录固定:程序、配置、数据、日志、快照分开。
- 节点角色固定:master、data、ingest、coordinating 不靠默认值长期混跑。
- 网络边界固定:9200、9300、5601 分别有访问策略。
- JVM 策略固定:自动堆或手动堆必须有监控依据。
- 分片策略固定:索引模板、ILM、rollover、保留周期统一管理。
- 磁盘策略固定:水位告警、扩容流程、历史索引清理流程提前写好。
- 备份策略固定:SLM、仓库验证、异地保存、恢复演练都要落地。
- 排障入口固定:健康、节点、分片、allocation、恢复、日志命令沉淀成脚本。
一套能长期跑的 Elasticsearch 集群,本质上是四件事长期稳定:节点稳定、分片稳定、磁盘稳定、快照可恢复。查询写得再漂亮,如果集群经常 red、磁盘经常满、快照从没恢复过,生产风险还是会回到运维现场。
官方参考入口
正式部署前建议重新校准这些官方文档:
