深色模式
K8s 日志收集方案
摘要:本文先讲清 K8s 日志落在哪、为什么容器删了日志就没了,再演示用 Fluent Bit 以 DaemonSet 方式采集并集中存储,覆盖多行堆栈合并、字段富化与日志轮转配置。
适用环境
- 可用 K8s 集群 +
kubectl - 有 Helm(便于部署 Loki / Elasticsearch)
- 存储:Loki(轻量)或 Elasticsearch(检索强)
操作步骤
一、日志到底存在哪
容器日志由容器运行时写到节点本地:
bash
ls /var/log/pods/<命名空间>_<pod名>_<uid>/<容器名>/
ls /var/log/containers/ # 软链,文件名带 命名空间_pod_容器 信息1
2
2
bash
kubectl logs <pod>
# kubelet 实际就是读上面这些文件1
2
2
注意
容器删除后这些日志文件随之消失,且节点上日志仅保留有限份数。任何需要追溯的日志都必须集中收集到集群外部。
二、三种收集模式对比
| 模式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| DaemonSet 采集(推荐) | 每节点一个采集器读 /var/log/containers | 与应用解耦,无需改 Pod | 需解析文件名获取元数据 |
| Sidecar 采集 | 每个 Pod 挂一个采集容器 | 可按应用定制 | 资源开销随 Pod 数线性增长 |
| 应用直传 | 应用自己发到日志系统 | 最直接 | 耦合业务代码,丢集群元数据 |
三、日志轮转必须配
kubelet 默认轮转策略由以下参数控制(kubeadm 通常已配置):
bash
ps aux | grep kubelet | grep -o 'container-log-max-size=[^ ]*'
# 或查看配置
sudo cat /var/lib/kubelet/config.yaml | grep -i log1
2
3
2
3
关键项:containerLogMaxSize(单文件上限,如 50Mi)、containerLogMaxFiles(保留份数,如 5)。
危险
不配轮转,一个疯狂打印日志的容器能在几小时内写满节点磁盘,导致整节点 Pod 被驱逐。这是生产最常见的「一个 Pod 拖垮一台机」场景。
四、部署 Loki(轻量方案)
bash
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install loki grafana/loki-stack -n logging --create-namespace \
--set grafana.enabled=true \
--set promtail.enabled=true
kubectl -n logging get pod1
2
3
4
5
6
2
3
4
5
6
若已有 Grafana,只装 Loki + Promtail:
bash
helm install loki grafana/loki -n logging --create-namespace1
五、用 Fluent Bit 采集(更可控)
bash
helm repo add fluent https://fluent.github.io/helm-charts
helm install fluent-bit fluent/fluent-bit -n logging --create-namespace \
--set backend.type=es \
--set backend.es.host=elasticsearch.logging.svc.cluster.local \
--set backend.es.port=92001
2
3
4
5
2
3
4
5
核心配置(ConfigMap 形式):
ini
[SERVICE]
Flush 5
Daemon Off
Log_Level info
Parsers_File parsers.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser cri
Tag kube.*
Mem_Buf_Limit 50MB
Skip_Long_Lines On
Refresh_Interval 10
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Merge_Log On
K8S-Logging.Parser On
K8S-Logging.Exclude On
[OUTPUT]
Name es
Match kube.*
Host elasticsearch.logging.svc.cluster.local
Port 9200
Index k8s-logs
Logstash_Format On1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
kubernetes 过滤器会自动把命名空间、Pod 名、容器名、labels 富化到每条日志上——这是 DaemonSet 模式能拿到元数据的关键。
六、多行堆栈合并(Java 异常必配)
ini
[PARSER]
Name multiline-java
Format regex
Regex /^(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(?<message>.*)$/
[FILTER]
Name multiline
Match kube.*
multiline.key_content log
multiline.parser java1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
不合并的话,一个异常栈会被拆成几十条独立日志,无法阅读也无法关联。
七、在 Loki 里查询(LogQL)
logql
{namespace="default", pod=~"web-.*"}
{namespace="prod"} |= "ERROR"
{namespace="prod", app="web"} |= "timeout" != "healthz"
{namespace="prod"} | json | status >= 500
sum(rate({namespace="prod"} |= "ERROR" [5m])) by (pod)1
2
3
4
5
2
3
4
5
八、采集排障
bash
kubectl -n logging get pod -o wide # 每个节点都要有一个
kubectl -n logging logs -l app=fluent-bit --tail=50
kubectl -n logging exec <pod> -- ls /var/log/containers/1
2
3
2
3
Fluent Bit 报 permission denied 多半是没挂 /var/log 卷:
yaml
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
验证
- [ ] 每个节点都有一个采集 Pod 且 Running
- [ ] 在 Loki/ES 中能按 namespace、pod 检索到日志
- [ ] 制造一条 ERROR 日志,能在集中存储里查到
- [ ] 删除 Pod 后,之前的日志仍能检索
常见坑
- 日志有延迟或丢失:
Mem_Buf_Limit太小,日志量大时采集器会丢弃。调到 50MB 以上并监控缓冲区。 - 采集 Pod 起不来:hostPath 挂载路径错误或 SELinux 限制。
- 堆栈被拆散:未配多行合并,见第六节。
- 字段没富化:Fluent Bit 缺 RBAC 权限,无法调 API 拿 Pod 元数据。
- 磁盘被日志写满:未配轮转,见上文危险提示。