运维知识
悠悠
2026年7月12日

线上服务挂了2小时没日志可查!K8s日志采集的坑,我一个不落全踩了

凌晨1点47分,钉钉告警群炸了。订单服务5xx飙到30%,用户下单全部超时。我爬起来打开Kibana准备查日志——空的。切到Grafana看Fluent Bit指标——采集量断崖式归零。

日志没了,线上故障等于在黑暗里排雷。

最后这个故障花了2小时才定位根因,整个过程中日志系统的崩溃让排查效率暴降80%。事后复盘,我发现日志链路上埋了至少4个定时炸弹,而这些问题90%的K8s集群都存在,只是还没爆而已。

事故现场

时间:周三凌晨1:47
告警:order-service 5xx rate > 20%,持续5分钟
用户体感:下单接口超时,页面转圈后报错
业务影响:核心交易链路中断,预估损失约12万/小时

打开Kibana查order-service最近10分钟ERROR日志,索引里最后一条停在1:32——距离告警过去15分钟,之后一片空白。

kubectl logs直接看Pod:

$ kubectl logs order-service-7b8f9c6d4-xk2mn -n production --tail=100

能看到实时日志。应用还在打日志,问题出在采集链路

排查踩坑——4步全错

第一步:Fluent Bit挂了?

$ kubectl get pods -n logging -l app=fluent-bit
NAME              READY   STATUS    RESTARTS   AGE
fluent-bit-4xk2n  1/1     Running   0          3d
fluent-bit-7nm9p  1/1     Running   0          3d
fluent-bit-bx5st  1/1     Running   0          3d

全部Running,没重启。

第二步:ES写不进去了?

$ curl -s elasticsearch.logging.svc:9200/_cluster/health | jq .
{
  "status": "green",
  "number_of_nodes": 3,
  "active_shards": 847
}

集群健康,green状态。

第三步:网络抖动?

$ kubectl exec -it fluent-bit-4xk2n -n logging -- curl -s elasticsearch.logging.svc:9200

通的,返回正常。

到这里已经懵了——Agent在跑、ES正常、网络通畅,日志就是不进来

第四步:看Fluent Bit自身日志

$ kubectl logs fluent-bit-4xk2n -n logging --tail=50

关键报错出现了:

[2025/07/10 01:33:15] [warn] [input:tail:tail.0] pausing tail, mem_buf_limit reached
[2025/07/10 01:33:15] [warn] [input:tail:tail.0] pausing tail, mem_buf_limit reached

内存缓冲区满了,停止读取日志文件。

但缓冲区为什么会满?正常情况下日志从读取到写入ES只需几十毫秒,不可能积压。继续往上翻:

[2025/07/10 01:30:42] [error] [output:es:es.0] HTTP status=429, Retry in 15 seconds
[2025/07/10 01:30:57] [error] [output:es:es.0] HTTP status=429, Retry in 30 seconds
[2025/07/10 01:31:27] [error] [output:es:es.0] HTTP status=429, Retry in 60 seconds

ES返回429(Too Many Requests),bulk写入被限流了。

根因——4个炸弹同时引爆

整个崩溃链路:

应用日志量突增 → ES bulk队列打满返回429 → Fluent Bit重试堆积 → 内存缓冲区撑满 → 停止采集 → 日志断流

日志量为什么突增?回头看监控,1:30有个CronJob触发数据同步任务,每秒打印上万行DEBUG日志,日志量瞬间打到平时50倍。

4个问题叠加:

问题本质
CronJob没有日志级别控制生产环境跑DEBUG
ES写入队列太小bulk_queue_size默认200,一打满就拒绝
Fluent Bit mem_buf_limit只有5MB下游阻塞立刻停采
没有对高日志量Pod做采集隔离一个Pod暴增拖垮整个节点

任何一个单独存在都不会炸,四个叠在一起就是连环爆。

修复方案——配置直接拿走

修复1:缓冲区扩容 + 文件缓冲兜底

内存缓冲从5MB调到50MB,开启filesystem buffer,满了先落盘不丢弃:

[SERVICE]
    Flush         1
    Log_Level     info
    HTTP_Server   On
    HTTP_Listen   0.0.0.0
    HTTP_Port     2020
    storage.path  /var/fluent-bit/storage/
    storage.sync  normal
    storage.checksum off
    storage.max_chunks_up 128

