
最近帮人搭了一套日志检索服务操作系统用的就是 Ubuntu 22.04 LTS搜索引擎选了 Elasticsearch 8.7.1。整个过程从零开始装了不止一次也把 8.x 和 7.x 的差异重新捋了一遍。如果你正准备在自己电脑或一台 Ubuntu 服务器上装 ES不管是为了跑 Kibana、做日志分析还是给业务系统加搜索能力这篇文章应该能帮你少走不少弯路。我会从安装方式选择、关键配置、安全认证、JVM 调优到日常排错把完整流程和踩过的坑都摊开讲。1. 安装前想清楚的三件事1.1 版本为什么选 8.7.1Elasticsearch 的版本节奏很快8.7.1 这个版本不算最新但属于 8.x 大版本里比较稳的一批。选版本这事我的习惯是“不追新求稳”。8.x 相比 7.x 最大的变化就是默认开启安全认证和 TLS 加密这对新部署来说其实省事不用再像 7.x 那样手动装 security 插件、手动生成证书那一套流程。另外如果你后续要接 Kibana、Filebeat 这些生态组件版本号必须和 ES 严格对应。比如 ES 是 8.7.1Kibana 也建议用 8.7.1跨小版本组合不是不能用但容易冒出来各种莫名其妙的兼容问题。8.7.1 的踩坑资料相对丰富论坛和博客里能搜到的问题基本都是对症的。1.2 硬件要求和前置依赖ES 是 Java 写的跑起来需要 JVM但这里有个好消息Elasticsearch 8.7.1 的发行包内置了 OpenJDK 17不需要你单独安装 Java。有些人习惯先java -version看看其实没必要装了也不影响ES 默认用自己的内置 JDK。内存是重点。ES 本身是内存大户官方建议至少 4GB 可用内存但这只是“能启动”的门槛。如果你要在上面跑真实索引和查询8GB 以上才比较舒服。磁盘方面SSD 最好机械盘会明显拖慢搜索和写入。系统层面Ubuntu 22.04 的内核和 glibc 版本都满足要求这一点不用担心。1.3 安装方式怎么选ES 在 Linux 上的安装方式主要有三种官方 apt 仓库、手动下载 deb 包、tar.gz 解压包。我给的结论是刚入门、打算长期用、想要方便升级的走 apt 仓库。服务器在隔离内网、下载不方便的直接找台有网的机器下 deb 包拷进去。只是临时测试、不准备用 systemd 托管的可以 tar.gz 解压就跑。实际生产里我基本都是 deb 包装完用 systemd 托管干净也省心。2. 安装与基础配置2.1 通过官方 Apt 仓库安装 8.7.1先导入官方的 GPG 密钥再添加仓库这是标准流程。Ubuntu 22.04 上要注意老的apt-key adv方式会提示 deprecated这里用gpg --dearmor的方式处理密钥文件wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg sudo apt-get install apt-transport-https echo deb [signed-by/usr/share/keyrings/elasticsearch-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-get update sudo apt-get install elasticsearch8.7.1apt-get install后面带了8.7.1这一步容易忽略但很重要。如果不带版本号当前 8.x 仓库里指向的可能是比你预期更新的小版本锁版本可以避免将来误升级。国内网络下载官方仓库偶尔会慢如果卡得没法忍可以直接下载 deb 包wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.7.1-amd64.deb sudo dpkg -i elasticsearch-8.7.1-amd64.debdeb 包装完后安装目录、配置目录、数据目录会被自动创建好系统也会建一个elasticsearch用户之后用 systemd 启动就是用它来跑不会让你拿 root 直接跑这个设计是对的。2.2 必须调整的三个系统参数这一步是新手最容易忽略的ES 启动失败有一半原因都在这里。第一个是vm.max_map_count。ES 底层用了 LuceneLucene 在索引合并和读取时依赖大量 mmap 内存映射映射数的上限就是这个参数。Ubuntu 22.04 默认值通常是 65530ES 要求至少 262144。不改的话启动日志里会直接报max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]改法是一次性生效加永久写入sudo sysctl -w vm.max_map_count262144 echo vm.max_map_count262144 | sudo tee -a /etc/sysctl.conf sudo sysctl -p第二个是文件描述符上限。ES 每建一个连接、每打开一个文件都要占文件描述符官方建议至少 65535。用 deb 包安装时systemd 的 service 文件里其实已经设置好了LimitNOFILE65535但如果你是解压 tar 包手动启动就容易被 shell 的默认ulimit卡住表现为/usr/share/elasticsearch/bin/elasticsearch启动时报错Too many open files。第三个是线程数上限对应ulimit -uES 要求至少 4096。一般 Ubuntu 默认够但如果你调整过/etc/security/limits.conf需要留意别压得太紧。2.3 写一份够用的 elasticsearch.yml配置文件在/etc/elasticsearch/elasticsearch.yml。我用得最多的是一份单机/开发环境配置基本长这样cluster.name: es-single 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 xpack.security.enabled: true这里几个点解释一下。network.host: 0.0.0.0表示监听所有网卡这样别的机器才能通过 IP 访问 ES。但有个陷阱一旦network.host绑定了非 loopback 地址ES 会自动进入“生产模式”启动时执行 bootstrap 检查。如果配置里没有集群相关的 discovery 设置就会报the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured如果你只是想在本机跑一个单节点测试直接加一行discovery.type: single-node最省事它会把单节点需要的 discovery 和 master 选举都自动搞定。xpack.security.enabled: true是 8.x 的默认值写出来只是为了明确。如果你从头到尾都用 HTTPS 访问 ES这个就保持默认即可。想完全关闭安全认证也不是不行把两个 ssl 相关配置和这个一起处理掉但我不太建议尤其是你要接 Kibana 的时候账号体系还是有点用的。3. 启动、安全认证与验证3.1 启动服务并找到初始密码deb 包装完后ES 不会在安装目录里直接写好密码而是在服务第一次启动时自动生成账号和证书密码会打印到日志里。先启动服务sudo systemctl daemon-reload sudo systemctl enable elasticsearch sudo systemctl start elasticsearch然后查看启动日志sudo journalctl -u elasticsearch -n 100 --no-pager日志里会有类似这样的片段message:Generated password for user [elastic],password:xxxxxxxxxxxx这个密码只出现一次刷屏刷过去就不容易找回来。如果你跟我一样没抓到别挣扎直接重置sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i执行后会提示输入新密码输入两次就完成。这个操作即使服务没起来也能跑所以不用太紧张。8.x 内置了好几个系统用户除了elastic超级用户还有kibana_system、logstash_system、beats_system等。这些用户分别给不同的生态组件用以后接 Kibana 时需要手动把kibana_system的密码配到 Kibana 里。3.2 证书与远程访问问题8.x 默认开启了 HTTP 层的 TLS所以用 curl 访问本地 9200 端口时直接curl https://localhost:9200会报证书错误。第一次验证最省事的办法是忽略证书校验curl -k -u elastic:你的密码 https://localhost:9200正常情况下会返回一段 JSON包含 cluster_name、tagline 之类的信息看到这些就说明服务活了。生产环境里别的服务如果需要通过 API 访问 ES一般有两个选择要么把 ES 的 CA 证书传到客户端在请求时挂上证书要么在 elasticsearch.yml 里把 HTTP 层 TLS 关掉走明文 HTTP。关 TLS 的配置是xpack.security.http.ssl.enabled: false改完需要重启 ESsudo systemctl restart elasticsearch之后curl -u elastic:密码 http://localhost:9200就不需要-k了。这里我个人建议内网测试环境关 TLS 问题不大但只要是公网或跨信任域TLS 一定要留着并且把 CA 证书分发到位。关 TLS 一时爽密码是明文裸奔在链路上这不是吓唬人抓包软件一抓一个准。3.3 用 curl 验证集群状态密码有了服务起来了接下来确认集群是否健康curl -k -u elastic:你的密码 https://localhost:9200/_cluster/health?pretty返回的 JSON 里重点看status字段。单节点部署时是yellow很正常别慌。原因是默认索引配置了 1 个副本分片单节点上副本无法分配所以状态是 yellow主分片还在正常工作。等以后扩展成多节点副本分配过去后才会变绿。想验证写入和查询功能的话可以快速建一个测试索引curl -k -u elastic:你的密码 -X PUT https://localhost:9200/test-index curl -k -u elastic:你的密码 -X POST https://localhost:9200/test-index/_doc -H Content-Type: application/json -d {title:hello es} curl -k -u elastic:你的密码 https://localhost:9200/test-index/_search?qhellopretty测试完记得删掉curl -k -u elastic:你的密码 -X DELETE https://localhost:9200/test-index这个流程能跑通说明安装、认证、写入路径都正常后面接 Kibana 或者业务代码基本不会有大问题。4. JVM 内存与性能优化基础4.1 堆内存怎么设置才不翻车ES 最核心的运行时资源就是 JVM 堆。很多新手装完以后不做任何配置默认堆设置不够用压测一上来就 OOM然后开始怀疑是不是机器不行。其实多数情况只是没调堆。ES 8.7.1 在 deb 安装后主配置jvm.options在/etc/elasticsearch/jvm.options但我不建议直接改这个文件一是升级会被覆盖二是 ES 官方专门留了jvm.options.d/目录这个目录下的配置才是给用户用的。改堆的正确姿势是新建一个文件sudo tee /etc/elasticsearch/jvm.options.d/heap.options写入下面的内容-Xms4g -Xmx4gXms是初始堆大小Xmx是最大堆大小。这里有几个原则Xms和Xmx必须设成一致避免运行期堆动态扩容触发 full GC 导致卡顿。堆大小不要超过物理内存的一半ES 除了堆还要留大量内存给文件系统缓存缓存对搜索性能很关键。堆最大不要超过 30GB。JDK 在堆超过 30GB 左右时会关闭压缩指针对象头变大内存反而浪费了性能不一定提升。比如一台 16GB 内存的机器我一般会设 4g最多 6g剩下留给操作系统和 Lucene 的页缓存。开完这个配置记得重启 ES。确认堆是否生效可以看启动日志sudo journalctl -u elasticsearch -n 50 --no-pager | grep -i heap4.2 磁盘水位线和索引层面的基础优化ES 对磁盘空间有自己的“水位线”机制默认有三个阈值低水位low是 85%高水位high是 90%洪灾水位flood_stage是 95%。当磁盘使用率超过 90%ES 会尝试把分片搬到别的节点超过 95%为了防止数据损坏索引会强制变成只读这时候写入直接报错。这个机制在生产环境很容易误伤。比如日志集群写入量很大磁盘告警后索引变只读业务侧就开始报错。遇到这种情况先清理磁盘然后动态调高水位线curl -k -u elastic:你的密码 -X PUT https://localhost:9200/_cluster/settings -H Content-Type: application/json -d { transient: { cluster.routing.allocation.disk.watermark.low: 90%, cluster.routing.allocation.disk.watermark.high: 95%, cluster.routing.allocation.disk.watermark.flood_stage: 98% } }这套是动态配置不需要重启。但注意这只是临时缓解手法核心还是把磁盘腾出来或者挂新盘。索引层面refresh_interval默认是 1s意思是写入后 1 秒内可被搜索到。实时性高的场景当然好但批量写入时 1s 刷一次会带来不少 IO 压力。批量灌数据时可以临时调大刷新间隔curl -k -u elastic:你的密码 -X PUT https://localhost:9200/my-index/_settings -H Content-Type: application/json -d { index.refresh_interval: 30s }数据灌完再调回 1s。另外一个容易被忽略的是分片副本数。默认建索引是 1 主分片 1 副本分片但单节点集群里副本永远不会分配等于多占了一份逻辑空间。自己测试时完全可以建索引时指定副本数为 0curl -k -u elastic:你的密码 -X PUT https://localhost:9200/my-index -H Content-Type: application/json -d { settings: { number_of_shards: 1, number_of_replicas: 0 } }等以后加了节点再动态调大副本数。5. 高频问题排查实录5.1 启动失败服务反复退出这是我被问得最多的一类问题。遇到这种情况第一件事不是搜报错而是看日志sudo journalctl -u elasticsearch -n 100 --no-pager几个高频报错和对应处理方式我整理在下面max virtual memory areas vm.max_map_count [65530] is too low就是 2.2 节里说的没调系统参数执行sysctl并持久化即可。max file descriptors [4096] for elasticsearch process is too low用 tar 包手动运行常遇到deb 包理论上没这个问题。检查/etc/security/limits.conf或者启动脚本里的ulimit -n。bootstrap checks failed多半是network.host绑了非本地地址但没配 discovery单机测试直接加discovery.type: single-node。initial heap size [268435456] is larger than the maximum heap sizejvm.options.d里Xms和Xmx不一致或者写错了单位。去/etc/elasticsearch/jvm.options.d/检查自定义配置。日志里连续出现OutOfMemoryError大概率堆设小了或者数据量超出预期调大堆同时检查有没有索引设计问题。还有一个不起眼但特别容易踩的elasticsearch.yml 的 YAML 缩进写错。ES 对解析错误很敏感少一个空格可能整个配置文件都读不了。改完配置先测一下语法sudo -u elasticsearch /usr/share/elasticsearch/bin/elasticsearch -t返回configuration OK再重启服务别直接重启了然后一脸懵。5.2 9200 端口通了但外部连不上本地curl localhost:9200正常别的机器就是访问不了。原因大概三类一是防火墙。Ubuntu 如果开了 ufw默认会挡掉外部端口sudo ufw status sudo ufw allow 9200/tcp二是network.host没配置对。如果 elasticsearch.yml 里还是默认的127.0.0.1那 ES 只在本地回环地址监听就算防火墙放行也没用。改成0.0.0.0或具体内网 IP 后必须重启。三是云服务器安全组。这个不在 Ubuntu 系统层面去云控制台看看 9200 端口的安全组放行规则。很多人折腾了半天系统结果是安全组没加规则。外部连不上时可以在本机用ss -lntp | grep 9200看监听地址再对照network.host排查。5.3 集群状态变成 red 或写入报只读集群red表示有主分片未分配yellow是副本未分配red才是真正要处理的。先看分片分配情况curl -k -u elastic:你的密码 https://localhost:9200/_cat/shards?vhindex,shard,prirep,state,nodesstateUNASSIGNED的分片是关键。如果是单节点且是指定为副本的UNASSIGNED基本不用管这是 yellow 的由来。如果是主分片UNASSIGNED可能原因包括磁盘空间不足触发了水位线、节点下线后分片迁移失败、索引配置异常等。磁盘不足时ES 会自动把索引设为只读写入请求返回类似blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]处理方式是清理磁盘或扩容然后关闭只读curl -k -u elastic:你的密码 -X PUT https://localhost:9200/_all/_settings -H Content-Type: application/json -d { index.blocks.read_only_allow_delete: null }这个命令是解除索引级别的只读块执行完索引就可以正常写入删除了。排在最后再分享一个小经验生产环境千万别裸奔着用装完 ES 第一件事就是配监控起码把/var/log/elasticsearch/的日志定期归档磁盘用量盯紧。ES 这个系统真出故障的时候日志里通常都有线索最怕的是日志也被写爆了才想起来看那就真的被动了。