欢迎光临
我们一直在努力

Docker - 利用ELK收集Docker容器日志实战

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Docker这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • Docker – 利用ELK收集Docker容器日志实战 🐳🔍📊
    • 🧱 一、整体架构设计与数据流向
    • 🐍 二、准备基础环境:Docker 与 Docker Compose
    • ☕ 三、编写结构化日志的 Java Spring Boot 应用
      • 3.1 Maven 依赖(pom.xml)
      • 3.2 主应用类(OrderApiApplication.java)
      • 3.3 结构化日志配置(src/main/resources/logback-spring.xml)
      • 3.4 构建 Docker 镜像(Dockerfile)
    • 📦 四、Filebeat:轻量级日志采集器(Shipper)
      • 4.1 创建 Filebeat 配置(filebeat.yml)
      • 4.2 编写 Filebeat Docker Compose 服务
    • ⚙️ 五、Logstash:日志增强与路由中枢
      • 5.1 Logstash 配置(logstash.conf)
      • 5.2 Logstash Docker Compose 服务
    • 🌐 六、Elasticsearch:分布式日志存储与搜索引擎
      • 6.1 创建 ILM 策略(logs-retention-policy.json)
      • 6.2 初始化 ILM 策略(curl 命令)
      • 6.3 Elasticsearch Docker Compose 服务
    • 📊 七、Kibana:日志可视化与告警平台
      • 7.1 Kibana 配置(kibana.yml)
      • 7.2 Kibana Docker Compose 服务
      • 7.3 创建 Kibana 数据视图(Index Pattern)
      • 7.4 构建首个监控仪表盘
    • 🧩 八、完整 docker-compose.yml 整合
    • 🚨 九、生产环境加固建议
      • 9.1 高可用(HA)部署
      • 9.2 性能调优
      • 9.3 安全合规
      • 9.4 成本控制
    • 🌟 十、总结:为什么这个方案值得信赖?
    • 📣 十一、下一步行动建议

Docker – 利用ELK收集Docker容器日志实战 🐳🔍📊

在现代云原生应用架构中,容器化部署已成为标准实践。而随着微服务数量激增、容器生命周期缩短、实例动态伸缩常态化,如何高效、统一、可追溯地收集、分析和告警容器日志,已成为运维可观测性(Observability)体系的核心挑战之一。单纯依赖 docker logs 查看单个容器输出,早已无法满足生产环境对实时性、聚合性、结构化查询与长期归档的需求。

本文将带你从零开始,构建一套稳定、可扩展、生产就绪的 ELK 日志采集流水线,专为 Docker 容器生态深度优化。我们将不依赖任何黑盒 SaaS 服务,全程使用开源组件自主搭建,并重点解决以下关键问题:

  • ✅ 如何让任意 Java 应用容器(Spring Boot)自动将结构化日志输出到标准输出(stdout),并携带 traceId、service.name 等上下文字段
  • ✅ 如何通过 Filebeat 的 docker input 类型,精准捕获容器日志流,自动解析时间戳、容器 ID、镜像名、标签等元数据
  • ✅ 如何在 Logstash 中完成日志 enrichment(丰富)、过滤、字段标准化与敏感信息脱敏
  • ✅ 如何利用 Elasticsearch 的索引生命周期管理(ILM)实现日志自动滚动与冷热分层
  • ✅ 如何在 Kibana 中创建可复用的仪表盘,支持按服务、状态码、响应耗时、错误关键词多维下钻分析
  • ✅ 如何保障日志链路高可用:Filebeat 故障自动重连、Logstash 持久化队列、ES 副本容错

整个方案完全基于 Docker Compose 编排,无需修改宿主机系统配置,开箱即用,且所有组件均采用当前主流稳定版本(Elasticsearch 8.14+、Logstash 8.14+、Kibana 8.14+、Filebeat 8.14+),兼容 Docker Desktop 与 Linux 服务器环境。

💡 小贴士:ELK 是 Elastic Stack 的经典简称,但自 7.x 版本起,官方已将其统一命名为 Elastic Stack。不过因历史习惯与社区认知,“ELK” 仍被广泛使用。本文中 ELK = Elasticsearch + Logstash + Kibana,外加 Filebeat 作为轻量级日志 shipper,构成完整日志管道: Container → stdout/stderr → Filebeat → Logstash → Elasticsearch → Kibana


🧱 一、整体架构设计与数据流向

我们先建立清晰的全局视角。下图展示了日志从 Java 应用容器出发,经由各中间件,最终抵达 Kibana 可视化界面的完整路径:

渲染错误: Mermaid 渲染失败: Parse error on line 22: …ion Layer,Collection Layer,Processing La ———————–^ Expecting 'SEMI', 'NEWLINE', 'EOF', 'AMP', 'COLON', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'SPACE'

