文章前言

需求背景

本地开发机器 192.168.1.8,内网服务器 192.168.1.140(大磁盘),需要搭建轻量化 ELK 日志平台收集 SpringBoot 应用日志。

踩坑记录:根分区 Docker 日志暴涨 42G 触发 ES 磁盘洪水只读锁、ES 单节点 yellow 分片告警、版本不兼容、内网 TCP 日志采集不通等问题,完整记录从 0 到日志可视化全流程,同时补充SpringBoot 集群接入ELK 三节点集群生产拓展方案,后续复用直接复制配置即可。

环境版本统一(避免版本报错)

  • Elasticsearch:7.17.14
  • Kibana:7.17.14
  • Logstash:7.17.14
  • SpringBoot:2.7.x/ 3.x 通用
  • 部署方式:Docker Compose

一、单机 ELK 部署(内网 Demo 可用,本次实操完整版)

1.1 服务器目录结构(192.168.1.140)

plaintext

/home/tlrobot/elk
├── docker-compose.yml       # ELK编排文件
├── elasticsearch/
│   ├── plugins              # ES插件挂载目录
│   └── data                 # ES持久化数据目录
├── kibana/config/
│   └── kibana.yml           # Kibana配置
└── logstash/
    └── logstash.conf        # 日志采集输出规则

1.2 docker-compose.yml 完整配置(修复所有报错版本)

version: '3.8'
networks:
  my-network:

services:
  elasticsearch:
    image: elasticsearch:7.17.14
    ports:
      - '19200:9200'
      - '19300:9300'
    container_name: elasticsearch
    restart: always
    environment:
      - "cluster.name=docker-cluster"
      - 'discovery.type=single-node'
      - 'ES_JAVA_OPTS=-Xms512m -Xmx512m'
      # 单节点关闭副本,消除yellow分片告警
      - index.number_of_replicas=0
      # 调低磁盘洪水水位,提前预警
      - cluster.routing.allocation.disk.watermark.low=85%
      - cluster.routing.allocation.disk.watermark.high=90%
      - cluster.routing.allocation.disk.watermark.flood_stage=92%
    volumes:
      - ./elasticsearch/plugins:/usr/share/elasticsearch/plugins
      - ./elasticsearch/data:/usr/share/elasticsearch/data
    networks:
      - my-network

  kibana:
    image: kibana:7.17.14
    container_name: kibana
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
    ports:
      - '5601:5601'
    links:
      - elasticsearch:es
    environment:
      - ELASTICSEARCH_URL=http://elasticsearch:9200
      - elasticsearch.hosts=http://es:9200
      - I18N_LOCALE=zh-CN
    networks:
      - my-network
    depends_on:
      - elasticsearch

  logstash:
    image: logstash:7.17.14
    container_name: logstash
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime
      - ./logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - '4560:4560'
      - '50000:50000/tcp'
      - '50000:50000/udp'
      - '9600:9600'
    environment:
      LS_JAVA_OPTS: -Xms512m -Xmx512m
      TZ: Asia/Shanghai
      MONITORING_ENABLED: false
    links:
      - elasticsearch:es
    networks:
      - my-network
    depends_on:
      - elasticsearch

1.3 Logstash 采集配置 logstash/logstash.conf

监听 4560 端口 TCP JSON 日志,按天分索引存储

input {
  tcp {
    mode => "server"
    host => "0.0.0.0"
    port => 4560
    codec => json_lines
    type => "info"
  }
}
filter {}
output {
  elasticsearch {
      action => "index"
      hosts => "es:9200"
      index => "springboot-demo-log-%{+YYYY.MM.dd}"
    }
}

1.4 服务器初始化 & 启动命令

1)创建目录并授权(ES 数据目录必须 777,启动报错根源)

mkdir -p elasticsearch/plugins elasticsearch/data kibana/config logstash
chmod 777 elasticsearch/data

2)防火墙放行内网端口

firewall-cmd --add-port=4560/tcp --permanent
firewall-cmd --add-port=19200/tcp --permanent
firewall-cmd --add-port=5601/tcp --permanent
firewall-cmd --reload

3)启动 ELK

docker compose up -d
# 查看启动状态
docker ps

启动成功截图:

1.5 踩坑 1:磁盘爆满触发 ES 429 洪水只读锁报错

报错日志:

disk usage exceeded flood-stage watermark, index has read-only-allow-delete block
根因

Docker 默认存储在根分区 /,容器日志暴涨 42G 占满磁盘,ES 自动上锁拒绝写入。

解决方案
  1. 清空超大容器日志(不删除镜像、容器)
# 截断42G日志文件,瞬间释放42G空间
truncate -s 0 /var/lib/docker/containers/xxx/json.log
  1. 解锁 ES 只读锁
curl -XPUT -H "Content-Type: application/json" http://127.0.0.1:19200/_all/_settings -d '{"index.blocks.read_only_allow_delete": null}'
  1. 长期根治:Docker 全局限制日志大小,避免日志暴涨
