无标题
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。
缺点:
- 空间浪费一倍 — 日志写文件一次,sidecar 重定向到 stdout 又写一次
- 改造 Pod — 每个需要日志收集的 Pod 都要注入 sidecar
升级版:把 Fluentd 直接作为 sidecar 跑在 Pod 内部,不经过 stdout,直接从容器内文件读取后发送到后端。
- 优点:更灵活,不浪费存储空间
- 缺点:
kubectl logs无法使用,配置复杂
方案三:应用直接推送
应用代码层面直接集成 SDK(比如 Logback、Fluentd Client),直接把日志推送到日志后端。
优点:架构最精简,性能损耗最小 缺点:需要改动源代码,超出 K8s 范围
三种方案对比
| 方案 | 部署形态 | 日志来源 | 侵入性 |
|---|---|---|---|
| DaemonSet | 每节点一个 Agent | stdout/stderr | 零侵入 |
| Sidecar 读文件 | 每个 Pod 一个 Sidecar | 容器内文件 | 需要改 Pod |
| Sidecar 直推 | 每个 Pod 一个 Fluentd | 容器内文件 | 需要改 Pod |
| 应用直推 | 应用内置 | 无限制 | 改源代码 |
三、EFK 架构详解
3.1 什么是 Elasticsearch?
Elasticsearch 是一个分布式 RESTful 风格的搜索和数据分析引擎,主要用于存储、检索和分析海量日志数据。
核心特点:
- 分布式:数据分片存储,支持横向扩展
- 倒排索引:让"在一亿条日志里找某条错误"变得毫秒级
- 实时性:数据写入后可近实时被检索到
- RESTful API:通过 HTTP 请求操作数据
3.2 Elasticsearch 核心概念
| 关系型数据库 | Elasticsearch | 说明 |
|---|---|---|
| Database | Index(索引) | 存储一类文档的地方 |
| Row | Document(文档) | 存储的基本单元,一条日志就是一篇文档 |
| Column | Field(字段) | 文档中的键值对 |
| Schema | Mapping(映射) | 定义字段的类型和属性 |
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:latest6.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?v7.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 日志采集不到怎么排查?
- 检查 Pod 是否打了
logging=true标签 - 检查 Pod 所在节点是否打了
fluentd-ds-ready=true标签 - 查看 Fluentd Pod 是否 Running
- 查看 Fluentd 日志是否有错误
- 检查 ES/Kafka 是否正常
- 确认 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索引名控制)的设计思想。