Elasticsearch服务器怎么搭建?索引、节点与资源配置详解

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。