📘 CentOS 9 使用 Docker Compose 部署 ELK + Filebeat 单机版(详细流程+架构图)
📑 目录
- 📝 前言
- 🔧 环境准备
- 📘 组件介绍
- 📥 安装 Docker 与 Docker Compose
- 🛠️ 拉取 ELK 镜像
- ⚙️ 编写 Docker Compose 配置文件
- 🚀 启动 ELK
- ⚠️ 重启es后,kibana登录失效问题
- 🔍 验证 ELK 部署
- 🖼️ ELK + Filebeat 架构图(Mermaid)
- 📊 优缺点分析
- 🏁 总结
📝 前言
ELK(Elasticsearch、Logstash、Kibana)是一套日志采集、存储和可视化解决方案。
Filebeat 是轻量级日志采集器,用于将日志发送到 Logstash 或 Elasticsearch。
本文将介绍如何在 CentOS 9 系统 使用 Docker Compose 部署 ELK + Filebeat 单机版
🔧 环境准备
- 操作系统:CentOS 9 Stream
- 推荐配置:CPU ≥ 2 核、内存 ≥ 8GB、磁盘 ≥ 50GB
- 网络:可访问 Docker Hub 或国内镜像源
📘 组件介绍
在单机环境下使用 Docker Compose 安装 ELK + Filebeat ,其实是搭建了一个完整的 日志收集、存储、分析和可视化平台,主要用途和能做的事情如下:
1️⃣ 组件作用
组件 | 功能 |
---|---|
Elasticsearch | 核心存储和搜索引擎,用于存储日志数据、提供搜索和聚合功能。 |
Logstash | 可选组件,用于日志收集、解析和处理(例如:JSON 解析、字段提取、正则处理)。 |
Kibana | 可视化界面,可对日志进行搜索、分析和可视化(图表、仪表盘、告警等)。 |
Filebeat | 轻量级日志采集器,安装在应用服务器上,将日志实时推送到 Elasticsearch 或 Logstash。 |
2️⃣ 能做什么
- 日志集中化
- 收集不同应用、系统、服务的日志,统一发送到 Elasticsearch 存储。
- 通过 Filebeat 可以采集:
- Linux 系统日志(
/var/log/messages
、/var/log/syslog
) - Nginx/Apache 日志
- Java 应用日志(Spring Boot 等)
- 自定义业务日志
- Linux 系统日志(
- 日志搜索与查询
- 可以按时间、级别、关键字搜索日志。
- 支持多字段组合查询,例如:某服务 + 某用户 + 某时间段。
- 日志可视化
- Kibana 提供丰富图表:
- 折线图:请求量、错误数随时间变化
- 饼图:按日志级别统计
- 条形图:按模块/服务统计
- 可以生成仪表盘,将关键指标一目了然展示。
- Kibana 提供丰富图表:
- 告警与监控
- 可以通过 Watcher 或 Kibana Alerts 配置告警:
- 当错误日志超过阈值
- 某指标异常波动
- 可发送邮件、Slack、Webhook 等通知。
- 可以通过 Watcher 或 Kibana Alerts 配置告警:
- 日志处理与解析
- Logstash 可对日志做:
- 正则提取字段
- JSON、CSV、XML 解析
- 数据脱敏和格式转换
- Logstash 可对日志做:
- 快速实验与开发
- 单机版可快速搭建环境:
- 学习 ELK 技术栈
- 调试日志采集配置
- 构建仪表盘
- 适合个人开发、测试或 PoC(Proof of Concept)
- 单机版可快速搭建环境:
3️⃣ 优势
- 部署简单,Docker Compose 一条命令即可启动
- 环境隔离好,不影响主机系统
- 支持快速修改配置和版本升级
- 可直接和业务应用日志打通,形成可视化监控平台
4️⃣ 限制
- 单机性能有限:
- Elasticsearch 存储量有限
- 并发查询量大时性能下降
- 没有高可用性,单点故障风险高
- 不适合生产环境大量日志存储
📥 安装 Docker 与 Docker Compose
🛠️ 拉取 ELK 镜像
bash
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.15.0
docker pull docker.elastic.co/logstash/logstash:8.15.0
docker pull docker.elastic.co/kibana/kibana:8.15.0
docker pull docker.elastic.co/beats/filebeat:8.15.0
⚙️ 编写 Docker Compose 配置文件

在 /etc/docker/docker-elk
创建 docker-compose.yml
:
yaml
version: '3.9' # 使用新版本 Compose 文件格式
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.enabled=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./es_data:/usr/share/elasticsearch/data # 数据持久化
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro # 可选 ES 配置
ports:
- "9200:9200"
logstash:
image: docker.elastic.co/logstash/logstash:8.15.0
container_name: logstash
volumes:
- ./pipeline/:/usr/share/logstash/pipeline/:ro # 挂载目录而不是单文件
ports:
- "5044:5044" # Beats input 默认端口
depends_on:
- elasticsearch
kibana:
image: docker.elastic.co/kibana/kibana:8.15.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- SERVER_NAME=kibana
- I18N_LOCALE=zh-CN # 设置中文界面
- ELASTICSEARCH_USERNAME=elastic # 可配置安全认证
- ELASTICSEARCH_PASSWORD=changeme
ports:
- "5601:5601"
volumes:
- ./kibana/data:/usr/share/kibana/data
depends_on:
- elasticsearch
filebeat:
image: docker.elastic.co/beats/filebeat:8.15.0
container_name: filebeat
user: root
volumes:
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro # 映射修改后的配置文件
- /var/log:/var/log:ro # 采集宿主机日志
depends_on:
- logstash
command: ["/usr/share/filebeat/filebeat", "-e", "-d", "*"] # 显式指定文件路径和命令
1️⃣ 创建目录结构
bash
cd /etc/docker/docker-elk
# Elasticsearch 数据卷
mkdir -p es_data
# Kibana 目录
mkdir -p ./kibana/data
# Logstash pipeline 目录
mkdir -p pipeline
# Filebeat 配置目录
mkdir -p filebeat
2️⃣ 创建文件并写入内容
(1) Elasticsearch 配置
bash
cat > elasticsearch.yml <<'EOF'
cluster.name: "docker-cluster"
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node
bootstrap.memory_lock: true
# 启用安全
xpack.security.enabled: true
xpack.monitoring.collection.enabled: true
# ❗ 禁用 https(否则 Kibana 配 http:// 会连不上)
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false
EOF
(2) Logstash 配置
bash
cat > pipeline/logstash.conf <<'EOF'
input {
beats {
port => 5044
}
}
filter {
#
# 通用字段:根据 log.file.path 添加 appname
#
if "dnf.log" in [log][file][path] {
mutate { add_field => { "appname" => "dnf" } }
} else if "messages" in [log][file][path] {
mutate { add_field => { "appname" => "system" } }
} else {
mutate { add_field => { "appname" => "app" } }
}
#
# 1. 解析 dnf.log 格式
# 示例: 2025-09-28T08:53:19+0800 DDEBUG Plugins were unloaded.
#
if [appname] == "dnf" {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:logtime}\s+%{WORD:log.level}\s+%{GREEDYDATA:msg}"
}
}
date {
match => ["logtime", "yyyy-MM-dd'T'HH:mm:ssZ"]
target => "@timestamp"
}
}
#
# 2. 解析 syslog 格式 (/var/log/messages)
# 示例: Sep 28 09:02:08 localhost NetworkManager[753]: <info> ...
#
else if [appname] == "system" {
grok {
match => {
"message" => "%{SYSLOGTIMESTAMP:logtime} %{HOSTNAME:host} %{DATA:program}(?:\[%{POSINT:pid}\])?: %{GREEDYDATA:msg}"
}
}
date {
match => ["logtime", "MMM d HH:mm:ss", "MMM dd HH:mm:ss"]
target => "@timestamp"
timezone => "Asia/Shanghai"
}
# 提取 <info>/<err>/<warn> 为 log.level
if [msg] =~ /<info>/ {
mutate { add_field => { "log.level" => "INFO" } }
} else if [msg] =~ /<err>/ {
mutate { add_field => { "log.level" => "ERROR" } }
} else if [msg] =~ /<warn>/ {
mutate { add_field => { "log.level" => "WARN" } }
} else {
mutate { add_field => { "log.level" => "INFO" } } # 默认
}
}
#
# 3. 解析 应用日志 格式
# 示例: [ERROR] something bad happened
#
else if [appname] == "app" {
grok {
match => {
"message" => "\[%{LOGLEVEL:log.level}\]\s+%{GREEDYDATA:msg}"
}
overwrite => [ "log.level" ]
}
}
#
# 清理无用字段
#
mutate {
remove_field => ["logtime", "ecs", "agent", "host"]
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
user => "elastic" # 默认管理员用户名
password => "changeme" # 你的密码
index => "filebeat-logs-%{+YYYY.MM.dd}"
}
stdout { codec => rubydebug }
}
(3) Filebeat 配置
bash
cat > filebeat/filebeat.yml <<'EOF'
filebeat.inputs:
# 系统日志(syslog/messages)
- type: log
paths:
- /var/log/messages
fields:
appname: system
fields_under_root: true
# dnf.log
- type: log
paths:
- /var/log/dnf.log
fields:
appname: dnf
fields_under_root: true
# 业务应用日志(假设放在 /var/log/myapp/)
- type: log
paths:
- /var/log/myapp/*.log
fields:
appname: myapp
fields_under_root: true
# 输出到 Logstash
output.logstash:
hosts: ["logstash:5044"]
3️⃣ 设置文件权限
Docker 容器内通常以非 root 用户运行,为了避免挂载报错,可以把文件和目录权限设置成 用户可读:
bash
# 所有配置文件可读写
chmod -R 644 elasticsearch.yml
chmod -R 644 pipeline/logstash.conf
chmod go-w filebeat/filebeat.yml
# 确保目录可进入
# 给所有用户读写权限(单机测试可用)
chmod -R 777 es_data pipeline kibana
说明:
644
→ 文件所有者可读写,其他用户可读777
→ 目录所有用户可读写执行
🚀 启动 ELK
bash
cd /etc/docker/docker-elk
docker compose up -d
docker ps

过一会,查看发现 kibana没有启动,报错了。
查询日志:docker logs kibana
Error: [config validation of [elasticsearch].username]: value of "elastic" is forbidden. This is a superuser account that cannot write to system indices that Kibana needs to function. Use a service account token instead. Learn more: https://www.elastic.co/guide/en/elasticsearch/reference/8.0/service-accounts.html
at ensureValidConfiguration (/usr/share/kibana/node_modules/@kbn/core-config-server-internal/src/ensure_valid_configuration.js:43:11)
at Server.preboot (/usr/share/kibana/node_modules/@kbn/core-root-server-internal/src/server.js:174:7)
at Root.preboot (/usr/share/kibana/node_modules/@kbn/core-root-server-internal/src/root/index.js:47:14)
at bootstrap (/usr/share/kibana/node_modules/@kbn/core-root-server-internal/src/bootstrap.js:95:29)
at Command.<anonymous> (/usr/share/kibana/src/cli/serve/serve.js:233:5)
FATAL Error: [config validation of [elasticsearch].username]: value of "elastic" is forbidden. This is a superuser account that cannot write to system indices that Kibana needs to function. Use a service account token instead. Learn more: https://www.elastic.co/guide/en/elasticsearch/reference/8.0/service-accounts.html
这个报错其实很关键:
从 8.x 开始,Kibana 不允许用
elastic
超级用户账号直接连接 ES。因为 Kibana 需要写
.kibana
系统索引,而官方禁止超级用户执行这种写入。
🔑 正确做法:使用 Kibana 的 Service Account Token(官网推荐,但是有生命周期)
1. 在 ES 容器里创建 Token
进入 ES 容器:
bash
docker exec -it elasticsearch bash
运行:
bash
bin/elasticsearch-service-tokens create elastic/kibana kibana-token
输出类似:
eyJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiJ9.xxxxx
这个就是 Kibana 的 service token。
2. 修改 docker-compose.yml
中的 Kibana 配置
把 ELASTICSEARCH_USERNAME
/ ELASTICSEARCH_PASSWORD
删除,改为:
yaml
kibana:
image: docker.elastic.co/kibana/kibana:8.15.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- SERVER_NAME=kibana
- ELASTICSEARCH_SERVICEACCOUNTTOKEN=eyJ2ZXIiOiIxLjAiLCJhbGciOiJIUzI1NiJ9.xxxxx
ports:
- "5601:5601"
depends_on:
- elasticsearch
注意:这里的
ELASTICSEARCH_SERVICEACCOUNTTOKEN
就是上一步生成的 token。
3. 重启 Kibana
bash
docker compose restart kibana
现在 Kibana 就能正常连接 Elasticsearch,不会再报
value of "elastic" is forbidden
。
⚠️ 总结
elastic
超级用户只能用来做初始化管理(比如改密码)。- Kibana 必须用 service account token 连接 ES。
- 这也是 8.x 安全体系的一大变化。
⚠️ 重启es后,kibana登录失效问题
Kibana 使用的 Service Account Token 在 Elasticsearch 重启后可能会失效
,原因如下:
1️⃣ Token 的生命周期
- Elasticsearch 的 Service Account Token 是短期有效的密钥,用于 Kibana 或其他服务访问 Elasticsearch 系统索引
- 默认不是永久有效,如果 ES 重启、集群重新初始化或 Token 被删除,Kibana 就无法用旧 Token 连接
2️⃣ 实际影响
-
如果你在 docker-compose 中用
ELASTICSEARCH_SERVICEACCOUNTTOKEN=<token>
启动 Kibana -
ES 容器重启后,这个 token 很可能不再有效
-
Kibana 会报错,类似:
FATAL Error: unable to authenticate with Elasticsearch using service account token
3️⃣ 解决方案
方案 A:使用普通用户替代 Token(推荐)
- 在 ES 中创建一个 普通用户,授予 Kibana 系统索引权限:
bash
# 创建角色
curl -u elastic:changeme -X PUT "http://localhost:9200/_security/role/kibana_user_role" -H 'Content-Type: application/json' -d '{
"cluster": ["monitor"],
"indices": [{"names": [".kibana*"], "privileges": ["all"]}]
}'
# 创建用户
curl -u elastic:changeme -X POST "http://localhost:9200/_security/user/kibana_user" -H 'Content-Type: application/json' -d '{
"password":"kibanapwd",
"roles":["kibana_user_role"],
"full_name":"Kibana User"
}'
- Docker Compose 配置:
yaml
kibana:
environment:
- ELASTICSEARCH_USERNAME=kibana_user
- ELASTICSEARCH_PASSWORD=kibanapwd
- 优点:不会随 ES 重启失效,稳定可靠
- 浏览器访问也用
kibana_user / kibanapwd
登录
方案 B:每次重启都生成新 Token
- 在每次 ES 启动后,用命令生成新的 Service Account Token,再更新 Kibana 配置
- 缺点:需要额外脚本维护,复杂且不稳定
✅ 总结
方法 | 是否受重启影响 | 是否推荐 |
---|---|---|
Service Account Token | 会失效 | 不推荐用于 Docker Compose 长期部署 |
普通用户(kibana_user) | 不会失效 | 推荐 |
🔍 验证 ELK 部署
注意:首次访问页面需要提供用户名,密码登录。用户名:elastic,密码:changeme
,密码是docker-compose.yml 中配置的
-
Elasticsearch: http://服务器IP:9200
-
Kibana: http://服务器IP:5601
-
Logstash: 监听 5044 端口
🖼️ ELK + Filebeat 架构图(Mermaid)
ELK 核心服务 - CentOS 9 服务器 日志采集 - Filebeat 日志来源 - CentOS 9 服务器 读取日志文件 读取日志文件 Beats协议
5044端口 过滤/转换后
写入索引 提供REST API
查询数据 Web界面
http://IP:5601 Logstash
日志处理 Elasticsearch
数据存储/检索 Kibana
可视化分析 Filebeat
采集器 系统日志
/var/log/* 应用日志
/opt/app/logs 用户/运维人员
📊 优缺点分析
✅ 优点
- 快速部署,环境隔离,适合开发/测试
- Filebeat 轻量高效,适合日志采集
- Docker Compose 配置灵活,可一键启停
❌ 缺点
- 单机性能有限,不适合大规模生产环境
- 数据持久化需注意卷配置
- 默认关闭安全认证,不适合公网生产部署
🏁 总结
本文完整介绍了 CentOS 9 + Docker Compose 部署 ELK + Filebeat 单机版 的方法:
- 搭建 ELK 平台
- 配置 Filebeat 日志采集