该架构具备三大核心优势:

  • 解耦清晰:每个组件职责单一,可独立升级、扩缩容。例如,当日志量突增时,可单独增加 Logstash 实例数,不影响 ES 存储或 Kibana 展示。
  • 弹性可靠:Filebeat 内置背压机制与磁盘缓冲;Logstash 支持持久化队列(Persistent Queue),即使 ES 临时不可用,日志也不会丢失;ES 多副本保障读写高可用。
  • 语义丰富:从容器启动参数(如 –label com.example.service=order-api)、环境变量(SPRING_PROFILES_ACTIVE=prod),到应用内嵌的 MDC(Mapped Diagnostic Context)字段,全部自动注入日志事件,形成端到端可观测性上下文。
  • 接下来,我们将按模块逐一手把手实现。


    🐍 二、准备基础环境:Docker 与 Docker Compose

    确保你已安装以下工具(版本建议):

    • Docker Engine ≥ 24.0.0
    • Docker Compose ≥ 2.20.0(推荐使用 docker compose v2 命令,非旧版 docker-compose)

    验证命令:

    docker –version # Docker version 24.0.7, build afdd53b
    docker compose version # Docker Compose version v2.21.0

    🔗 延伸阅读:Docker 官方安装指南(适用于 macOS / Windows / Linux) https://docs.docker.com/engine/install/

    为避免端口冲突与权限问题,请确认以下端口未被占用: ✅ 9200(Elasticsearch HTTP) ✅ 5601(Kibana UI) ✅ 5044(Logstash Beats input) ✅ 9000(Java 应用示例端口)

    若已在使用,可在后续 docker-compose.yml 中修改对应 ports 映射。


    ☕ 三、编写结构化日志的 Java Spring Boot 应用

    日志采集质量,始于应用层输出规范。一个“好日志”应具备:时间戳准确、格式统一(推荐 JSON)、字段语义明确、包含必要上下文(traceId、spanId、service name)。

    我们以一个极简但典型的订单服务为例,使用 Spring Boot 3.x(基于 Jakarta EE 9+)构建,并集成 spring-boot-starter-logging 与 logback-spring.xml 自定义配置。

    3.1 Maven 依赖(pom.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.7</version>
    <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>order-api</artifactId>
    <version>1.0.0</version>
    <name>order-api</name>

    <properties>
    <java.version>17</java.version>
    <logstash-logback-encoder.version>7.4</logstash-logback-encoder.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!– 结构化日志支持:将 logback 日志转为 JSON –>
    <dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>${logstash-logback-encoder.version}</version>
    </dependency>
    <!– OpenTelemetry 自动埋点(可选,用于 traceId 注入) –>
    <dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-webmvc-6.0</artifactId>
    <version>2.0.0-alpha</version>
    </dependency>
    <!– Micrometer 用于指标暴露(与日志正交,但同属可观测性) –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    🔗 logstash-logback-encoder 文档(权威配置说明) https://github.com/logfellow/logstash-logback-encoder

    3.2 主应用类(OrderApiApplication.java)

    package com.example.orderapi;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.*;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;

    @SpringBootApplication
    public class OrderApiApplication {

    private static final Logger log = LoggerFactory.getLogger(OrderApiApplication.class);

    public static void main(String[] args) {
    SpringApplication.run(OrderApiApplication.class, args);
    }

    @GetMapping("/health")
    public Map<String, String> health() {
    return Map.of("status", "UP", "timestamp", String.valueOf(System.currentTimeMillis()));
    }

    @PostMapping("/orders")
    public Map<String, Object> createOrder(@RequestBody OrderRequest request) {
    // 模拟生成唯一 traceId(生产环境应由 OpenTelemetry 自动注入)
    String traceId = UUID.randomUUID().toString().replace("-", "");
    // 将 traceId 注入 MDC,供 logback 在日志中自动提取
    org.slf4j.MDC.put("traceId", traceId);
    org.slf4j.MDC.put("service.name", "order-api");
    org.slf4j.MDC.put("env", System.getProperty("spring.profiles.active", "dev"));

    try {
    log.info("Received order creation request",
    "orderId", request.getOrderId(),
    "customerId", request.getCustomerId(),
    "amount", request.getAmount()
    );

    // 模拟业务逻辑
    if (request.getAmount() <= 0) {
    log.warn("Invalid order amount", "amount", request.getAmount());
    throw new IllegalArgumentException("Amount must be positive");
    }

    // 模拟成功
    String orderId = "ORD-" + System.currentTimeMillis();
    log.info("Order created successfully", "newOrderId", orderId, "traceId", traceId);

    Map<String, Object> result = new HashMap<>();
    result.put("code", 201);
    result.put("message", "Created");
    result.put("data", Map.of("orderId", orderId, "traceId", traceId));
    return result;

    } catch (Exception e) {
    log.error("Failed to create order",
    "errorType", e.getClass().getSimpleName(),
    "errorMessage", e.getMessage(),
    "traceId", traceId,
    "stackTrace", e.getStackTrace()
    );
    throw e;
    } finally {
    // 清理 MDC,防止线程复用污染
    org.slf4j.MDC.clear();
    }
    }
    }

    record OrderRequest(String orderId, String customerId, Double amount) {}

    💡 关键点解析:

    • 使用 org.slf4j.MDC.put() 将 traceId, service.name, env 注入日志上下文,这些字段将在 logback-spring.xml 中被引用。
    • log.info() 和 log.error() 调用时传入 键值对参数(而非字符串拼接),这是 logstash-logback-encoder 正确提取结构化字段的前提。
    • finally { MDC.clear() } 是强制最佳实践,避免 Tomcat 线程池复用导致 MDC 泄漏。

    3.3 结构化日志配置(src/main/resources/logback-spring.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>

    <!– 定义 JSON encoder –>
    <appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <!– 时间戳格式化为 ISO8601,与 ES 兼容 –>
    <timestampPattern>yyyy-MM-dd'T'HH:mm:ss.SSSXXX</timestampPattern>
    <!– 添加自定义字段 –>
    <customFields>{"service":"order-api","version":"1.0.0"}</customFields>
    <!– 包含 MDC 中的所有字段 –>
    <includeContext>true</includeContext>
    <!– 包含异常堆栈(仅 error 级别) –>
    <includeCallerInfo>false</includeCallerInfo>
    <includeStacktrace>true</includeStacktrace>
    <stackTraceMaxLength>1000</stackTraceMaxLength>
    </encoder>
    </appender>

    <!– 标准文本日志(仅开发调试用) –>
    <appender name="CONSOLE_TEXT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} – %msg%n</pattern>
    </encoder>
    </appender>

    <!– 生产环境只启用 JSON 输出 –>
    <springProfile name="prod">
    <root level="INFO">
    <appender-ref ref="CONSOLE_JSON"/>
    </root>
    </springProfile>

    <!– 开发环境可同时输出文本与 JSON –>
    <springProfile name="dev">
    <root level="DEBUG">
    <appender-ref ref="CONSOLE_JSON"/>
    <appender-ref ref="CONSOLE_TEXT"/>
    </root>
    </springProfile>
    </configuration>

    ✅ 此配置确保:

    • 所有日志以标准 JSON 行格式(NDJSON)输出到 stdout,每行一个 JSON 对象;
    • 自动包含 @timestamp(ISO8601)、service、version、traceId、env 等字段;
    • stackTrace 字段在 error 日志中完整保留,便于排查;
    • 通过 Spring Profile 控制不同环境行为,生产环境禁用冗余文本日志。

    3.4 构建 Docker 镜像(Dockerfile)

    # 使用官方 Spring Boot 运行时镜像(更小、更安全)
    FROM springio/spring-boot:3.2-jre17

    # 设置工作目录
    WORKDIR /app

    # 复制 jar 包(假设 mvn package 后生成 target/order-api-1.0.0.jar)
    COPY target/order-api-1.0.0.jar app.jar

    # 暴露端口
    EXPOSE 9000

    # 设置 JVM 参数(生产建议调优)
    ENV JAVA_OPTS="-Xms256m -Xmx512m -Djava.security.egd=file:/dev/./urandom"

    # 启动命令
    ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]

    # 添加容器标签,供 Filebeat 自动发现
    LABEL com.example.service="order-api"
    LABEL com.example.team="backend"
    LABEL com.example.env="prod"

    构建命令:

    mvn clean package
    docker build -t order-api:1.0.0 .

    ✅ 验证镜像是否正常运行:

    docker run -p 9000:9000 –rm –label com.example.service=order-api order-api:1.0.0
    # 然后访问 http://localhost:9000/health,应返回 JSON {"status":"UP",…}

    此时,你已在终端看到类似如下 JSON 日志(已美化):

    {
    "@timestamp": "2024-07-15T10:22:33.456+08:00",
    "@version": "1",
    "message": "Order created successfully",
    "logger_name": "com.example.orderapi.OrderApiApplication",
    "thread_name": "http-nio-9000-exec-1",
    "level": "INFO",
    "level_value": 20000,
    "traceId": "a1b2c3d4e5f678901234567890abcdef",
    "service.name": "order-api",
    "env": "prod",
    "newOrderId": "ORD-1721010153456",
    "service": "order-api",
    "version": "1.0.0"
    }

    ✅ 结构化日志已就绪! 这是 ELK 流水线最坚实的第一块基石。


    📦 四、Filebeat:轻量级日志采集器(Shipper)

    Filebeat 是 Elastic 官方推荐的轻量级日志 shipper,专为容器环境优化。它直接读取 Docker daemon 的日志文件(/var/lib/docker/containers/*/*.log),无需在容器内安装代理,资源占用极低(内存 < 50MB)。

    4.1 创建 Filebeat 配置(filebeat.yml)

    # filebeat.yml
    filebeat.inputs:
    – type: docker
    # 自动发现所有正在运行的容器
    containers.ids:
    – "*"
    # 只采集带特定 label 的容器(可选,提高精度)
    containers.include_labels: ['com.example.service']
    # 排除系统容器(如 elasticsearch, kibana)
    containers.exclude_labels: ['io.kubernetes.pod.namespace', 'com.docker.compose.project']

    # 解析容器日志时间戳(Docker 默认格式)
    processors:
    – add_docker_metadata: ~
    – add_host_metadata:
    when.not.contains.tags: forwarded
    – add_cloud_metadata: ~

    # 输出到 Logstash(而非直连 ES,以便做复杂处理)
    output.logstash:
    hosts: ["logstash:5044"]
    # 启用 SSL(生产必须)
    ssl.enabled: false
    # 重试策略
    loadbalance: true
    bulk_max_size: 2048

    # 日志轮转与性能调优
    filebeat.registry.path: /var/lib/filebeat/registry
    filebeat.registry.rotation.period: 24h

    # 日志级别(生产建议 info,调试可设 debug)
    logging.level: info
    logging.to_files: true
    logging.files:
    path: /var/log/filebeat
    name: filebeat
    keepfiles: 7
    permissions: 0644

    📌 关键配置说明:

    • type: docker:声明输入类型为 Docker 容器日志,Filebeat 将自动监听 /var/lib/docker/containers/ 目录。
    • containers.include_labels:精准筛选业务容器,避免采集 ELK 自身日志造成干扰。
    • add_docker_metadata:自动注入 docker.container.id, docker.image, docker.labels.* 等元数据字段。
    • output.logstash:指向 Logstash 的 Beats input 端口(5044),不直连 ES —— 因为我们需要 Logstash 做字段清洗与增强。

    4.2 编写 Filebeat Docker Compose 服务

    在 docker-compose.yml 中添加:

    filebeat:
    image: docker.elastic.co/beats/filebeat:8.14.0
    container_name: filebeat
    user: root
    volumes:
    – /var/lib/docker/containers:/var/lib/docker/containers:ro
    – /var/run/docker.sock:/var/run/docker.sock:ro
    – ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
    – ./filebeat–data:/var/lib/filebeat/data
    – ./filebeat–logs:/var/log/filebeat
    restart: unless–stopped
    depends_on:
    – logstash
    networks:
    – elk

    ⚠️ 注意事项:

    • user: root:必需,因需读取 /var/run/docker.sock 和容器日志目录。
    • volumes 映射:/var/lib/docker/containers 和 /var/run/docker.sock 必须以 ro(只读)挂载,符合最小权限原则。
    • filebeat-data 目录用于持久化 registry(记录已读日志位置),防止容器重启后重复采集。

    ⚙️ 五、Logstash:日志增强与路由中枢

    Logstash 是 ELK 中的“瑞士军刀”,负责日志的 Filter(过滤)、Enrich(丰富)、Transform(转换)、Drop(丢弃)。对于容器日志,我们主要用它完成:

    • ✅ 将 Filebeat 发送的原始 JSON 日志解析为结构化事件
    • ✅ 提取并标准化 http.status_code, response_time_ms, url.path 等关键字段(即使原始日志未显式提供)
    • ✅ 对 message 字段进行 Grok 解析(兼容非 JSON 日志)
    • ✅ 删除 DEBUG 级别日志(生产环境)
    • ✅ 对 creditCard, password 等敏感字段执行 PII(Personally Identifiable Information)脱敏
    • ✅ 根据 service.name 或 docker.labels.com.example.team 将日志路由至不同 ES 索引

    5.1 Logstash 配置(logstash.conf)

    # logstash.conf
    input {
    beats {
    port => 5044
    ssl => false
    }
    }

    filter {
    # 1. 如果日志已是 JSON,直接解析(来自我们的 Spring Boot 应用)
    if [message] =~ /^\\{.*\\}$/ {
    json {
    source => "message"
    skip_on_invalid_json => true
    remove_field => ["message"]
    }
    }

    # 2. 对非 JSON 日志(如 legacy 应用),尝试用 Grok 解析常见格式
    else {
    grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \\[%{DATA:thread}\\] %{LOGLEVEL:level} %{JAVACLASS:logger} – %{GREEDYDATA:msg}" }
    overwrite => ["message"]
    tag_on_failure => ["_grokparsefailure_legacy"]
    }
    }

    # 3. 标准化时间戳(确保 @timestamp 字段存在且正确)
    date {
    match => ["@timestamp", "ISO8601"]
    target => "@timestamp"
    timezone => "Asia/Shanghai"
    }

    # 4. 提取 HTTP 相关字段(如果 message 或 url 字段存在)
    if [url] or [message] =~ /GET|POST|PUT|DELETE/ {
    dissect {
    mapping => {
    "message" => "%{method} %{path} %{protocol} %{status} %{bytes}"
    }
    convert_datatype => { "status" => "integer" }
    tag_on_failure => ["_dissect_failure_http"]
    }
    }

    # 5. PII 脱敏:掩码信用卡号(简单正则,生产请用专用库)
    if [creditCard] {
    mutate {
    gsub => ["creditCard", "(\\d{4})\\d{8}(\\d{4})", "\\1****\\2"]
    }
    }
    if [password] {
    mutate {
    replace => ["password", "[REDACTED]"]
    }
    }

    # 6. 删除无价值字段(减少 ES 存储压力)
    mutate {
    remove_field => [
    "prospector.type",
    "beat.hostname",
    "beat.name",
    "host.name",
    "input.type",
    "agent.ephemeral_id",
    "agent.id",
    "agent.type",
    "agent.version",
    "ecs.version"
    ]
    }

    # 7. 生产环境过滤:丢弃 DEBUG 日志
    if "prod" in [env] and [level] == "DEBUG" {
    drop { }
    }
    }

    output {
    # 根据 service.name 路由到不同数据流(Data Stream),实现逻辑隔离
    if [service.name] == "order-api" {
    elasticsearch {
    hosts => ["https://elasticsearch:9200"]
    username => "${ELASTIC_USERNAME:elastic}"
    password => "${ELASTIC_PASSWORD:changeme}"
    ssl_certificate_verification => false
    ilm_enabled => true
    ilm_rollover_alias => "logs-order-api"
    ilm_pattern => "{now/d}-000001"
    ilm_policy => "logs-retention-policy"
    data_stream => true
    data_stream_type => "logs"
    data_stream_dataset => "order-api"
    data_stream_namespace => "default"
    }
    }
    else if [service.name] == "payment-gateway" {
    elasticsearch {
    hosts => ["https://elasticsearch:9200"]
    username => "${ELASTIC_USERNAME:elastic}"
    password => "${ELASTIC_PASSWORD:changeme}"
    ssl_certificate_verification => false
    ilm_enabled => true
    ilm_rollover_alias => "logs-payment-gateway"
    ilm_pattern => "{now/d}-000001"
    ilm_policy => "logs-retention-policy"
    data_stream => true
    data_stream_type => "logs"
    data_stream_dataset => "payment-gateway"
    data_stream_namespace => "default"
    }
    }
    else {
    # 默认路由到通用日志流
    elasticsearch {
    hosts => ["https://elasticsearch:9200"]
    username => "${ELASTIC_USERNAME:elastic}"
    password => "${ELASTIC_PASSWORD:changeme}"
    ssl_certificate_verification => false
    ilm_enabled => true
    ilm_rollover_alias => "logs-generic"
    ilm_pattern => "{now/d}-000001"
    ilm_policy => "logs-retention-policy"
    data_stream => true
    data_stream_type => "logs"
    data_stream_dataset => "generic"
    data_stream_namespace => "default"
    }
    }
    }

    ✅ 此配置实现了:

    • 双模解析:优先尝试 JSON 解析;失败则 fallback 到 Grok,兼容异构日志源。
    • 智能路由:根据 service.name 将日志写入不同 Data Stream,天然支持多租户与权限隔离。
    • 安全合规:内置 PII 脱敏规则,满足 GDPR / 等保基本要求。
    • 生产就绪:自动丢弃 DEBUG 日志,大幅降低存储成本与查询延迟。

    🔗 Logstash Filter 插件官方文档(深入 grok / dissect / mutate 用法) https://www.elastic.co/guide/en/logstash/current/filter-plugins.html

    5.2 Logstash Docker Compose 服务

    logstash:
    image: docker.elastic.co/logstash/logstash:8.14.0
    container_name: logstash
    environment:
    – xpack.monitoring.enabled=false
    – LS_JAVA_OPTS=–Xms512m –Xmx512m
    – ELASTIC_USERNAME=elastic
    – ELASTIC_PASSWORD=changeme
    volumes:
    – ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro
    – ./logstash–data:/usr/share/logstash/data
    ports:
    – "5044:5044"
    restart: unless–stopped
    depends_on:
    – elasticsearch
    networks:
    – elk

    💡 提示:LS_JAVA_OPTS 设为 -Xms512m -Xmx512m 是生产推荐值,避免频繁 GC;若日志量极大,可提升至 1g。


    🌐 六、Elasticsearch:分布式日志存储与搜索引擎

    Elasticsearch 是日志的“大脑”,负责存储、索引、全文检索与聚合分析。在 8.x 版本中,Data Stream(数据流) + ILM(索引生命周期管理) 已成为处理时间序列日志(如日志)的事实标准。

    6.1 创建 ILM 策略(logs-retention-policy.json)

    {
    "policy": {
    "phases": {
    "hot": {
    "min_age": "0ms",
    "actions": {
    "rollover": {
    "max_size": "50gb",
    "max_age": "30d"
    },
    "set_priority": {
    "priority": 100
    }
    }
    },
    "warm": {
    "min_age": "30d",
    "actions": {
    "allocate": {
    "number_of_replicas": 1,
    "require": {
    "data": "warm"
    }
    },
    "set_priority": {
    "priority": 50
    }
    }
    },
    "cold": {
    "min_age": "90d",
    "actions": {
    "freeze": {},
    "set_priority": {
    "priority": 0
    }
    }
    },
    "delete": {
    "min_age": "365d",
    "actions": {
    "delete": {}
    }
    }
    }
    }
    }

    📌 策略解读:

    • hot 阶段(0ms~30天):主分片写入热节点,自动 rollover(滚动)当单索引达 50GB 或满 30 天;
    • warm 阶段(30~90天):副本数设为 1,分配到 warm 节点(需额外配置 node.attr.data=“warm”);
    • cold 阶段(90~365天):冻结索引(freeze),释放内存,仅保留磁盘存储;
    • delete 阶段(365天后):自动删除,彻底释放空间。

    🔗 Elasticsearch ILM 官方指南(含 warm/cold 节点配置) https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html

    6.2 初始化 ILM 策略(curl 命令)

    # 启动 ES 后执行(需在宿主机或容器内运行)
    curl -X PUT "http://localhost:9200/_ilm/policy/logs-retention-policy" \\
    -H 'Content-Type: application/json' \\
    -d @logs-retention-policy.json

    6.3 Elasticsearch Docker Compose 服务

    elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
    container_name: elasticsearch
    environment:
    – discovery.type=single–node
    – xpack.security.enabled=true
    – xpack.security.http.ssl.enabled=true
    – xpack.security.http.ssl.key=certs/http.key
    – xpack.security.http.ssl.certificate=certs/http.crt
    – xpack.security.transport.ssl.enabled=true
    – xpack.security.transport.ssl.verification_mode=certificate
    – xpack.security.transport.ssl.key=certs/transport.key
    – xpack.security.transport.ssl.certificate=certs/transport.crt
    – xpack.security.transport.ssl.certificate_authorities=certs/ca.crt
    – ELASTIC_PASSWORD=changeme
    – "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    volumes:
    – ./es–data:/usr/share/elasticsearch/data
    – ./certs:/usr/share/elasticsearch/config/certs:ro
    ports:
    – "9200:9200"
    – "9300:9300"
    restart: unless–stopped
    networks:
    – elk

    🔐 安全说明:Elasticsearch 8.x 默认启用安全特性(TLS + RBAC)。上述配置启用了 HTTPS 和 Transport 加密。certs/ 目录需提前生成证书(可通过 elasticsearch-certutil 工具)。

    ⚠️ 若仅本地测试,可简化为 xpack.security.enabled=false,但生产环境严禁关闭安全。


    📊 七、Kibana:日志可视化与告警平台

    Kibana 是 ELK 的“眼睛”,提供 Discover(探索)、Dashboard(仪表盘)、Alerting(告警)、Stack Management(集群管理)等能力。

    7.1 Kibana 配置(kibana.yml)

    # kibana.yml
    server.port: 5601
    server.host: "0.0.0.0"
    server.name: "kibana"
    elasticsearch.hosts: ["https://elasticsearch:9200"]
    elasticsearch.username: "kibana_system"
    elasticsearch.password: "changeme"
    elasticsearch.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca.crt"]
    elasticsearch.ssl.verificationMode: "certificate"
    monitoring.ui.container.elasticsearch.enabled: true

    7.2 Kibana Docker Compose 服务

    kibana:
    image: docker.elastic.co/kibana/kibana:8.14.0
    container_name: kibana
    environment:
    – SERVERNAME=kibana
    – ELASTICSEARCH_HOSTS=https://elasticsearch:9200
    – ELASTICSEARCH_USERNAME=kibana_system
    – ELASTICSEARCH_PASSWORD=changeme
    – ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/ca.crt
    volumes:
    – ./kibana.yml:/usr/share/kibana/config/kibana.yml:ro
    – ./certs:/usr/share/kibana/config/certs:ro
    ports:
    – "5601:5601"
    restart: unless–stopped
    depends_on:
    – elasticsearch
    networks:
    – elk

    7.3 创建 Kibana 数据视图(Index Pattern)

    启动 Kibana 后(http://localhost:5601),首次登录需用 elastic/changeme(默认凭据)。

    进入 Stack Management → Index Patterns → Create index pattern:

    • Index pattern name: logs-*
    • Time field: @timestamp

    ✅ Kibana 将自动识别 logs-order-api, logs-payment-gateway 等 Data Stream,并映射其字段。

    7.4 构建首个监控仪表盘

    我们创建一个“订单服务健康概览”仪表盘,包含:

    • 📈 折线图:过去 1 小时 5xx 错误率趋势
    • 📊 柱状图:Top 5 错误消息(errorType)
    • ⏱️ 分布图:response_time_ms 百分位(P95, P99)
    • 🌐 地图:客户端 IP 地理分布(需开启 GeoIP 处理,本文暂略)

    Kibana 提供拖拽式构建,无需代码。核心查询 DSL 示例(用于 Lens 可视化):

    // 查询 5xx 错误率(每5分钟)
    {
    "aggs": {
    "rate": {
    "rate": {
    "field": "status"
    }
    }
    },
    "query": {
    "bool": {
    "must": [
    { "match": { "service.name": "order-api" } },
    { "range": { "@timestamp": { "gte": "now-1h/h" } } },
    { "range": { "status": { "gte": 500, "lt": 600 } } }
    ]
    }
    }
    }

    🔗 Kibana 可视化最佳实践指南 https://www.elastic.co/guide/en/kibana/current/visualize.html


    🧩 八、完整 docker-compose.yml 整合

    将以上所有服务整合为一份 docker-compose.yml:

    version: '3.8'

    networks:
    elk:
    driver: bridge

    volumes:
    es-data: {}
    filebeat-data: {}
    filebeat-logs: {}
    logstash-data: {}

    services:

    elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
    container_name: elasticsearch
    environment:
    – discovery.type=single–node
    – xpack.security.enabled=true
    – xpack.security.http.ssl.enabled=true
    – xpack.security.http.ssl.key=certs/http.key
    – xpack.security.http.ssl.certificate=certs/http.crt
    – xpack.security.transport.ssl.enabled=true
    – xpack.security.transport.ssl.verification_mode=certificate
    – xpack.security.transport.ssl.key=certs/transport.key
    – xpack.security.transport.ssl.certificate=certs/transport.crt
    – xpack.security.transport.ssl.certificate_authorities=certs/ca.crt
    – ELASTIC_PASSWORD=changeme
    – "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    volumes:
    – ./es–data:/usr/share/elasticsearch/data
    – ./certs:/usr/share/elasticsearch/config/certs:ro
    ports:
    – "9200:9200"
    – "9300:9300"
    restart: unless–stopped
    networks:
    – elk

    logstash:
    image: docker.elastic.co/logstash/logstash:8.14.0
    container_name: logstash
    environment:
    – xpack.monitoring.enabled=false
    – LS_JAVA_OPTS=–Xms512m –Xmx512m
    – ELASTIC_USERNAME=elastic
    – ELASTIC_PASSWORD=changeme
    volumes:
    – ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro
    – ./logstash–data:/usr/share/logstash/data
    ports:
    – "5044:5044"
    restart: unless–stopped
    depends_on:
    – elasticsearch
    networks:
    – elk

    kibana:
    image: docker.elastic.co/kibana/kibana:8.14.0
    container_name: kibana
    environment:
    – SERVERNAME=kibana
    – ELASTICSEARCH_HOSTS=https://elasticsearch:9200
    – ELASTICSEARCH_USERNAME=kibana_system
    – ELASTICSEARCH_PASSWORD=changeme
    – ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/ca.crt
    – ELASTICSEARCH_SSL_VERIFICATIONMODE=certificate
    volumes:
    – ./kibana.yml:/usr/share/kibana/config/kibana.yml:ro
    – ./certs:/usr/share/kibana/config/certs:ro
    ports:
    – "5601:5601"
    restart: unless–stopped
    depends_on:
    – elasticsearch
    networks:
    – elk

    filebeat:
    image: docker.elastic.co/beats/filebeat:8.14.0
    container_name: filebeat
    user: root
    volumes:
    – /var/lib/docker/containers:/var/lib/docker/containers:ro
    – /var/run/docker.sock:/var/run/docker.sock:ro
    – ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
    – ./filebeat–data:/var/lib/filebeat/data
    – ./filebeat–logs:/var/log/filebeat
    restart: unless–stopped
    depends_on:
    – logstash
    networks:
    – elk

    order-api:
    image: order–api:1.0.0
    container_name: order–api
    environment:
    – SPRING_PROFILES_ACTIVE=prod
    – LOG_LEVEL=INFO
    ports:
    – "9000:9000"
    labels:
    – "com.example.service=order-api"
    – "com.example.team=backend"
    – "com.example.env=prod"
    restart: unless–stopped
    networks:
    – elk

    ✅ 启动整套栈:

    # 第一次启动前,确保 certs/ 目录存在(或注释掉 TLS 配置)
    docker compose up -d

    # 查看日志流是否通畅
    docker compose logs -f filebeat
    docker compose logs -f logstash

    等待 1–2 分钟,访问 http://localhost:5601,登录后进入 Discover,选择 logs-* 数据视图,即可看到实时滚动的 order-api 日志!


    🚨 九、生产环境加固建议

    上述方案已具备生产可用基础,但大规模部署还需关注:

    9.1 高可用(HA)部署

    • Elasticsearch:至少 3 个 master-eligible 节点 + 多个 data 节点,启用 discovery.seed_hosts。
    • Logstash:部署多个实例,前端加 Load Balancer(如 Nginx),Filebeat 配置 loadbalance: true。
    • Filebeat:每个宿主机部署一个 DaemonSet(K8s)或 Service(Docker Swarm)。

    9.2 性能调优

    • Filebeat:增大 bulk_max_size(如 4096),启用 queue.mem.events: 8192。
    • Logstash:启用 pipeline.workers: 4(等于 CPU 核数),pipeline.batch.size: 125。
    • Elasticsearch:调整 indices.memory.index_buffer_size: 30%,refresh_interval: 30s(日志场景可放宽)。

    9.3 安全合规

    • 启用 TLS 双向认证(mTLS)于 Filebeat ↔ Logstash ↔ ES 链路。
    • 使用 Role-Based Access Control(RBAC):为 filebeat_internal 用户授 manage_ilm, create_index 权限;为 kibana_system 用户授 monitor 权限。
    • 审计日志:开启 xpack.security.audit.enabled: true,将审计日志发送至独立 ES 集群。

    9.4 成本控制

    • 启用 index.codec: best_compression 减少磁盘占用。
    • 对 message 字段设置 "index": false(不索引,仅存储),节省内存。
    • 使用 data stream 替代传统索引,ILM 自动清理过期数据,杜绝“磁盘爆满”。

    🔗 Elastic 官方生产检查清单 https://www.elastic.co/guide/en/elasticsearch/reference/current/production-checklist.html


    🌟 十、总结:为什么这个方案值得信赖?

    我们走完了从 Java 应用编码、Docker 打包、Filebeat 采集、Logstash 增强、Elasticsearch 存储到 Kibana 可视化的全链路。这不是一个玩具 Demo,而是一套经过工程验证的、面向真实世界的日志解决方案。

    它的价值体现在:

    维度传统方式(docker logs + grep)本 ELK 方案
    实时性 秒级延迟,需手动 tail -f 毫秒级采集,Logstash 持久化队列保障不丢
    可搜索性 grep 文本,无法关联字段 全字段倒排索引,支持 traceId: "abc" AND status >= 500
    可扩展性 宿主机磁盘瓶颈,无法水平扩展 ES 分片自动负载均衡,Filebeat 可无限扩容
    可维护性 日志散落各处,无统一 schema Data Stream 强制结构,ILM 自动生命周期管理
    可观测性深度 仅看到“发生了什么” 结合 traceId 关联上下游,定位“为什么发生”

    更重要的是,所有技术选型均为开源、成熟、社区活跃:

    • Spring Boot(VMware / Pivotal)
    • Docker(Moby Project)
    • Elastic Stack(Elastic NV)
    • Logstash Filter 插件(Apache 2.0 License)

    你无需支付任何许可费用,也无需绑定某家云厂商——这正是云原生精神的精髓:开放、标准、可移植、自主可控。


    📣 十一、下一步行动建议

    学完本文,你可以立即着手:

  • ✅ 将本文的 order-api 示例,替换为你团队的真实 Java 微服务,复用 logback-spring.xml 和 Dockerfile;
  • ✅ 在预发环境部署 docker-compose.yml,接入现有容器,观察日志是否自动出现;
  • ✅ 在 Kibana 中创建第一个告警:当 logs-order-api 中 5xx 错误率连续 5 分钟 > 3%,邮件通知值班人;
  • ✅ 将 Filebeat 配置中的 containers.include_labels 扩展为 ["com.example.service", "com.acme.team"],实现跨部门日志治理;
  • ✅ 阅读 Elastic 官方日志最佳实践,持续精进。
  • 🌈 最后,送你一句运维箴言: “日志不是写给机器看的,而是写给人类未来某个焦头烂额的深夜时刻看的。” 所以,请认真对待每一行 log.info(),就像在为未来的自己写一封情书 💌。

    Happy Logging! 🐳✨📊


    🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

    赞(0)
    未经允许不得转载:171主机测评 » Docker - 利用ELK收集Docker容器日志实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址