K8s日志分为容器标准输出/错误和容器内文件日志两类。日志收集方案需解决:采集→聚合→存储→查询→告警的完整链路。
┌────────────── 日志收集架构 ──────────────┐
│ │
│ ┌──── Pod ────┐ │
│ │ Container │──→ stdout/stderr │
│ │ /var/log/* │──→ 文件日志 │
│ └──────┬──────┘ │
│ │ │
│ 方案A:Node级DaemonSet采集 │
│ ┌──────▼──────┐ │
│ │ Fluentd/ │ 每节点一个,采集 │
│ │ Fluent Bit │ /var/log/containers/* │
│ └──────┬──────┘ │
│ │ │
│ 方案B:Sidecar采集 │
│ ┌──────▼──────┐ │
│ │ Log Sidecar │ 每Pod一个,读文件日志 │
│ └──────┬──────┘ │
│ │ │
│ ┌──────▼──────────────────────────────┐ │
│ │ 日志后端 │ │
│ │ Elasticsearch / Loki / CloudWatch │ │
│ └──────┬──────────────────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ Grafana │ 可视化查询 │
│ │ Kibana │ │
│ └─────────────┘ │
└────────────────────────────────────────────┘
# fluent-bit-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
labels:
app: fluent-bit
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
volumeMounts:
- name: varlog
mountPath: /var/log
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc/
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: config
configMap:
name: fluent-bit-config
---
# fluent-bit-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Mem_Buf_Limit 5MB
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
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude Off
[OUTPUT]
Name loki
Match *
Host loki.logging.svc.cluster.local
Port 3100
Labels job=fluent-bit, namespace=k8s
Auto_Kubernetes_Labels on
parsers.conf: |
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
# ✅ 验证通过
kubectl apply -f fluent-bit-daemonset.yaml
kubectl get pods -n logging -l app=fluent-bit
# NAME READY STATUS RESTARTS AGE
# fluent-bit-xxxxx 1/1 Running 0 30s
# 安装Loki Stack(轻量级,不依赖Elasticsearch)
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
--namespace logging \
--create-namespace \
--set loki.persistence.enabled=true \
--set loki.persistence.size=10Gi \
--set promtail.enabled=true
# ✅ 验证通过
kubectl get pods -n logging
# NAME READY STATUS RESTARTS AGE
# loki-0 1/1 Running 0 2m
# loki-promtail-xxxxx 1/1 Running 0 2m
# LogQL查询示例
# 查看某namespace所有日志
{namespace="default"}
# 过滤特定应用
{namespace="default", app="my-app"} |= "error"
# 统计错误日志数
sum(count_over_time({namespace="default"} |= "error" [5m]))
# 提取JSON字段
{app="my-app"} | logfmt | status >= 500
# EFK = Elasticsearch + Fluentd + Kibana
# 适合大规模日志场景
# 安装ECK(Elastic Cloud on Kubernetes)
kubectl apply -f https://download.elastic.co/downloads/eck/2.9.0/crds.yaml
kubectl apply -f https://download.elastic.co/downloads/eck/2.9.0/operator.yaml
# 创建Elasticsearch集群
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: quickstart
spec:
version: 8.12.0
nodeSets:
- name: default
count: 3
config:
node.store.allow_mmap: false
resources:
requests:
memory: 4Gi
limits:
memory: 4Gi
---
# 创建Kibana
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: quickstart
spec:
version: 8.12.0
count: 1
elasticsearchRef:
name: quickstart
# ✅ 验证通过
kubectl get elasticsearch
# NAME HEALTH NODES VERSION PHASE AGE
# quickstart green 3 8.12.0 Ready 5m
# ❌ 非结构化日志——难以搜索和分析
print(f"User {user_id} logged in from {ip}")
# ✅ 结构化日志——机器可读
import json
import logging
class JSONFormatter(logging.Formatter):
def format(self, record):
log_data = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"service": "my-app",
"version": "1.0.0",
}
if hasattr(record, 'custom_fields'):
log_data.update(record.custom_fields)
return json.dumps(log_data)
# 使用
logger = logging.getLogger(__name__)
logger.info("User login", extra={
"custom_fields": {
"user_id": user_id,
"ip": ip,
"trace_id": trace_id
}
})
# 输出:
# {"timestamp":"2026-01-01T00:00:00","level":"INFO","message":"User login","service":"my-app","user_id":"123","ip":"1.2.3.4","trace_id":"abc123"}
# 日志级别规范
# ERROR → 需要立即处理(触发告警)
# WARN → 需要关注(可能发展为ERROR)
# INFO → 关键业务事件(用户登录、订单创建等)
# DEBUG → 调试信息(生产环境默认关闭)
# Loki告警规则
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: log-alerts
namespace: monitoring
spec:
groups:
- name: log-alerts
rules:
- alert: HighErrorRate
expr: |
sum(count_over_time({namespace="default"} |= "ERROR" [5m]))
/
sum(count_over_time({namespace="default"} [5m]))
> 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"
# 检查容器日志轮转配置
cat /etc/docker/daemon.json
# {
# "log-driver": "json-file",
# "log-opts": {
# "max-size": "10m", # 单文件最大
# "max-file": "3" # 最多保留3个
# }
# }
# 如果应用日志量大,调整轮转参数
# 或使用Sidecar直接采集文件日志
# 检查Fluent Bit状态
kubectl logs -n logging daemonset/fluent-bit
# 查看是否有权限错误或连接失败
# 检查节点日志目录
ls /var/log/containers/ | head -5
# 应该有大量日志文件
# 检查Loki连通性
kubectl exec -n logging fluent-bit-xxx -- wget -qO- loki:3100/ready
下一课预告:第15课深入告警与SLO——生产级告警体系设计。
14-日志收集补充要点:K8s生产实践扩展
🔹 资源配额(ResourceQuota):限制命名空间总资源
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: production
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
pods: "50"
services: "10"
🔹 LimitRange:设置默认资源限制
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
spec:
limits:
- type: Container
default:
cpu: "200m"
memory: 256Mi
defaultRequest:
cpu: "100m"
memory: 128Mi
max:
cpu: "2"
memory: 4Gi
🔹 Pod优先级与抢占
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
---
spec:
preemptionPolicy: PreemptLowerPriority
🔹 优雅处理Pod中断
• PDB保证最小可用副本
• preStop钩子处理连接排空
• terminationGracePeriodSeconds充足
• 应用必须处理SIGTERM信号
🔹 生产环境Checklist
✅ 设置resources requests/limits
✅ 配置liveness/readiness探针
✅ 使用PDB保护关键服务
✅ 实现优雅关闭(SIGTERM)
✅ 配置HPA自动伸缩
✅ 使用NetworkPolicy隔离
✅ 开启RBAC最小权限
✅ 日志结构化输出
✅ 指标暴露/metrics端点
✅ 配置PVC数据备份