本文基于实际项目,记录了使用 Docker Compose 部署 ELK 日志收集平台的完整过程,包括架构设计、配置详解、踩坑记录和效果展示。

前言

作为运维人员,你是否遇到过这些场景:

  • 服务器报错了,日志分散在 10 台机器上,一台一台 ssh 上去 cat
  • Nginx 访问量突然暴涨,哪个接口被刷了?
  • 程序挂了,想看最近 1 小时的错误日志,却不知道从哪找?

ELK 就是解决这些问题的利器!

它能把分散在各台服务器的日志集中收集、全文搜索、可视化展示,让你在一个网页上就能查所有机器的日志。

什么是 ELK?

ELK 是三个工具的组合:

组件 作用 类比
Elasticsearch 存储和搜索日志的数据库 图书馆的书架 + 检索系统
Logstash 过滤、解析、转换日志 图书馆的分拣员
Kibana 可视化展示日志 图书馆的查询终端

再加上一个轻量级采集器 Filebeat,组成完整的日志收集体系。

架构设计

整体架构

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Nginx     │───▶│  Filebeat   │───▶│  Logstash   │───▶│Elasticsearch│
│  产生日志   │    │  采集日志   │    │  过滤解析   │    │  存储日志   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
                                                              │
                                                              ▼
                                                     ┌─────────────┐
                                                     │   Kibana    │
                                                     │  可视化展示 │
                                                     └─────────────┘

数据流向

  1. Nginx 产生访问日志(JSON 格式)
  2. Filebeat 读取日志文件,发送给 Logstash
  3. Logstash 解析 JSON,提取 IP、时间、URL、状态码等字段
  4. 解析后的日志存入 Elasticsearch
  5. 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 { ... } — 提取时间字段,替换默认的 @timestamp
  • mutate { 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 中查看日志

  1. 打开 Kibana:http://localhost:5601
  2. 点击左上角菜单 → “Discover”
  3. 点击 “Create data view”
  4. 填写:
    • Name: nginx-access-*
    • Pattern: nginx-access-*
    • Timestamp: @timestamp
  5. 点击 “Save data view to Kibana”
  6. 现在可以看到日志了!

踩坑记录

踩坑 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 帮你找到"具体是什么问题"

扩展方向

  1. 收集多个应用的日志 — 修改 Filebeat 配置,添加多个 input
  2. 添加告警 — 使用 Elasticsearch 的 Watcher 或 Kibana 的 Alerting
  3. 日志保留策略 — 配置 Elasticsearch 的 ILM(Index Lifecycle Management)
  4. 安全加固 — 启用 Elasticsearch 的 X-Pack Security
  5. 性能优化 — 调整 Elasticsearch 的分片和副本数量

总结

通过本文,我们完成了:

  1. ✅ 使用 Docker Compose 部署 ELK 平台
  2. ✅ 配置 Nginx 输出 JSON 格式日志
  3. ✅ 配置 Filebeat 采集日志
  4. ✅ 配置 Logstash 解析日志
  5. ✅ 在 Kibana 中查看和可视化日志

ELK 是运维工程师的必备技能,掌握它能让你的日志管理效率提升 10 倍!

参考文档


如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