Elasticsearch(简称 ES)几乎是搜索和日志分析场景的默认选择:全文检索、日志聚合、指标分析、安全分析,都能用它扛。但它也是新手最容易踩坑的一套系统——不是装不上,而是装完之后各种奇怪问题:启动报 JVM 内存错误、磁盘水位一满就变只读、堆设太大反而变慢、单节点跑起来发现分片没法分配。
这篇文章从零讲清楚在一台或几台 Linux 服务器上搭建 Elasticsearch 的完整过程,重点放在三个新手最需要理解的地方:单节点与集群的区别、索引/分片/副本的概念、以及内存和磁盘的资源规划。最后把节点角色和常见坑一并说清。
一、先理解三个核心概念
在敲命令之前,先把概念理顺,否则后面全是"照着做但不知道为什么"。
索引(index) 相当于关系数据库里的"表",是文档的逻辑集合。你往里写 JSON 文档,ES 建立倒排索引以便快速检索。ES 的索引不是单文件,而是被切成多个分片(shard)。
分片(shard) 是索引的物理切分单位,每个分片是一个独立的 Lucene 索引。分片分两种:主分片(primary shard)负责写入和存储,副本分片(replica shard)是主分片的拷贝,用于容错和分担读流量。主分片数在索引创建后不能直接修改(除非 reindex 或用 split/shrink API),所以建索引时就要想清楚。
副本(replica) 默认每个主分片有一个副本。副本可以在主分片所在节点挂掉时提升为主,保证数据不丢;同时读请求可以打到副本上,提升查询吞吐。
一条经验规则:单个分片的大小建议控制在 10GB 到 50GB 之间(经验值,视场景而定),分片数不宜过多——每个分片都有固定开销,分片太多会让集群元数据膨胀、查询变慢。粗略估算分片数:总分片数 ≈ 数据总量 ÷ 30GB。比如 300GB 数据,主分片设 10 个大致合适。
二、系统准备:vm.max_map_count 必须改
ES 依赖 Lucene,而 Lucene 用的是 mmap 方式访问索引文件,对虚拟内存映射数量有要求。Linux 默认的 vm.max_map_count 只有 65530,ES 启动时会检查,不满足直接拒绝启动并提示:
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
临时改(重启失效):
sysctl -w vm.max_map_count=262144
永久改,写入 sysctl 配置文件并生效:
# RHEL / CentOS / Rocky 与 Debian / Ubuntu 路径略有不同,但都被 sysctl 读取
echo 'vm.max_map_count=262144' >> /etc/sysctl.conf
sysctl -p
# 也可单独放一个文件(systemd 系统推荐)
echo 'vm.max_map_count=262144' > /etc/sysctl.d/99-elasticsearch.conf
sysctl --system
这一步必须做,而且重启后要仍然生效,否则机器一重启 ES 又起不来。
除此之外,还需要处理文件句柄数(nofile)和线程数(nproc)。ES 需要能创建大量 file descriptor,一般设到 65536 以上:
cat >> /etc/security/limits.conf <<'EOF'
elasticsearch - nofile 65536
elasticsearch - nproc 4096
EOF
# 用 systemd 管理时,更推荐在 service 文件里设 LimitNOFILE
ES 官方不建议用 root 用户运行,安装包会自动创建一个 elasticsearch 系统用户,用 systemd 启动时以该用户身份运行,属于常规做法。
三、安装 Elasticsearch
RHEL / CentOS / Rocky
# 1. 导入官方 GPG key
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
# 2. 添加仓库
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
# 3. 安装
dnf install -y elasticsearch
# CentOS 7 用 yum
Debian / Ubuntu
curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | \
gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] \
https://artifacts.elastic.co/packages/8.x/apt stable main" \
> /etc/apt/sources.list.d/elastic-8.x.list
apt-get update
apt-get install -y elasticsearch
安装完成后,配置文件在 /etc/elasticsearch/elasticsearch.yml,JVM 配置在 /etc/elasticsearch/jvm.options 及其 jvm.options.d/ 目录,数据目录默认 /var/lib/elasticsearch,日志 /var/log/elasticsearch。
启动与管理:
systemctl daemon-reload
systemctl enable --now elasticsearch
systemctl status elasticsearch
# 查看启动日志
tail -f /var/log/elasticsearch/elasticsearch.log
# 健康检查
curl -X GET "localhost:9200/"
curl -X GET "localhost:9200/_cluster/health?pretty"
四、配置单节点
单节点适合开发、测试、以及数据量和访问量都不大的场景。关键配置:
# /etc/elasticsearch/elasticsearch.yml
cluster.name: my-es
node.name: node-1
# 只在本机监听(默认),提供内网访问时改成内网 IP
network.host: 127.0.0.1
http.port: 9200
# 单节点模式:告诉 ES 这是独立集群,避免启动时选主超时
discovery.type: single-node
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
discovery.type: single-node 是单节点的关键。不加它,ES 会尝试做集群发现,单节点可能因为找不到其他节点而一直等选主,日志里反复出现警告。
单节点的一个现实问题:默认副本数不适用。ES 索引默认 number_of_replicas: 1,意思是要有主分片以外的一份副本,但单节点上副本分片没地方放,集群健康会一直显示 yellow(主分片可用、副本未分配)。这不是故障,但会让人误以为有问题。单节点上建议把副本数设为 0:
curl -X PUT "localhost:9200/myindex/_settings" -H 'Content-Type: application/json' -d'
{ "index": { "number_of_replicas": 0 } }'
五、配置多节点集群
生产环境至少要三个节点(master-eligible 节点数最好为奇数,便于选出主节点)。三个节点各写各的 elasticsearch.yml:
# 节点 1(10.0.0.5)
cluster.name: my-es
node.name: node-1
node.roles: [ master, data, ingest ]
network.host: 10.0.0.5
http.port: 9200
discovery.seed_hosts: ["10.0.0.5:9300", "10.0.0.6:9300", "10.0.0.7:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
节点 2、节点 3 只需改 node.name 和 network.host。要点:
discovery.seed_hosts列出所有候选节点,用于节点发现。cluster.initial_master_nodes只在集群首次启动时使用,列出有资格竞选主节点的节点名。集群形成后,配置里应移除这一行再滚动重启,否则重启流程可能出问题。这是很常见的操作误区。- 三节点集群建议把
number_of_replicas设为 1,这样任意一个节点挂掉,数据仍然完整,集群健康是green。
集群起来后检查:
curl -X GET "10.0.0.5:9200/_cluster/health?pretty"
curl -X GET "10.0.0.5:9200/_cat/nodes?v"
curl -X GET "10.0.0.5:9200/_cat/shards?v"
六、JVM 堆设置:最容易出问题的地方
ES 是 Java 应用,堆大小直接决定性能和稳定性,也是新手最常设错的地方。
规则一:堆大小不要超过物理内存的一半。因为 Lucene 重度依赖操作系统的文件系统缓存(page cache)来加速索引读取,要给系统留足内存。一台 16GB 的机器,堆设 8GB 是上限;一台 32GB 的机器,堆一般设 16GB(经验值)。把内存的大部分给堆,反而会因为 page cache 不足而变慢。
规则二:单节点堆大小不要超过约 31GB。这是因为 JVM 在堆超过约 32GB 后会关闭"压缩普通对象指针"(Compressed Oops),此时对象指针从 4 字节变 8 字节,反而更耗内存,性能可能不升反降。所以实践上把堆设在 30GB 或 31GB 这个边界以下是常见做法(经验值,准确分界约在 32GB 附近,视 JVM 而定)。如果一台机器内存很大(比如 128GB),正确做法是在一台机器上跑多个 ES 实例,每个实例堆设 31GB 以下,而不是一个实例堆设 64GB。
规则三:堆的初始值和最大值设为相等,即 -Xms 等于 -Xmx,避免运行时动态调整堆大小带来的停顿。
配置方式有两种。直接改 /etc/elasticsearch/jvm.options 里的默认参数,但更推荐在 jvm.options.d/ 下放一个独立文件,升级时不至于被覆盖:
# /etc/elasticsearch/jvm.options.d/heap.options
-Xms8g
-Xmx8g
改完重启生效。验证运行时堆大小:
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty" | grep -A3 heap
还有一点:ES 默认会尝试使用 mlockall(锁定内存,防止被换出到 swap),如果系统不支持会报错。可以在 elasticsearch.yml 里设 bootstrap.memory_lock: true 并用 systemd 授权,或者接受默认行为。同时建议禁用 swap,或把 vm.swappiness 调低,因为 ES 讨厌被换页。
七、磁盘与水印:别让集群变只读
ES 对磁盘使用率非常敏感,它用"水位(watermark)"机制来保护自己:
- low watermark(默认 85%):达到后不再往该节点分配新分片。
- high watermark(默认 90%):达到后 ES 尝试把分片迁移走。
- flood stage(默认 95%):达到后索引变为只读,写入直接被拒绝。
这个机制的初衷是防止磁盘写满导致数据损坏,但后果是很多新手在磁盘将满时突然发现"写不进去了",排查半天才发现是水位触发。所以:
# elasticsearch.yml 里可调水位(百分比或绝对值,支持 92% 或 200gb)
cluster.routing.allocation.disk.threshold_enabled: true
cluster.routing.allocation.disk.watermark.low: 85%
cluster.routing.allocation.disk.watermark.high: 90%
cluster.routing.allocation.disk.watermark.flood_stage: 95%
磁盘规划上,path.data 最好挂独立数据盘,用 SSD 或 NVMe,ES 对随机 IO 很敏感,机械盘基本不可用。容量上要留足余量,并配合索引生命周期管理(ILM)定期滚动、删除或转入冷存储,否则日志类数据增长会很快把盘填满。
如果已经触发只读,清理磁盘后手动解除:
curl -X PUT "localhost:9200/_all/_settings" -H 'Content-Type: application/json' -d'
{ "index.blocks.read_only_allow_delete": null }'
八、节点角色
ES 7.x 之后把节点角色显式化了,通过 node.roles 指定。合理划分角色能提升稳定性:
| 角色 | 职责 | 说明 |
|---|---|---|
| master | 管理集群状态、分片分配 | 只做管理,不存数据,避免负载干扰 |
| data | 存储分片、执行查询聚合 | 最耗资源的角色,规格要足 |
| ingest | 执行预处理管道 | 写入时转换数据用 |
| coordinating | 接受请求、转发、汇总结果 | 默认每个节点都有,专设可减轻数据节点压力 |
| ml | 机器学习任务 | 一般场景用不到 |
小集群可以让一个节点同时是 master+data+ingest。大集群则应把 master 角色独立出来(专用 master 节点,通常 3 个),避免数据节点的负载影响集群管理。生产上一条常见经验:专用 master 节点用较小规格但要稳定,数据节点用大内存、SSD。
九、常见启动报错排查
max virtual memory areas vm.max_map_count too low:按第二节改 sysctl。max file descriptors too low:提高 nofile 限制。Cannot allocate memory、OutOfMemoryError:堆设得过大而物理内存不足,调小堆或加内存。failed to obtain node locks:数据目录被另一个 ES 进程占用,或上次异常退出留下锁。先确认没有重复启动,再检查path.data是否被多实例共享。- 集群一直
yellow:单节点是副本无处分配,属正常;多节点要检查节点数是否够、分片分配是否被阻塞。 - 端口 9300 与 9200 的区别:9200 是 REST API(HTTP),9300 是节点间传输端口(transport)。防火墙、安全组要按需放行。
安全方面,ES 8.x 默认开启安全功能(TLS 和账号密码),首次启动会自动配置证书并生成 elastic 超级用户密码,记得保存首次输出的密码。如果要对外提供访问,务必配置认证和 TLS,不要裸奔 9200 端口。
常见问题
Q:单节点能用于生产吗?
可以用于小规模、可容忍短时中断的场景,但要接受没有冗余。有可用性要求就必须多节点,且 master 节点数为奇数。
Q:堆是不是越大越好?
不是。堆越大,留给文件系统缓存的内存越少,反而变慢;而且超过约 31GB 会失去压缩指针的优化。堆设物理内存一半、且不超过约 31GB 是基本准则。
Q:为什么我的集群健康一直是 yellow?
单节点上多半是副本无处分配,把副本数设为 0 即变 green。多节点上要检查节点数量、角色配置和分片分配是否被磁盘水位阻塞。
Q:磁盘满了写入失败,怎么快速恢复?
先清理磁盘或扩容,再执行解除只读的 API。根本解决办法是配 ILM 做数据生命周期管理,别让盘填满。
Q:9200 和 9300 都要开放吗?
对外只需要 9200(且要加认证/TLS),9300 只在集群节点之间通信,安全组里应只在内网节点间放行,不要暴露公网。
总结
- 装 ES 前必须改
vm.max_map_count=262144并持久化,同时提高 nofile 限制,否则起不来。 - 单节点用
discovery.type: single-node且副本数设 0;多节点要配discovery.seed_hosts和首次的cluster.initial_master_nodes,集群形成后移除后者。 - JVM 堆不超过物理内存一半、且不超过约 31GB,
-Xms与-Xmx相等;大内存机器跑多个实例而不是超大堆。 - 磁盘要挂 SSD 并留足余量,理解 low/high/flood 三级水位,配 ILM 管理数据生命周期,避免集群变只读。
- 合理划分 master/data/coordinating 角色,master 节点用奇数个并保持独立,对外提供访问必须开认证和 TLS。
企业QQ咨询