[INPUT]
    Name              tail
    Path              /var/log/containers/*.log
    Parser            cri
    Tag               kube.*
    Mem_Buf_Limit     50MB
    storage.type      filesystem
    Skip_Long_Lines   On
    Refresh_Interval  5
    Rotate_Wait       30
    DB                /var/fluent-bit/tail-db/pos.db

核心storage.type filesystem 配合 storage.path,缓冲区满了日志写本地磁盘暂存,下游恢复后再发。这是防丢日志的关键配置。

修复2:ES扩大写入队列

# elasticsearch.yml
thread_pool:
  write:
    queue_size: 1000    # 默认200
    size: 8             # 写入线程数,按CPU核数调

indices:
  memory:
    index_buffer_size: 20%   # 默认10%

Fluent Bit output端加重试控制:

[OUTPUT]
    Name              es
    Match             kube.*
    Host              elasticsearch.logging.svc.cluster.local
    Port              9200
    Logstash_Format   On
    Logstash_Prefix   k8s-logs
    Replace_Dots      On
    Suppress_Type_Name On
    Buffer_Size       512KB
    Retry_Limit       5
    workers           2

workers 2 多线程并发写入提高吞吐,Retry_Limit 5 避免无限重试耗尽资源。

修复3:高日志量Pod采集隔离 + 限流

[FILTER]
    Name    grep
    Match   kube.*
    Exclude $kubernetes['labels']['logging-exclude'] true

[FILTER]
    Name    throttle
    Match   kube.batch.*
    Rate    1000
    Window  5
    Interval 1s
    Print_Status true

CronJob Pod模板加标签:

metadata:
  labels:
    logging-exclude: "true"

修复4:Kyverno强制生产环境日志级别

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-log-level
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-log-level
      match:
        resources:
          kinds:
            - Pod
          namespaces:
            - production
      validate:
        message: "生产环境Pod必须设置LOG_LEVEL且不能为DEBUG"
        pattern:
          spec:
            containers:
              - env:
                  - name: LOG_LEVEL
                    value: "!DEBUG"

架构改造——加Kafka缓冲层

改造前

容器stdout → Fluent Bit → Elasticsearch(直连)

改造后

容器stdout → Fluent Bit → Kafka(缓冲) → Logstash → Elasticsearch
                                        ↘ S3(审计日志冷存储)

Kafka扛着,ES挂了日志也不丢。Fluent Bit的Kafka output配置:

[OUTPUT]
    Name              kafka
    Match             kube.*
    Brokers           kafka-0.kafka.svc:9092,kafka-1.kafka.svc:9092,kafka-2.kafka.svc:9092
    Topics            k8s-logs
    Timestamp_Key     @timestamp
    rdkafka.log.connection.close false
    rdkafka.request.required.acks 1
    rdkafka.message.max.bytes    1048576

日志系统自身的监控——不能裸奔

# Prometheus告警规则
groups:
  - name: logging-pipeline
    rules:
      - alert: FluentBitInputPaused
        expr: fluentbit_input_paused == 1
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Fluent Bit采集暂停!日志可能丢失"

      - alert: FluentBitOutputRetryHigh
        expr: rate(fluentbit_output_retries_total[5m]) > 10
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "Fluent Bit写入重试频繁,下游可能阻塞"

      - alert: KafkaConsumerLag
        expr: kafka_consumer_group_lag{group="logstash-consumer"} > 100000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "日志消费积压超10万条"

Fluent Bit开启metrics端点:

[SERVICE]
    HTTP_Server  On
    HTTP_Listen  0.0.0.0
    HTTP_Port    2020

Prometheus通过 :2020/api/v1/metrics/prometheus 拉取。

三条血泪教训

1. 日志系统必须有自己的监控和告警。 日志链路断了你根本不知道,等排查线上故障才发现没日志——已经晚了。

2. 永远不要让一个Pod的日志暴增拖垮整个节点的采集。 做好隔离和限流,CronJob/Job这类批处理任务是重灾区。

3. 内存缓冲区 ≠ 不丢日志。 Mem_Buf_Limit 到了就停采,必须配合文件缓冲或Kafka才能真正保障。


如果你的K8s集群日志方案还是Fluent Bit直连ES、没有缓冲层、没有采集限流、没有日志系统自身的监控——趁没爆之前赶紧改,别等凌晨2点被教做人。

觉得有用就转发一下,让更多人少踩这个坑 🙏


公众号:耕云躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码