
👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕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'
该架构具备三大核心优势:
接下来,我们将按模块逐一手把手实现。
🐍 二、准备基础环境: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,而是一套经过工程验证的、面向真实世界的日志解决方案。
它的价值体现在:
| 实时性 | 秒级延迟,需手动 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)
你无需支付任何许可费用,也无需绑定某家云厂商——这正是云原生精神的精髓:开放、标准、可移植、自主可控。
📣 十一、下一步行动建议
学完本文,你可以立即着手:
🌈 最后,送你一句运维箴言: “日志不是写给机器看的,而是写给人类未来某个焦头烂额的深夜时刻看的。” 所以,请认真对待每一行 log.info(),就像在为未来的自己写一封情书 💌。
Happy Logging! 🐳✨📊
🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨





