ELK 日志收集平台实战:Docker Compose 一键部署,从零搭建运维日志看板
本文基于实际项目,记录了使用 Docker Compose 部署 ELK 日志收集平台的完整过程,包括架构设计、配置详解、踩坑记录和效果展示。
前言
作为运维人员,你是否遇到过这些场景:
- 服务器报错了,日志分散在 10 台机器上,一台一台
ssh上去cat? - Nginx 访问量突然暴涨,哪个接口被刷了?
- 程序挂了,想看最近 1 小时的错误日志,却不知道从哪找?
ELK 就是解决这些问题的利器!
它能把分散在各台服务器的日志集中收集、全文搜索、可视化展示,让你在一个网页上就能查所有机器的日志。
什么是 ELK?
ELK 是三个工具的组合:
| 组件 | 作用 | 类比 |
|---|---|---|
| Elasticsearch | 存储和搜索日志的数据库 | 图书馆的书架 + 检索系统 |
| Logstash | 过滤、解析、转换日志 | 图书馆的分拣员 |
| Kibana | 可视化展示日志 | 图书馆的查询终端 |
再加上一个轻量级采集器 Filebeat,组成完整的日志收集体系。
架构设计
整体架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Nginx │───▶│ Filebeat │───▶│ Logstash │───▶│Elasticsearch│
│ 产生日志 │ │ 采集日志 │ │ 过滤解析 │ │ 存储日志 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Kibana │
│ 可视化展示 │
└─────────────┘
数据流向
- Nginx 产生访问日志(JSON 格式)
- Filebeat 读取日志文件,发送给 Logstash
- Logstash 解析 JSON,提取 IP、时间、URL、状态码等字段
- 解析后的日志存入 Elasticsearch
- Kibana 从 Elasticsearch 读取数据,展示图表
为什么用 Filebeat?
你可能会问:为什么不直接让 Logstash 读日志文件?
原因:
- Logstash 是 Java 写的,吃资源(内存大)
- Filebeat 是 Go 写的,轻量级,只负责"读文件、发送"
- 实际生产环境:每台服务器装 Filebeat 采集日志,集中发给 Logstash 处理
类比:
- Filebeat = 快递员(上门取件)
- Logstash = 分拣中心(拆包、分类、打包)
- Elasticsearch = 仓库(存储)
- Kibana = 查询系统(查快递到哪了)
环境准备
前置条件
- Docker 已安装
- Docker Compose 已安装
- 至少 4GB 可用内存
- 至少 10GB 可用磁盘空间
检查命令
docker --version
docker compose version
free -h
df -h /
部署步骤
第一步:创建项目目录
mkdir -p /root/elk-platform
cd /root/elk-platform
mkdir -p nginx/conf.d nginx/logs logstash/pipeline filebeat
第二步:配置 Nginx
创建 nginx/nginx.conf:
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;
# 定义 JSON 格式的日志格式
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
创建 nginx/conf.d/default.conf:
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/access.log json_combined;
error_log /var/log/nginx/error.log;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
为什么要用 JSON 格式?
传统的 Nginx 日志是这样的:
192.168.1.1 - - [04/Jun/2026:10:30:00 +0000] "GET /index.html HTTP/1.1" 200 1234
Logstash 需要用正则表达式解析,容易出错。改成 JSON 格式后:
{"time_local":"04/Jun/2026:10:30:00 +0000","remote_addr":"192.168.1.1","request":"GET /index.html HTTP/1.1","status":"200"}
Logstash 直接解析 JSON,简单可靠。
第三步:配置 Logstash
创建 logstash/pipeline/nginx.conf:
input {
beats {
port => 5044
}
}
filter {
json {
source => "message"
target => "nginx"
}
date {
match => ["[nginx][time_local]", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
mutate {
convert => {
"[nginx][status]" => "integer"
"[nginx][body_bytes_sent]" => "integer"
"[nginx][request_time]" => "float"
}
}
mutate {
add_field => { "source" => "nginx" }
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "nginx-access-%{+YYYY.MM.dd}"
}
}
配置详解:
input { beats { port => 5044 } }— 接收 Filebeat 发送的数据json { source => "message" }— 解析 JSON 格式的日志date { ... }— 提取时间字段,替换默认的 @timestampmutate { convert => ... }— 转换字段类型(字符串转数字)output { elasticsearch { ... } }— 输出到 Elasticsearch,索引名按日期分
第四步:配置 Filebeat
创建 filebeat/filebeat.yml:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
output.logstash:
hosts: ["logstash:5044"]
logging.level: info
logging.to_files: true
logging.files:
path: /var/log/filebeat
name: filebeat
keepfiles: 7
permissions: 0644
注意:hosts 要写 logstash:5044,不能写 localhost:5044,因为容器内的 localhost 指的是容器自己。
第五步:创建 Docker Compose
创建 docker-compose.yml:
version: '3.8'
services:
elasticsearch:
image: elasticsearch:8.11.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- ES_JAVA_OPTS=-Xms512m -Xmx512m
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
networks:
- elk
healthcheck:
test: ["CMD-SHELL","curl -f http://localhost:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
logstash:
image: logstash:8.11.0
container_name: logstash
ports:
- "5044:5044"
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
environment:
- LS_JAVA_OPTS=-Xms256m -Xmx256m
networks:
- elk
depends_on:
elasticsearch:
condition: service_healthy
kibana:
image: kibana:8.11.0
container_name: kibana
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
networks:
- elk
depends_on:
elasticsearch:
condition: service_healthy
filebeat:
image: elastic/filebeat:8.11.0
container_name: filebeat
user: root
volumes:
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- ./nginx/logs:/var/log/nginx:ro
- filebeat_data:/usr/share/filebeat/data
networks:
- elk
depends_on:
- logstash
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/logs:/var/log/nginx
networks:
- elk
volumes:
es_data:
filebeat_data:
networks:
elk:
driver: bridge
第六步:启动服务
docker-compose up -d
启动需要 1-2 分钟,用以下命令查看状态:
docker-compose ps
第七步:验证服务
# 验证 Elasticsearch
curl http://localhost:9200
# 生成测试日志
curl http://localhost
curl http://localhost/test
# 查看 Elasticsearch 索引
curl http://localhost:9200/_cat/indices
第八步:在 Kibana 中查看日志
- 打开 Kibana:
http://localhost:5601 - 点击左上角菜单 → “Discover”
- 点击 “Create data view”
- 填写:
- Name:
nginx-access-* - Pattern:
nginx-access-* - Timestamp:
@timestamp
- Name:
- 点击 “Save data view to Kibana”
- 现在可以看到日志了!
踩坑记录
踩坑 1:Nginx log_format 必须在 http 块内
现象:
nginx: [emerg] "log_format" directive is not allowed here
原因:
log_format是全局指令,必须在http块内定义- 我们把配置放在
conf.d/目录,这个目录的文件会被包含到server块内 server块内不能定义log_format
解决:
- 挂载完整的
nginx.conf,在http块内定义log_format conf.d/default.conf只定义server块
教训:
配置文件写完一定要测试,不能只看逻辑对不对。
踩坑 2:Filebeat 输出地址要用容器名
现象:
- Filebeat 日志显示连接 Logstash 失败
connection refused
原因:
- 配置文件中写的是
localhost:5044 - 在容器内
localhost指的是容器自己,不是 Logstash 容器
解决:
# 错误
output.logstash:
hosts: ["localhost:5044"]
# 正确
output.logstash:
hosts: ["logstash:5044"]
教训:
Docker 容器内访问其他容器,要用容器名,不能用 localhost。
踩坑 3:Elasticsearch 内存不足
现象:
elasticsearch exited with code 137
原因:
- Elasticsearch 默认需要 1GB 内存
- 虚拟机内存不足,被 OOM Killer 杀掉
解决:
environment:
- ES_JAVA_OPTS=-Xms512m -Xmx512m
教训:
Java 应用要限制内存,避免占用太多资源。
踩坑 4:Filebeat 权限不足
现象:
Permission denied
原因:
- Filebeat 默认用户是 filebeat(UID 1000)
- Nginx 日志文件的所有者是 root
- filebeat 用户没有读权限
解决:
filebeat:
user: root
教训:
Docker 挂载文件时要注意权限问题。
效果展示
Kibana Discover 页面
在 Discover 页面可以看到所有的 Nginx 访问日志,包括:
- 请求时间
- 客户端 IP
- 请求路径
- HTTP 状态码
- 响应大小
- 用户代理
创建可视化图表
在 Kibana 中可以创建各种图表:
- 请求量趋势:按时间展示请求量变化
- 状态码分布:饼图展示 200、404、500 的比例
- Top 10 IP:表格展示访问量最大的 IP
- 请求路径:词云展示最常访问的路径
常用命令
# 启动服务
docker-compose up -d
# 停止服务
docker-compose down
# 停止并删除数据
docker-compose down -v
# 查看所有日志
docker-compose logs -f
# 查看某个服务的日志
docker-compose logs -f elasticsearch
docker-compose logs -f logstash
docker-compose logs -f kibana
docker-compose logs -f filebeat
docker-compose logs -f nginx
# 重启服务
docker-compose restart
# 进入容器
docker-compose exec elasticsearch bash
docker-compose exec logstash bash
和 Prometheus 的区别
| Prometheus | ELK | |
|---|---|---|
| 监控什么 | 指标(数字) | 日志(文本) |
| 典型问题 | “CPU 90% 了!” | “哪个接口报错了?” |
| 存储方式 | 时序数据库 | 全文搜索引擎 |
两者互补,不是替代关系。
- Prometheus 告诉你"服务器出问题了"
- ELK 帮你找到"具体是什么问题"
扩展方向
- 收集多个应用的日志 — 修改 Filebeat 配置,添加多个 input
- 添加告警 — 使用 Elasticsearch 的 Watcher 或 Kibana 的 Alerting
- 日志保留策略 — 配置 Elasticsearch 的 ILM(Index Lifecycle Management)
- 安全加固 — 启用 Elasticsearch 的 X-Pack Security
- 性能优化 — 调整 Elasticsearch 的分片和副本数量
总结
通过本文,我们完成了:
- ✅ 使用 Docker Compose 部署 ELK 平台
- ✅ 配置 Nginx 输出 JSON 格式日志
- ✅ 配置 Filebeat 采集日志
- ✅ 配置 Logstash 解析日志
- ✅ 在 Kibana 中查看和可视化日志
ELK 是运维工程师的必备技能,掌握它能让你的日志管理效率提升 10 倍!
参考文档
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)