引言
在微服务和分布式架构盛行的今天,应用的日志不再集中在一台服务器上,而是散落在各个容器、主机中。排查问题时,若仍使用 tail -f 逐台登录查看日志,效率极低且容易遗漏关键信息。ELK(Elasticsearch、Logstash、Kibana)是业界最为成熟的开源日志解决方案,能够方便地完成日志的采集、传输、存储、搜索和可视化。本文将带你从零开始,用 Docker Compose 快速搭建一套 ELK 系统,并实战采集 Nginx 访问日志,在 Kibana 中生成实时可视化看板。所有代码均可直接复制运行,让你在十分钟内拥有自己的日志中心。
核心概念与架构
实际的日志流水线通常由四部分组成:
- Filebeat(轻量级采集器):部署在产生日志的服务器上,监控日志文件并将日志推送到 Logstash 或直接到 Elasticsearch。它资源消耗极小,适合做日志的"搬运工"。
- Logstash(数据处理管道):负责接收、过滤、转换日志。可以用 Grok 解析非结构化日志,还可以丰富字段(如添加地理位置信息)。
- Elasticsearch(存储与搜索引擎):存储索引后的日志数据,提供近乎实时的搜索和分析能力。
- Kibana(可视化界面):通过图表、仪表盘展示 Elasticsearch 中的数据,支持交互式搜索。
一个典型的架构如下:
Nginx(容器) → Filebeat → Logstash → Elasticsearch → Kibana
本文示例中,所有组件都运行在 Docker 中,方便快速搭建和复现。
实战:搭建 ELK + Filebeat 采集 Nginx 日志
1. 环境准备
确保你的机器已安装 Docker 和 Docker Compose(版本 ≥ 1.29)。建议给 Docker 分配至少 4GB 内存,因为 Elasticsearch 比较占内存。
创建项目目录:
bash
mkdir elk-stack && cd elk-stack
2. 编写 Docker Compose 编排文件
在 docker-compose.yml 中定义 Elasticsearch、Logstash、Kibana、Nginx 和 Filebeat 服务。我们采用桥接网络 elk-net 让容器间互通。
yaml
version: '3.7'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.12
container_name: elasticsearch
environment:
- discovery.type=single-node # 单节点模式
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" # 限制堆内存
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata:/usr/share/elasticsearch/data
ports:
- "9200:9200"
networks:
- elk-net
logstash:
image: docker.elastic.co/logstash/logstash:7.17.12
container_name: logstash
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline # 自定义管道配置
ports:
- "5044:5044" # 接收 Filebeat 输入
depends_on:
- elasticsearch
networks:
- elk-net
kibana:
image: docker.elastic.co/kibana/kibana:7.17.12
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
networks:
- elk-net
nginx:
image: nginx:1.21
container_name: nginx
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # 自定义 Nginx 日志格式
- nginx_logs:/var/log/nginx
ports:
- "8080:80"
networks:
- elk-net
filebeat:
image: docker.elastic.co/beats/filebeat:7.17.12
container_name: filebeat
user: root # 需要 root 读取 Nginx 日志
volumes:
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- nginx_logs:/var/log/nginx:ro # 挂载同一个日志卷
command: filebeat -e -strict.perms=false
depends_on:
- logstash
- nginx
networks:
- elk-net
volumes:
esdata:
nginx_logs:
networks:
elk-net:
driver: bridge
3. 配置各组件
Nginx 日志格式
为了让日志包含更丰富的字段(如请求时间、状态码、响应时间等),我们自定义 JSON 格式的日志。创建 nginx/nginx.conf:
nginx
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format json_combined escape=json '{'
'"time_local":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.log json_combined;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
重启 Nginx 后,访问日志将是 JSON 格式,非常便于 Logstash 解析。
Logstash 管道
创建 logstash/pipeline/nginx.conf:
input {
beats {
port => 5044
}
}
filter {
# 如果日志是 JSON 格式(Nginx 访问日志),直接解析
if [log][file][path] =~ "access" {
json {
source => "message"
target => "nginx" # 解析后的字段放入 nginx 对象
}
# 将常用字段提升到顶级
mutate {
copy => {
"[nginx][remote_addr]" => "client_ip"
"[nginx][status]" => "status_code"
"[nginx][request_time]" => "response_time"
"[nginx][request]" => "request_url"
}
convert => {
"status_code" => "integer"
"response_time" => "float"
}
}
# 移除原始 message 和 nginx 大对象(可选)
# mutate { remove_field => ["message", "nginx"] }
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "nginx-%{+YYYY.MM.dd}"
user => "elastic"
password => "changeme" # 如有安全设置需配置,这里暂不启用
manage_template => false
}
# stdout { codec => rubydebug } # 调试时可开启
}
此配置将 Filebeat 传来的日志按 JSON 解析,提取出 Nginx 的关键字段,最终存入以日期分片的索引中。
Filebeat 配置
创建 filebeat/filebeat.yml:
yaml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
# 多行合并(如果需要)
# multiline.pattern: '^\{'
# multiline.negate: true
# multiline.match: after
output.logstash:
hosts: ["logstash:5044"]
Filebeat 监控 Nginx 日志文件,并发送给 Logstash。
4. 启动服务
在项目根目录下执行:
bash
docker-compose up -d
首次启动会拉取镜像,稍等片刻。待所有容器状态为 Up 后,用浏览器访问 http://localhost:8080 多次,产生一些 Nginx 日志。
5. 在 Kibana 中查看与分析
- 打开浏览器访问
http://localhost:5601。 - 进入 Management → Stack Management → Index Patterns ,创建索引模式。
- Index pattern name 填写
nginx-* - 选择时间字段
@timestamp(由 Filebeat 自动添加)。
- Index pattern name 填写
- 创建成功后,进入 Discover 页面,选择
nginx-*索引,即可看到 Nginx 日志数据。可以使用 KQL 查询,例如:status_code:404查找 404 请求。 - 制作可视化看板:
- 点击 Create new 选择 Lens 或 Visualization 。
- 以柱状图展示每分钟请求量:x 轴为
@timestamp,y 轴为Count。 - 将
status_code作为分桶维度,统计各状态码分布。 - 保存图表并添加到 Dashboard,最终生成实时监控仪表盘。
这样,一个基础但完整的日志中心便搭建完成。
常见问题与注意事项
-
Elasticsearch 内存不足导致启动失败
现象:
elasticsearch容器反复退出。解决方案:增加 Docker 可用内存,或调整ES_JAVA_OPTS的-Xms和-Xmx参数,但最小不低于 512m。生产环境建议分配 2G 以上。 -
Filebeat 无权读取日志文件
日志卷权限默认为 root,需要使用
user: root运行 Filebeat 容器并为配置文件设置-strict.perms=false。生产环境建议用chmod或组授权更安全的方式处理。 -
Logstash 管道语法错误
如果 Logstash 启动后自动退出,可能是配置文件有误。可以通过
docker logs logstash查看错误信息。调试时可开启stdout { codec => rubydebug }输出,将日志打印在控制台,确认数据流转正常。 -
索引模式未匹配到数据
检查时间字段选择,确认日志中确实有
@timestamp且是有效的日期类型。若使用自定义解析,确保date过滤器正确转换了时间字段。 -
磁盘空间增长过快
Elasticsearch 索引默认每天创建一个,长期运行会占用大量磁盘。建议设置 ILM(索引生命周期管理)策略,定期删除或归档老旧索引。
-
安全性
本文示例未开启安全认证,仅适合演示环境。生产环境中务必启用 X-Pack 认证、加密、用户角色等安全功能。
总结
本文详细介绍了如何使用 Docker Compose 快速部署一套 ELK + Filebeat 日志收集系统,并实战采集 Nginx 的 JSON 格式日志,在 Kibana 中实现了搜索和可视化。通过这套组合,无论是开发环境排错,还是生产环境监控,都能大幅提升日志处理效率。
从本文你可以学到:
-
ELK 各个组件的作用和数据流向
-
Docker 化部署 ELK 的 YAML 编排技巧
-
Nginx 日志格式定制与 Filebeat、Logstash 的配置联动
-
Kibana 索引模式与可视化图表的基本操作
希望这篇实战指南能帮助你在项目中轻松落地日志中心。下一步你可以尝试加入 Grafana、告警规则,或扩展到 Kubernetes 日志采集,打造企业级可观测性平台。