← 返回文章列表

无标题

15 分钟阅读
字号

Kubernetes 日志系统从入门到精通

本文系统讲解 K8s 日志系统的核心概念、三种采集方案、EFK 架构部署及生产实践,涵盖完整数据流分析、组件配置详解和面试高频问答。


一、为什么需要日志系统?

1.1 三个核心原因

1. 报警无法反馈根因

告警告诉你"服务不可用了",但为什么不可用?这时候需要去看服务运行时的日志来定位根因。告警是果,日志是因。

2. 多服务环境下逐一查日志太麻烦

几十上百个微服务,如果每个服务单独登录机器查日志,维护成本极高。需要一个统一的日志平台来集中查看和检索。

3. Pod 删除后日志会消失

K8s 中 Pod 如果没有挂载持久存储,日志存在容器运行时的临时目录里。Pod 一删,日志就没了。引入日志系统后,日志被实时采集到后端,持久化保存。


二、K8s 日志采集的三种方案

方案一:节点级 Agent(DaemonSet)

原理:在每个节点上运行一个日志采集 Agent(Fluentd),通过 hostPath 挂载宿主机的 /var/log/containers 目录,读取所有容器日志后发往后端。

宿主机 /var/log/containers/*.log  →  DaemonSet Fluentd  →  Elasticsearch

前提:应用必须把日志输出到 stdout/stderr,这样容器运行时才会把它写到宿主机文件。

优点:对应用零侵入,简单常见 缺点:只能采集 stdout/stderr 的日志,写到文件里的日志收不到


方案二:Sidecar 容器

如果应用把日志写到容器内文件,方案一就失效了。可以在 Pod 里注入一个 sidecar 容器,与业务容器共享存储卷,sidecar 读取日志文件内容后重定向到 stdout。

缺点

  1. 空间浪费一倍 — 日志写文件一次,sidecar 重定向到 stdout 又写一次
  2. 改造 Pod — 每个需要日志收集的 Pod 都要注入 sidecar

升级版:把 Fluentd 直接作为 sidecar 跑在 Pod 内部,不经过 stdout,直接从容器内文件读取后发送到后端。

  • 优点:更灵活,不浪费存储空间
  • 缺点:kubectl logs 无法使用,配置复杂

方案三:应用直接推送

应用代码层面直接集成 SDK(比如 Logback、Fluentd Client),直接把日志推送到日志后端。

优点:架构最精简,性能损耗最小 缺点:需要改动源代码,超出 K8s 范围


三种方案对比

方案部署形态日志来源侵入性
DaemonSet每节点一个 Agentstdout/stderr零侵入
Sidecar 读文件每个 Pod 一个 Sidecar容器内文件需要改 Pod
Sidecar 直推每个 Pod 一个 Fluentd容器内文件需要改 Pod
应用直推应用内置无限制改源代码

三、EFK 架构详解

3.1 什么是 Elasticsearch?

Elasticsearch 是一个分布式 RESTful 风格的搜索和数据分析引擎,主要用于存储、检索和分析海量日志数据。

核心特点

  • 分布式:数据分片存储,支持横向扩展
  • 倒排索引:让"在一亿条日志里找某条错误"变得毫秒级
  • 实时性:数据写入后可近实时被检索到
  • RESTful API:通过 HTTP 请求操作数据

3.2 Elasticsearch 核心概念

关系型数据库Elasticsearch说明
DatabaseIndex(索引)存储一类文档的地方
RowDocument(文档)存储的基本单元,一条日志就是一篇文档
ColumnField(字段)文档中的键值对
SchemaMapping(映射)定义字段的类型和属性

3.3 ES 集群三种角色

角色职责存储数据
Master集群管理、选主、调度分片
Data实际存储文档和索引数据
Client接收请求,转发到 Data 节点

四、完整日志数据流

4.1 两道关卡设计

┌─────────────────────────────────────────────────────────────┐
│                    完整日志流向                               │
└─────────────────────────────────────────────────────────────┘
 
Pod stdout

宿主机 /var/log/containers/*.log

Fluentd DaemonSet(第一道关卡)
    ↓ 按 logging=true 过滤
Kafka(削峰缓冲)

Logstash(第二道关卡)
    ↓ 按 logIndex 决定索引名
Elasticsearch

Kibana

两道关卡的作用

  • 第一道( Fluentd):只采集带有 logging=true 标签的 Pod 日志,精准控制采集范围
  • 第二道( Logstash):按 logIndex 决定 ES 中的索引名,实现数据隔离

4.2 为什么引入 Kafka?

大规模集群中,如果多个 Fluentd 实例同时往 ES 发数据,ES 可能因为高并发被打死。Kafka 作为消息队列起到流量削峰作用:

Fluentd × N  →  Kafka  →  Logstash  →  ES
  • 削峰填谷:吸收高峰流量,平滑发送给下游
  • 解耦:Fluentd 和 ES 之间解耦,一方出问题不影响另一方
  • 多消费者:一份日志数据可以被多个下游系统消费

4.3 Fluentd 和 Logstash 的分工

组件擅长负责
Fluentd日志高效收集和传输采集、过滤、初步处理、发送到 Kafka
Logstash复杂数据处理和转换消费 Kafka、字段提取、格式转换、输出到 ES

五、Fluentd 配置详解

5.1 配置三段式结构

Fluentd 配置分为三部分:source(采集)filter(过滤)match(输出)

5.2 日志源配置

<source>
  @id fluentd-containers.log
  @type tail                                    # 持续读取文件新增内容
  path /var/log/containers/*.log               # 容器 stdout 日志路径
  pos_file /var/log/es-containers.log.pos     # 断点续读位置文件
  tag raw.kubernetes.*                         # 打标签
  read_from_head true                           # 首次从头读
  <parse>
    @type multi_format                          # 支持多种解析格式
    <pattern>
      format json                               # 先尝试 JSON 格式
    </pattern>
    <pattern>
      format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
    </pattern>
  </parse>
</source>

参数说明

  • @type tail:类似于 Linux 的 tail -f,持续从文件获取新增日志
  • pos_file:记录读取到哪个位置,重启后断点续传,避免日志丢失或重复
  • multi_format:先用 JSON 解析,失败则用正则表达式

5.3 过滤器配置

# 拼接多行日志(换行符合并)
<filter kubernetes.**>
  @type concat
  key message
  multiline_end_regexp /\n$/
  separator ""
</filter>
 
# 添加 Kubernetes 元数据
<filter kubernetes.**>
  @type kubernetes_metadata
</filter>
 
# 删除无用字段
<filter kubernetes.**>
  @type record_transformer
  remove_keys $.docker.container_id,$.kubernetes.container_image_id,...
end
 
# 只采集 logging=true 的 Pod
<filter kubernetes.**>
  @type grep
  <regexp>
    key $.kubernetes.labels.logging
    pattern ^true$
  </regexp>
</filter>

5.4 输出配置

# 输出到 Kafka
<match **>
  @type kafka2
  brokers kafka-0.kafka-headless.logging.svc.cluster.local:9092
  topic_key k8slog
  default_topic messages
  <buffer k8slog>
    @type file
    path /var/log/td-agent/buffer/td
    flush_interval 3s
  </buffer>
  <format>
    @type json
  </format>
  required_acks -1
  compression_codec gzip
</match>

六、部署 EFK 架构

6.1 部署架构图

┌─────────────────────────────────────────────────────┐
│               Kubernetes 集群                        │
│                                                     │
│  ┌──────────────────────────────────┐               │
│  │      logging 命名空间              │               │
│  │                                   │               │
│  │  ES集群(Master/Data/Client)     │               │
│  │  Kibana                           │               │
│  │  Logstash                         │               │
│  │  Kafka + Zookeeper                │               │
│  └──────────────────────────────────┘               │
│                                                     │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐           │
│  │Node节点  │  │Node节点  │  │ Node    │           │
│  │Fluentd  │  │Fluentd  │  │Fluentd  │ ← DaemonSet│
│  └─────────┘  └─────────┘  └─────────┘           │
└─────────────────────────────────────────────────────┘

6.2 部署顺序

# 1. 创建日志命名空间
kubectl create ns logging
 
# 2. 安装 NFS StorageClass(如需持久化)
#    部署 nfs-subdir-external-provisioner
 
# 3. 创建 ES 证书和账号密码
kubectl create secret -n logging generic elastic-certs \
  --from-file=elastic-certificates.p12
kubectl create secret -n logging generic elastic-auth \
  --from-literal=username=elastic --from-literal=password=egon666
 
# 4. 安装 Elasticsearch(三种角色)
helm install es-master -f values-master.yaml --namespace logging .
helm install es-data -f values-data.yaml --namespace logging .
helm install es-client -f values-client.yaml --namespace logging .
 
# 5. 安装 Kibana
helm install kibana -f values-kibana.yaml --namespace logging .
 
# 6. 安装 Fluentd DaemonSet
kubectl apply -f fluentd-configmap.yaml -n logging
kubectl apply -f fluentd-daemonset.yaml -n logging
 
# 7. 给节点打标签
kubectl label nodes --all beta.kubernetes.io/fluentd-ds-ready=true
 
# 8. 安装 Kafka(如需)
helm install kafka -f values-kafka.yaml --namespace logging .
 
# 9. 安装 Logstash(如用 Kafka)
helm install logstash -f values-logstash.yaml --namespace logging .

6.3 ES 三种角色配置要点

# Master 节点
roles:
  master: 'true'
  ingest: 'false'
  data: 'false'
persistence:
  enabled: true
  volumeClaimTemplate:
    storageClassName: nfs-client
    resources:
      requests:
        storage: 5Gi
 
# Data 节点
roles:
  master: 'false'
  ingest: 'true'
  data: 'true'
persistence:
  enabled: true
  volumeClaimTemplate:
    storageClassName: nfs-client
    resources:
      requests:
        storage: 10Gi
 
# Client 节点
roles:
  master: 'false'
  ingest: 'false'
  data: 'false'
persistence:
  enabled: false   # Client 不存储数据

6.4 让业务 Pod 的日志被采集

核心:给 Pod 打标签 logging=true

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    logging: "true"      # 让 Fluentd 采集
    logIndex: "myapp"   # 决定 ES 索引名(可选)
spec:
  containers:
  - name: myapp
    image: myapp:latest

6.5 Kibana 配置

访问 http://节点IP:30601,用 elastic / egon666 登录。

创建索引模式:

Stack Management → 索引模式 → 创建索引模式
索引名称:logstash-k8s-*
时间字段:@timestamp

七、部署后验证与问题排查

7.1 验证清单

# 1. 查看所有 Pod 状态
kubectl get pods -n logging -o wide
 
# 2. 验证 ES 集群健康
curl -u elastic:egon666 http://elasticsearch-client:9200/_cluster/health
 
# 3. 查看所有索引
curl -u elastic:egon666 http://elasticsearch-client:9200/_cat/indices?v

7.2 常见问题

问题原因解决
Fluentd 采集不到日志Pod 没打 logging=true 标签kubectl label pod xxx logging=true
Kibana 无数据索引名不匹配检查是 k8s-* 还是 logstash-k8s-*
ES 集群变黄副本分片未分配等自动分配或检查资源
Pod 无法调度节点没打 fluentd 标签kubectl label node xxx beta.kubernetes.io/fluentd-ds-ready=true

八、面试高频问答

Q1:为什么要引入日志系统?

报警很多时候无法反馈问题的根因,分析根因还需要依赖日志。多服务环境下逐一查日志繁琐,且 Pod 删除后日志会消失,需要统一采集到后端持久化保存。

Q2:EFK 各组件作用是什么?

  • Elasticsearch:分布式日志存储数据库,用倒排索引实现快速检索
  • Fluentd:日志采集器,DaemonSet 部署在每节点,采集 stdout 日志
  • Logstash:日志处理器,从 Kafka 消费,做复杂数据转换后存 ES
  • Kafka:消息队列,削峰缓冲,保护 ES 不被打挂
  • Kibana:日志可视化平台,连接 ES 查询展示

Q3:Fluentd 为什么用 DaemonSet 而不是 Sidecar?

DaemonSet 对应用零侵入,资源效率高(一个节点一个 Agent),运维简单。Sidecar 用于应用日志写到文件、不能改 stdout 的特殊场景。

Q4:完整日志流向是什么?

Pod stdout → 宿主机文件 → Fluentd 采集(按 logging=true 过滤)→ Kafka 缓冲 → Logstash 处理(按 logIndex 决定索引名)→ ES 存储 → Kibana 展示。

Q5:Pod 日志采集不到怎么排查?

  1. 检查 Pod 是否打了 logging=true 标签
  2. 检查 Pod 所在节点是否打了 fluentd-ds-ready=true 标签
  3. 查看 Fluentd Pod 是否 Running
  4. 查看 Fluentd 日志是否有错误
  5. 检查 ES/Kafka 是否正常
  6. 确认 Kibana 索引名是否匹配

Q6:为什么引入 Kafka?

大规模集群中,多个 Fluentd 同时往 ES 发数据会打死 ES。Kafka 作为消息队列起削峰作用:吸收高峰流量、平滑发送给下游、Fluentd 和 ES 解耦。

Q7:Fluentd 和 Logstash 区别?

  • Fluentd:擅长日志高效收集和传输,轻量级,适合云原生环境
  • Logstash:擅长复杂数据处理和转换,有丰富的过滤插件

Q8:ES 集群变红/变黄怎么处理?

  • Yellow:副本分片未分配,通常是资源不足,等自动分配或增加节点
  • Red:主分片丢失,检查磁盘、网络或从备份恢复

九、生产优化建议

优化项说明
ES 分片规划每个分片数据量建议 30-50GB,避免太小或太大
副本数量生产环境至少 1 副本,高可用场景 2+ 副本
定期清理旧索引用 Curator 或定时脚本删除超期数据
冷热分离热数据用 SSD,冷数据用 HDD,降低成本
监控告警Prometheus 监控 ES 集群健康状态

一句话总结:K8s 日志系统的核心是理解 stdout → DaemonSet Fluentd → Kafka → Logstash → Elasticsearch → Kibana 这条数据流,以及两道关卡(logging=true 标签过滤 + logIndex 索引名控制)的设计思想。

分享
0%