Filebeat 9.5 + Elasticsearch + Kibana 生产级日志平台:从三节点集群到日志保留,一步不漏
凌晨两点多,值班群里有人发了一张截图:Kibana Discover 里所有服务的日志都停在 01:47,之后一片空白。应用没报警,接口也正常,就是查不到日志。
更麻烦的是,这时候恰好有个支付回调异常要排查。日志平台平时没人在意,出事时它是唯一的证据来源。
我把这类问题来回看过很多遍,发现日志平台“搭起来”很简单,“搭对”才难:权限没收紧、证书嫌麻烦直接关掉、保留策略用默认值。前几个月风平浪静,某天磁盘水位到 95%,索引被自动加上只读块,Filebeat 一直重试,一条日志都写不进去。
这篇我按 Elastic 官方 9.x 文档,把一套三节点 Elasticsearch + Kibana + Filebeat 的生产部署从头走一遍。截至 2026 年 10 月底,官方当前稳定版是 9.5.5,10 月 6 日 Elasticsearch 和 Kibana 都随 9.5.5 发布了安全更新,新部署建议直接上这个版本。每一步都给验证命令和失败时的处理方式。
核心观点先放这里:日志平台的生产级,不在于集群有多大,而在于每条日志从写入到删除,全程有人管。
动手之前,先把版本和拓扑钉死
先说清楚这篇的环境,环境不同时请自行调整命令:
- 操作系统:Ubuntu 22.04/24.04,使用 deb 包和 systemd。RHEL 系用 rpm 仓库,差异会单独注明。
- 版本:Elasticsearch、Kibana、Filebeat 统一 9.5.5。Kibana 官方文档明确要求和 Elasticsearch 使用相同版本。
- 拓扑(示例地址):
| 角色 | 主机名 | 地址 | 端口 |
|---|---|---|---|
| Elasticsearch | es-01 / es-02 / es-03 | 10.0.0.11 / .12 / .13 | 9200 客户端,9300 仅节点间 |
| Kibana | kb-01 | 10.0.0.21 | 5601 仅反向代理或办公网 |
| Filebeat | 各业务主机 | — | 不对外监听 |
三个节点同时承担 master 和 data 角色,适合日增几十 GB 以内的规模。数据盘单独挂载到 /var/lib/elasticsearch,不用改 path.data,也不会让日志把系统盘写满。
有一点要在安装前做:用 hostnamectl set-hostname es-01 这类命令把主机名设好。deb 包安装时会用当前主机名写入 cluster.initial_master_nodes,主机名事后再改,首次引导时就会对不上。
所有 Elastic 主机都添加官方 9.x 仓库,然后锁定版本安装:
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | \
sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
sudo apt-get install -y apt-transport-https
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/9.x/apt stable main" | \
sudo tee /etc/apt/sources.list.d/elastic-9.x.list
sudo apt-get update
apt-cache policy elasticsearch | head -n 5 # 候选版本里应能看到 9.5.5不要用 add-apt-repository。官方说明它会额外写一条 deb-src,而 Elastic 不提供源码包,apt-get update 会报 Unable to find expected entry。RHEL 系把 baseurl 写成 https://artifacts.elastic.co/packages/9.x/yum,gpgkey 用同一把密钥。
操作系统这几项不改,节点在生产模式下起不来
Elasticsearch 绑定到非回环地址后进入生产模式,启动时会做 bootstrap checks,不达标直接拒绝启动。下面是有状态变更,先在 es-01 做完并验证,再照搬到另外两台。
先只读看当前值:
sysctl vm.max_map_count net.ipv4.tcp_retries2
swapon --show
timedatectl | grep -E 'synchronized|NTP'判断标准:vm.max_map_count 低于 1048576 需要调整,这是官方 9.x 文档给的值;swapon 有输出说明 swap 开着;System clock synchronized 必须是 yes,节点间时钟漂移会让日志时间和集群事件对不上。
sudo cp /etc/sysctl.conf /etc/sysctl.conf.bak.$(date +%F)
echo 'vm.max_map_count=1048576' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv4.tcp_retries2=5' | sudo tee -a /etc/sysctl.conf
sudo sysctl --system | grep -E 'max_map_count|tcp_retries2'
sudo swapoff -a
sudo cp /etc/fstab /etc/fstab.bak.$(date +%F)
sudo sed -i '/\sswap\s/s/^/#/' /etc/fstab
swapon --show # 没有输出才算关掉风险:swapoff 会把 swap 中的页换回内存,主机内存紧张时可能触发 OOM。官方建议 Elasticsearch 独占主机,不要和 Logstash 这类重负载服务混部。回滚方式是恢复两个备份文件,再执行 sysctl --system 和 swapon -a。
文件句柄和线程数不用手动改,deb 包自带的 systemd unit 已经设置好,启动后用 API 核对即可。
三节点集群:第一台先起,后两台用注册令牌加入
安装:三台都装,但只启动 es-01
sudo apt-get install -y elasticsearch=9.5.5
sudo apt-mark hold elasticsearchdeb 包安装时会自动完成安全初始化:生成传输层和 HTTP 层 TLS 证书,把安全配置写进 elasticsearch.yml,并在终端打印 elastic 超级用户的初始密码。这段输出不要截图发群,后面会重置。
这里有个坑必须提前讲:es-02、es-03 装完后千万不要启动。一旦启动,它们会各自组成单节点集群,之后加不进 es-01,只能清空数据目录重来。
配置并启动第一个节点
编辑 es-01 的 /etc/elasticsearch/elasticsearch.yml。文件末尾有一段 SECURITY AUTO-CONFIGURATION 自动配置块,不要动它,只改上方:
cluster.name: logs-prod
node.name: es-01
network.host: 10.0.0.11
transport.host: 10.0.0.11改完核对两件事:自动配置块里 cluster.initial_master_nodes 的值要和 node.name 一致;里面的 http.host: 0.0.0.0 表示 9200 监听所有网卡,主机有公网网卡时改成内网地址,或者用防火墙只放行内网网段。
grep -nE 'initial_master_nodes|http.host' /etc/elasticsearch/elasticsearch.yml
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch.service
sudo tail -n 50 /var/log/elasticsearch/logs-prod.logsystemctl start 不会告诉你是否启动成功,日志在 /var/log/elasticsearch/,文件名跟着 cluster.name 走。看到 started 且没有 bootstrap check failure 才算成功;看到 vm.max_map_count is too low,就回上一节检查 sysctl。
重置 elastic 密码并做第一次验证:
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i
read -rs ES_PASS && export ES_PASS # 交互输入,避免密码进 shell 历史
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u "elastic:$ES_PASS" https://10.0.0.11:9200返回里看 version.number 是否为 9.5.5、cluster_name 是否为 logs-prod。401 是密码不对;证书报错说明 --cacert 路径不对,或者用了 http。
让 es-02、es-03 加入
在 es-01 上生成节点注册令牌,有效期 30 分钟,每个节点单独生成一个:
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s node在从未启动过的 es-02 上,先确认网络能通,再用令牌重新配置:
nc -zv 10.0.0.11 9200 && nc -zv 10.0.0.11 9300
sudo /usr/share/elasticsearch/bin/elasticsearch-reconfigure-node --enrollment-token 出现 Do you want to continue 时输入 y。工具会写好 discovery.seed_hosts 和 transport.host,你再手动补上 cluster.name: logs-prod、node.name: es-02、network.host: 10.0.0.12,然后启动。es-03 同样操作,令牌重新生成。
curl -s --cacert /etc/elasticsearch/certs/http_ca.crt -u "elastic:$ES_PASS" \
"https://10.0.0.11:9200/_cat/nodes?v&h=name,ip,node.role,master,heap.percent,disk.used_percent"三行都在、其中一行 master 列为 * 才算组网成功。少了节点就去看新节点日志里 discovery 相关的报错,最常见的是 9300 被防火墙挡了。
正常时输出类似下面这样(示例数据):
name ip node.role master heap.percent disk.used_percent
es-01 10.0.0.11 cdfhilmrstw * 31 12.40
es-02 10.0.0.12 cdfhilmrstw - 27 12.10
es-03 10.0.0.13 cdfhilmrstw - 29 12.25node.role 里包含 m 表示可以当选 master,包含 d 表示存数据。三个节点 heap.percent 差距很大时,先看是不是某个节点承担了大部分分片。
收尾:不改发现配置,下次重启必出问题
这一步很多人漏掉。动手前先备份:sudo cp /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml.bak.$(date +%F),改坏了拷回去重启即可。es-01 是按单节点方式引导的,有 cluster.initial_master_nodes,却没有 discovery.seed_hosts,下次重启会过不了 discovery 的 bootstrap check。官方文档要求三台都做:注释掉 cluster.initial_master_nodes,并把 seed_hosts 写全:
discovery.seed_hosts: ["10.0.0.11:9300", "10.0.0.12:9300", "10.0.0.13:9300"]改完逐台重启,每台都等集群恢复 green 再动下一台:
sudo systemctl restart elasticsearch
curl -s --cacert /etc/elasticsearch/certs/http_ca.crt -u "elastic:$ES_PASS" \
"https://10.0.0.11:9200/_cluster/health?wait_for_status=green&timeout=120s&pretty"停止条件:长时间 yellow 或 red 时,不要继续重启下一台,先用 GET _cluster/allocation/explain 看哪个分片分配不了、原因是什么。
再核对文件句柄和堆内存:
curl -s --cacert /etc/elasticsearch/certs/http_ca.crt -u "elastic:$ES_PASS" \
"https://10.0.0.11:9200/_nodes/stats/process,jvm?filter_path=**.max_file_descriptors,**.heap_max_in_bytes&pretty"max_file_descriptors 不应低于 65535。9.x 的堆内存默认按节点角色和物理内存自动计算,官方不建议随意修改。确实要固定时,在 /etc/elasticsearch/jvm.options.d/heap.options 写 -Xms 和 -Xmx,两者相同且不超过物理内存一半,不要改根目录的 jvm.options。
Kibana:注册、补齐密钥,再收紧访问入口
# kb-01
sudo apt-get install -y kibana=9.5.5 && sudo apt-mark hold kibana
# es-01:生成 Kibana 注册令牌,有效期 30 分钟
sudo /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana在 /etc/kibana/kibana.yml 写入 server.host: "10.0.0.21",然后启动并取验证码:
sudo systemctl daemon-reload && sudo systemctl enable --now kibana
sudo systemctl status kibana --no-pager | grep -o 'http://.*code=[0-9]*'浏览器打开这个地址,粘贴令牌,按提示输入六位验证码,最后用 elastic 登录。注册会把 Elasticsearch 地址、CA 证书和 kibana 服务账号令牌写进 kibana.yml。这个窗口期 5601 是开着的,防火墙只放行你自己的办公网 IP。
注册完成后继续编辑 kibana.yml。如果 elasticsearch.hosts 里只有一个地址,补齐三个节点:
server.publicBaseUrl: "https://kibana.example.internal"
elasticsearch.hosts: ["https://10.0.0.11:9200", "https://10.0.0.12:9200", "https://10.0.0.13:9200"]再生成加密密钥,把输出的三行追加到 kibana.yml。不配置的话,Kibana 每次重启都用随机密钥,告警规则这类加密对象和会话都会受影响:
sudo /usr/share/kibana/bin/kibana-encryption-keys generate -q
sudo systemctl restart kibana
curl -s -u "elastic:$ES_PASS" http://10.0.0.21:5601/api/status | python3 -m json.tool | grep -A2 '"overall"'overall.level 为 available 才正常。Kibana 默认是 HTTP 明文,生产必须放在 HTTPS 反向代理后面,或者配置 server.ssl.*,5601 不要直接暴露到公网。
Filebeat:用最小权限的 API Key 写入,而不是 elastic
官方把 Filebeat 的权限拆成两类:setup 负责加载索引模板和 ILM 策略,权限高,只用一次;writer 负责日常写入,权限越小越好。我按这个思路建两把 API Key。在 Kibana 的 Dev Tools 里用 elastic 执行:
POST /_security/api_key
{
"name": "filebeat_setup_once",
"expiration": "1d",
"role_descriptors": {
"filebeat_setup": {
"cluster": ["monitor", "manage_ilm", "manage_index_templates", "manage_pipeline"],
"index": [{ "names": ["filebeat-*"], "privileges": ["manage"] }]
}
}
}
POST /_security/api_key
{
"name": "filebeat_writer_app01",
"expiration": "365d",
"role_descriptors": {
"filebeat_writer": {
"cluster": ["monitor", "read_ilm", "read_pipeline"],
"index": [{ "names": ["filebeat-*"], "privileges": ["view_index_metadata", "create_doc", "auto_configure"] }]
}
}
}返回的 id 和 api_key 用冒号拼成 id:api_key,就是 Filebeat 要的格式。setup key 一天过期;writer key 一台主机一把,泄露时可以单独作废。
安装和配置
业务主机同样添加 9.x 仓库,然后:
sudo apt-get install -y filebeat=9.5.5 && sudo apt-mark hold filebeat
sudo mkdir -p /etc/filebeat/certs
# 从任一 ES 节点拷贝 /etc/elasticsearch/certs/http_ca.crt 到 /etc/filebeat/certs/,只拷 CA 公钥证书
sudo filebeat keystore create
read -rs K && printf '%s' "$K" | sudo filebeat keystore add ES_API_KEY --stdin --force
read -rs K && printf '%s' "$K" | sudo filebeat keystore add ES_SETUP_KEY --stdin --force; unset Kkeystore 必须由运行 Filebeat 的同一个用户创建,deb 包默认以 root 运行,所以加 sudo。
备份 /etc/filebeat/filebeat.yml 后改成:
filebeat.inputs:
- type: filestream
id: order-api-json # 全局唯一,上线后不要改
paths:
- /var/log/app/order-api/*.log
parsers:
- ndjson:
target: ""
add_error_key: true
overwrite_keys: true
fields:
service: order-api
env: prod
- type: filestream
id: nginx-access
paths:
- /var/log/nginx/access.log*
prospector.scanner.exclude_files: ['\.gz