vim /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "500m",
    "max-file": "3"
  }
}
systemctl restart docker

二、SpringBoot 应用接入 ELK(单机 / 集群通用)

2.1 Maven 依赖 pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Logstash JSON日志编码器 -->
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
        <version>7.0.1</version>
    </dependency>
</dependencies>

2.2 日志配置 resources/logback-spring.xml

内网 Logstash 地址:192.168.1.140:4560

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <!-- TCP输出到内网Logstash -->
    <appender name="LOGSTASH_TCP" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>192.168.1.140:4560</destination>
        <reconnectionDelay>3000</reconnectionDelay>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeMdcKeyName>appName</includeMdcKeyName>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="LOGSTASH_TCP"/>
    </root>
</configuration>

2.3 应用配置 application.yml

spring:
  application:
    name: elk-springboot-demo

2.4 测试日志 Controller

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LogTestController {
    private static final Logger log = LoggerFactory.getLogger(LogTestController.class);

    @GetMapping("/demo/log")
    public String printLog() {
        log.info("测试普通日志,参数={}", 666);
        log.warn("警告日志测试");
        log.error("异常日志测试", new Exception("demo模拟报错"));
        return "日志发送完成,请访问Kibana查看";
    }
}

2.5 验证链路

  1. 本地启动 SpringBoot,访问 http://localhost:8080/demo/log
  2. Kibana 索引管理页面查看自动生成日志索引

三、Kibana 可视化查看日志完整操作

3.1 创建索引模式

左侧菜单栏「Kibana」→「索引模式」→ 创建索引模式,匹配规则填写:

springboot-demo-log-*

时间字段选择 @timestamp 完成创建。

3.2 Discover 日志检索页面(最终效果图)

点击左上角汉堡菜单,展开主菜单→「数据」→「Discover」,即可查看所有采集日志,支持过滤、检索、查看异常堆栈。

常用检索语法

  1. 只查看错误日志:level: ERROR
  2. 检索包含指定关键词日志:message: 测试异常
  3. 筛选当前服务日志:appName: elk-springboot-demo

四、拓展 1:SpringBoot 集群接入 ELK 高可用方案

4.1 多台 Logstash 做故障转移

内网部署 2 台 Logstash 服务器:

  • 192.168.1.140
  • 192.168.1.141 修改 logback 的 destination,多地址逗号分隔,自动故障转移:
<destination>192.168.1.140:4560,192.168.1.141:4560</destination>

4.2 多应用集群区分日志

每个微服务配置独立 spring.application.name,Kibana 通过 appName 字段筛选不同服务日志。

4.3 生产优化:新增 Redis/Kafka 缓冲

高并发场景建议在 SpringBoot 和 Logstash 中间增加 Kafka,避免 Logstash 宕机丢失日志。


五、拓展 2:ELK 三节点集群部署方案(生产容灾)

5.1 ES 集群核心改动

  1. 删除 discovery.type=single-node 单节点配置
  2. 开启副本分片 index.number_of_replicas=1,集群状态变为 Green
  3. 配置集群节点发现、主节点选举
  4. 三个 ES 容器独立端口、独立数据卷

Logstash 输出多 ES 节点(负载均衡)

output {
  elasticsearch {
      hosts => ["es-node1:9200","es-node2:9200","es-node3:9200"]
      index => "springboot-demo-log-%{+YYYY.MM.dd}"
      load_balancing => true
    }
}

5.2 Kibana 集群配置

连接多个 ES 节点,防止单 ES 节点宕机无法访问日志

environment:
  elasticsearch.hosts: ["http://es-node1:9200","http://es-node2:9200"]

5.3 单机 vs 集群核心差异对比

维度 单机 ELK(Demo) 三节点 ELK 集群(生产)
分片状态 yellow(副本 1)/green(副本 0) Green,分片有副本容灾
数据安全 单磁盘故障日志全部丢失 分片多副本,单节点宕机不丢日志
并发能力 单 ES 节点性能有限 分片分散多节点,支持高并发写入
部署复杂度 极简,单 compose 文件 多节点编排,需配置集群发现
SpringBoot 配置 单 Logstash 地址 多 Logstash 地址故障转移

六、总结 & 复用指南

  1. Demo 快速搭建:直接复制第一章节单机 docker-compose,执行初始化命令即可一键启动 ELK;
  2. 日志采集链路:SpringBoot TCP 输出 JSON → Logstash 4560 端口接收 → ES 按天分索引 → Kibana 可视化检索;
  3. 核心踩坑点:根分区 Docker 日志爆满、ES 数据目录权限不足、ELK 版本不一致、单节点 yellow 分片;
  4. 生产拓展:多 Logstash 高可用、ES 三节点集群、Kafka 缓冲削峰。

更多推荐