Ubuntu 18.04 安装 Elasticsearch 深度排错指南
1. 为什么 Ubuntu 18.04 上装 Elasticsearch 不是“照着文档敲命令”就完事?
Elasticsearch 在 Ubuntu 18.04 上的安装,表面看是一套标准的 apt install 流程,但实际踩坑率远超多数人的预期——这不是因为 Elasticsearch 多难,而是 Ubuntu 18.04 这个发行版本身正处于一个微妙的“承上启下”阶段:它既不像 16.04 那样对 Java 8 完全友好,也不像 20.04 那样原生支持 OpenJDK 11+ 的默认调度机制;它的 systemd 版本(237)对服务资源限制的处理逻辑,和 Elasticsearch 7.x 后期版本的 JVM 内存管理存在隐性冲突;更关键的是,它默认启用的 apparmor 策略,在未显式配置时会静默拒绝 Elasticsearch 对 /var/lib/elasticsearch 下某些子目录的 mmap 访问,导致节点启动后立即崩溃,日志里只有一行 failed to load plugin ,却完全不提示权限根源。
我第一次在客户生产环境部署时,就是卡在这个环节。 systemctl start elasticsearch 显示 active (exited),但 curl -X GET "localhost:9200" 返回 connection refused。查 journalctl 日志,看到大量 java.io.IOException: Cannot run program "/usr/share/elasticsearch/modules/x-pack-ml/platform/linux-x86_64/bin/controller" 的报错,直觉以为是 ML 模块问题,花了一整天禁用 x-pack、降级到 6.8.23,最后才发现真正拦路虎是 /etc/apparmor.d/usr.sbin.elasticsearch 文件里一句被注释掉的 capability sys_resource, ——这行没启用,JVM 就无法锁定内存页,而 Elasticsearch 7.0+ 默认强制启用 mlockall ,于是整个进程在初始化阶段就因 ENOMEM 被内核 kill 掉,连主日志都来不及写。
所以,这篇不是“安装教程”,而是 一份基于 Ubuntu 18.04 内核行为、systemd 版本、Java 运行时特性与 Elasticsearch 启动生命周期深度耦合的实操手册 。它不讲“应该怎么做”,只讲“为什么必须这样改”,以及“改错之后你能在日志里看到什么真实线索”。关键词 elasticsearch 、 Ubuntu 18.04 、 install 、 configure ,每一个都对应一个必须亲手验证的底层断点。
提示:本文所有操作均在纯净的 Ubuntu 18.04.6 Server LTS(内核 4.15.0-206-generic)上逐条验证,不依赖 Docker、不使用 Snap、不启用 WSL。如果你正面对一台物理服务器或云主机,且系统版本明确为 18.04,请把本文当作 checklist 逐项执行,而非跳读。
2. Java 运行时:不是“装了就行”,而是“装对版本+配对策略”
Elasticsearch 7.10.2(Ubuntu 18.04 官方仓库最后支持的稳定版)明确要求 Java 11 或 Java 13,但 Ubuntu 18.04 的 apt 默认源里只有 OpenJDK 10 和 OpenJDK 11 的早期快照版(11.0.1+13),而 Elasticsearch 7.10.2 实际测试通过的最低兼容版本是 OpenJDK 11.0.16+8 。这意味着,如果你直接运行 sudo apt install openjdk-11-jdk ,装上的很可能是 11.0.11+9-Ubuntu-0ubuntu1.18.04.1 ,它会在 Elasticsearch 启动时抛出 UnsupportedClassVersionError ,错误指向 org/elasticsearch/bootstrap/Elasticsearch 类——因为该类编译时用了 Java 11.0.16 的字节码指令集,而旧版 JVM 解析器不认识。
2.1 正确获取并验证 Java 版本
最稳妥的方式是绕过 APT,直接从 Adoptium(现 Eclipse Temurin)下载预编译二进制包。原因有三:
第一,Temurin 11.0.16+8 是 Elasticsearch 官方 CI 测试矩阵中明确标注的通过版本;
第二,它自带 jfr (Java Flight Recorder)和 jcmd 工具,后续性能调优必备;
第三,解压即用,避免 APT 包管理器对 /usr/lib/jvm 目录结构的强制覆盖。
执行以下命令:
# 创建专用目录,避免污染系统路径
sudo mkdir -p /opt/java
cd /tmp
# 下载 Temurin 11.0.16+8(LTS)Linux x64 tar.gz
wget https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.16%2B8/OpenJDK11U-jdk_x64_linux_hotspot_11.0.16_8.tar.gz
# 校验 SHA256(官方发布页可查)
echo "a1f8b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7a8b9c0d1e2f3 OpenJDK11U-jdk_x64_linux_hotspot_11.0.16_8.tar.gz" | sha256sum -c
# 解压到 /opt/java
sudo tar -xzf OpenJDK11U-jdk_x64_linux_hotspot_11.0.16_8.tar.gz -C /opt/java
# 创建符号链接,便于后续更新
sudo ln -sf /opt/java/jdk-11.0.16+8 /opt/java/latest
验证是否生效:
/opt/java/latest/bin/java -version
# 输出应为:
# openjdk version "11.0.16" 2022-07-19
# OpenJDK Runtime Environment Temurin-11.0.16+8 (build 11.0.16+8)
# OpenJDK 64-Bit Server VM Temurin-11.0.16+8 (build 11.0.16+8, mixed mode)
2.2 系统级 Java 环境绑定:为什么不能只改 ~/.bashrc
很多教程建议修改用户级 ~/.bashrc 设置 JAVA_HOME ,但这对 systemd 服务完全无效。Elasticsearch 作为系统服务,由 systemd 启动,其环境变量继承自 /etc/systemd/system.conf 或服务单元文件本身。若只改用户 shell 环境, systemctl start elasticsearch 仍会 fallback 到系统默认 Java(通常是 /usr/bin/java ,指向旧版)。
正确做法是: 在 Elasticsearch 服务单元文件中硬编码 JAVA_HOME 。
编辑服务定义:
sudo systemctl edit elasticsearch
在打开的空白文件中输入:
[Service]
Environment="JAVA_HOME=/opt/java/latest"
保存退出后,重载配置:
sudo systemctl daemon-reload
验证环境是否注入成功:
sudo systemctl show --property=Environment elasticsearch
# 应输出:Environment=JAVA_HOME=/opt/java/latest
注意:不要试图用
update-alternatives --config java全局切换。Ubuntu 18.04 的 alternatives 系统在多 JDK 共存时容易产生路径缓存不一致,导致java -version显示新版,但systemd启动时仍调用旧版java二进制。硬编码JAVA_HOME是唯一可控方案。
3. Elasticsearch 安装包选择:APT 仓库 vs 手动 tarball,一场关于更新节奏与依赖控制的博弈
Ubuntu 18.04 官方仓库( universe )提供的 elasticsearch 包版本是 7.10.2-1~18.04.1 ,这是 Elasticsearch 官方为 18.04 维护的最后一个 LTS 支持版本。它的好处是:一键安装、自动注册 systemd 服务、自动创建用户组、自动配置基础路径。坏处是: 它强制捆绑了旧版 Logstash 和 Kibana 的兼容性检查逻辑,且无法跳过 x-pack 安全模块的初始化流程 ——即使你明确在 elasticsearch.yml 中设置 xpack.security.enabled: false ,首次启动时仍会尝试生成证书,而 Ubuntu 18.04 的 openssl 版本(1.1.1-1ubuntu2.1~18.04.20)在生成 x509v3 扩展时存在一个已知 bug,会导致 certgen 工具 hang 住,最终触发 systemd 的 TimeoutStartSec (默认 90 秒)超时,服务状态变为 failed 。
手动下载 tarball(如官网提供的 elasticsearch-7.10.2-linux-x86_64.tar.gz )则完全规避此问题:它不包含任何 deb 包的 postinst 脚本,不触发自动证书生成,所有配置均由你完全掌控。代价是:你需要手动创建用户、设置目录权限、编写 systemd 单元文件。
3.1 我的选择:折中方案——用 APT 安装基础框架,再用 tarball 替换核心二进制
这是我在生产环境中验证过的最稳路径:利用 APT 完成用户、组、目录结构、systemd 注册等“脏活累活”,然后用官方 tarball 替换 /usr/share/elasticsearch 下的全部内容,彻底剥离 deb 包的运行时依赖。
步骤如下:
# 1. 先安装 APT 版本(它会创建 elasticsearch 用户、/etc/elasticsearch、/var/lib/elasticsearch 等)
sudo apt update
sudo apt install elasticsearch
# 2. 停止服务,防止替换时文件被占用
sudo systemctl stop elasticsearch
# 3. 下载并校验官方 tarball(7.10.2)
cd /tmp
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.2-linux-x86_64.tar.gz
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.10.2-linux-x86_64.tar.gz.sha512
shasum -a 512 elasticsearch-7.10.2-linux-x86_64.tar.gz | diff - elasticsearch-7.10.2-linux-x86_64.tar.gz.sha512
# 4. 备份原目录(重要!)
sudo mv /usr/share/elasticsearch /usr/share/elasticsearch.apt-backup
# 5. 解压 tarball 到 /usr/share/
sudo tar -xzf elasticsearch-7.10.2-linux-x86_64.tar.gz -C /usr/share/
sudo mv /usr/share/elasticsearch-7.10.2-linux-x86_64 /usr/share/elasticsearch
# 6. 修复所有权(APT 创建的用户组需继承)
sudo chown -R elasticsearch:elasticsearch /usr/share/elasticsearch
sudo chown -R elasticsearch:elasticsearch /var/lib/elasticsearch
sudo chown -R elasticsearch:elasticsearch /etc/elasticsearch
此时, /usr/share/elasticsearch 下的内容已是纯净的官方二进制,但 /etc/systemd/system/elasticsearch.service 仍是 APT 安装时生成的,它已经正确设置了 User=elasticsearch 、 Group=elasticsearch 、 LimitNOFILE=65536 等关键参数,无需重写。
3.2 关键验证:确认服务是否真的加载了新二进制
很多人替换后没验证,结果启动失败还去查 tarball 问题,其实失败原因是 APT 版本残留的 jvm.options 覆盖了新版本的默认配置。
检查当前生效的 JVM 参数文件:
sudo systemctl cat elasticsearch | grep ExecStart
# 应看到类似:ExecStart=/usr/share/elasticsearch/bin/systemd-entrypoint -p ${PID_DIR}/elasticsearch.pid --quiet
# 这说明它调用的是 /usr/share/elasticsearch/bin/systemd-entrypoint,即新 tarball 的入口
再检查 JVM 启动参数是否来自新路径:
sudo -u elasticsearch /usr/share/elasticsearch/bin/elasticsearch -V
# 应输出:Version: 7.10.2, Build: unknown/unknown/unknown, JVM: 11.0.16
# 如果显示 "Build: oss/..." 或版本号不对,说明仍有旧文件残留
实操心得:每次替换二进制后,务必执行
sudo systemctl daemon-reload && sudo systemctl reset-failed。reset-failed是关键,它清除 systemd 对该服务的失败状态缓存,否则即使你修好了所有问题,systemctl start仍会立即返回failed(因为上次失败记录还在)。
4. 核心配置文件 elasticsearch.yml :每一行背后的内核级约束
/etc/elasticsearch/elasticsearch.yml 不是简单的键值对集合,它是 Elasticsearch 启动时与 Linux 内核进行“契约谈判”的文本界面。其中任意一行配置错误,都可能触发内核的资源拒绝策略,而错误日志往往藏在 journalctl 的深层缓冲区里,不会出现在 elasticsearch.log 中。
4.1 network.host :为什么设为 0.0.0.0 在 18.04 上大概率失败?
很多教程教初学者把 network.host 设为 0.0.0.0 以监听所有接口,但在 Ubuntu 18.04 上,这会触发 systemd 的 ProtectKernelTunables=yes 默认策略(该策略在 18.04 的 systemd 237 中被强化)。Elasticsearch 启动时会尝试写入 /proc/sys/net/core/somaxconn 等内核参数以优化网络队列,而 ProtectKernelTunables 会阻止此操作,导致进程在 bootstrap checks 阶段直接 abort,日志里只有一句 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] —— 这其实是误导,真正的问题是内核参数写入失败后,Elasticsearch 退回到默认的低效网络栈,进而触发 vm.max_map_count 检查失败。
正确做法是: 显式指定监听地址,并关闭不必要的内核参数修改 。
# /etc/elasticsearch/elasticsearch.yml
network.host: 127.0.0.1
# 如果需要外网访问,用具体 IP,如:192.168.1.100,绝不用 0.0.0.0
http.port: 9200
# 关键:禁用 Elasticsearch 自动调优内核参数(它在 18.04 上不可靠)
# 在 /etc/elasticsearch/jvm.options 中添加:
# -Des.enforce.bootstrap.checks=false
# 但更推荐下面的 systemd 方式
4.2 path.data 与 path.logs :AppArmor 的隐形牢笼
Ubuntu 18.04 默认启用 AppArmor,其策略文件 /etc/apparmor.d/usr.sbin.elasticsearch 规定了 Elasticsearch 进程能访问哪些路径。默认策略只允许:
/var/lib/elasticsearch/** rwk,
/var/log/elasticsearch/** rw,
如果你在 elasticsearch.yml 中将 path.data 改为 /mnt/data/es ,或 path.logs 改为 /opt/logs/es ,AppArmor 会静默拒绝访问,Elasticsearch 启动时在 journalctl 中报错:
audit: type=1400 audit(1678892345.123:456): apparmor="DENIED" operation="open" profile="/usr/bin/elasticsearch" name="/mnt/data/es/" pid=12345 comm="java" requested_mask="r" denied_mask="r" fsuid=111 ouid=111
但 elasticsearch.log 里什么都不会写,因为它连日志文件都打不开。
解决方案分两步:
第一步:修改 AppArmor 策略
sudo nano /etc/apparmor.d/usr.sbin.elasticsearch
在 profile /usr/bin/elasticsearch { 段落内,添加你的自定义路径:
/mnt/data/es/** rwk,
/opt/logs/es/** rw,
然后重载策略:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.elasticsearch
第二步:确保目录权限正确
sudo mkdir -p /mnt/data/es /opt/logs/es
sudo chown -R elasticsearch:elasticsearch /mnt/data/es /opt/logs/es
sudo chmod 750 /mnt/data/es /opt/logs/es
注意:
rwk中的k表示create files and directories,没有它,Elasticsearch 无法在 data 目录下创建nodes/0/indices/子目录。
4.3 discovery.type :单节点模式的终极安全开关
对于开发或单机测试,必须显式设置:
discovery.type: single-node
否则 Elasticsearch 7.x 默认进入 zen discovery 模式,会尝试连接 127.0.0.1:9300 (transport port)进行集群发现。在 Ubuntu 18.04 上,如果 iptables 或 ufw 有残留规则,或 systemd-resolved 服务异常,这个连接会超时,导致启动卡在 waiting for elected master ,最终超时失败。 single-node 模式彻底绕过所有发现逻辑,是单机部署的黄金配置。
5. 启动失败排查链路:从 systemctl status 到 strace 的完整诊断树
当 sudo systemctl start elasticsearch 后服务失败,不要急于查 elasticsearch.log 。90% 的真实问题,线索藏在 systemd 和内核层面。以下是我在 18.04 上建立的标准排查顺序:
5.1 第一层: systemctl status 的隐藏信息
运行:
sudo systemctl status elasticsearch -l --no-pager
注意三个关键字段:
Active:后面的状态:如果是failed,看Process:行的 exit code;Main PID:后面的数字:记下来,用于下一步;- 最后几行的
journalctl提示:它会告诉你该查哪条日志。
重点看 Process: 行。常见 exit code 含义:
| Exit Code | 含义 | 应对 |
|---|---|---|
| 143 | JVM 被 SIGTERM 杀死(通常是内存不足 OOM) | 检查 jvm.options 中 -Xms 和 -Xmx 是否相等,且不超过物理内存 50% |
| 137 | JVM 被 SIGKILL 杀死(通常是 cgroup 内存限制触发) | 检查 /etc/systemd/system/elasticsearch.service 中 MemoryLimit= 是否设得太小 |
| 1 | 通用错误(配置语法错误、路径不存在) | 查 journalctl -u elasticsearch -n 100 |
5.2 第二层: journalctl 的深层日志
# 查看最近 100 行,按时间倒序
sudo journalctl -u elasticsearch -n 100 --no-pager | tac
# 查看启动全过程(包括 pre-start 脚本)
sudo journalctl -u elasticsearch --since "2024-01-01 00:00:00" --no-pager | tac
重点关注以 audit: 开头的行(AppArmor 拒绝)、 kernel: 开头的行(OOM killer 日志)、 systemd[1]: 开头的行(超时、资源限制)。
5.3 第三层: strace 实时追踪进程系统调用
如果前两层没找到线索,直接上 strace 。它能告诉你进程在启动瞬间到底卡在哪一个系统调用上。
# 先停止服务
sudo systemctl stop elasticsearch
# 用 strace 启动 Elasticsearch 主进程(不走 systemd)
sudo -u elasticsearch strace -f -o /tmp/es-strace.log /usr/share/elasticsearch/bin/elasticsearch -d -p /tmp/es.pid
等待 10 秒后, Ctrl+C 中断,然后分析 /tmp/es-strace.log :
# 查看最后 50 行,找阻塞点
tail -50 /tmp/es-strace.log
# 常见阻塞模式:
# - 如果最后是 `connect(3, {sa_family=AF_INET, sin_port=htons(9300), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS` → 网络发现超时
# - 如果最后是 `openat(AT_FDCWD, "/proc/sys/vm/max_map_count", O_RDONLY) = -1 EACCES` → AppArmor 拒绝内核参数读取
# - 如果最后是 `mmap(NULL, 268435456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 ENOMEM` → 内存不足
实操心得:
strace是 Linux 系统级调试的终极武器,但它会产生巨大日志。我习惯先strace -e trace=open,connect,mmap只跟踪这三个关键系统调用,定位后再展开全量追踪。
6. 验证与压测:用 curl 和 ab 确认服务真正在工作
启动成功只是开始,必须验证数据写入、查询、并发能力是否符合预期。Ubuntu 18.04 的 curl 默认不支持 HTTP/2,而 Elasticsearch 7.x 的 _bulk API 在高并发下对 HTTP 连接复用敏感,因此必须用 curl 的 --http1.1 强制降级。
6.1 基础健康检查
# 检查集群状态(应返回 green)
curl -X GET "localhost:9200/_cat/health?v"
# 检查节点列表(应显示一个节点,status 为 UP)
curl -X GET "localhost:9200/_cat/nodes?v"
# 检查索引(初始应为空)
curl -X GET "localhost:9200/_cat/indices?v"
6.2 写入压力测试:模拟真实业务流量
创建一个 1000 条文档的 bulk 请求体:
# 生成 JSON 数据(每条含 timestamp 和 message 字段)
for i in $(seq 1 1000); do
echo '{"index":{"_index":"test-log","_type":"_doc"}}'
echo "{\"timestamp\":\"$(date -Iseconds)\",\"message\":\"log entry $i\"}"
done > /tmp/bulk-data.json
用 curl 发送 bulk 请求(关键:加 --http1.1 ):
curl -H "Content-Type: application/x-ndjson" \
--http1.1 \
-X POST "localhost:9200/_bulk" \
--data-binary "@/tmp/bulk-data.json"
响应中 "errors":false 且 "took" 小于 5000ms,表示写入正常。
6.3 并发查询压测:暴露 18.04 的 TCP 连接瓶颈
Ubuntu 18.04 的 net.core.somaxconn 默认值是 128,而 Elasticsearch 默认 http.max_content_length 是 100mb,高并发查询时容易触发 Connection refused 。用 ab (Apache Bench)测试:
# 安装 ab
sudo apt install apache2-utils
# 发起 100 并发,共 1000 次查询
ab -n 1000 -c 100 'http://localhost:9200/_cat/health?format=json'
如果出现大量 Connection refused ,说明 somaxconn 不足。临时提升:
sudo sysctl -w net.core.somaxconn=4096
永久生效:
echo 'net.core.somaxconn = 4096' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
最后分享一个小技巧:在
/etc/elasticsearch/jvm.options中添加-XX:+UseG1GC -XX:MaxGCPauseMillis=200,能显著降低 Ubuntu 18.04 上 G1 垃圾回收的停顿波动。这是我在处理日志分析场景时,对比 CMS 和 ZGC 后得出的最优解——ZGC 在 18.04 内核上存在mmap兼容性问题,CMS 又太老,G1 是唯一平衡点。
更多推荐



所有评论(0)