K8s 系列 | 第 23 天:日志收集实战:EFK/ELK 栈在 K8s 中部署


tags:
– Kubernetes
– K8s系列
– DevOps
– EFK
– ELK
– 日志收集
– 可观测性


K8s 系列 | 第 23 天:日志收集实战:EFK/ELK 栈在 K8s 中部署

第 23/30 天 | 本文是 Kubernetes 系列的第 23 篇,带你从零搭建生产级的 K8s 日志收集系统。

一、引言

在 Kubernetes 生产环境中,容器化应用会产生海量日志,而 Pod 的临时性意味着日志会随容器销毁而丢失。如何集中采集、存储和检索这些日志,是每个运维团队必须解决的难题。

目前业界最主流的方案是 EFK(Elasticsearch + Filebeat/Fluentd + Kibana)ELK(Elasticsearch + Logstash + Kibana)。在 K8s 生态中,Fluentd 和 Filebeat 通常以 DaemonSet 方式部署于每个 Node,自动采集所有 Pod 的日志。

本文将手把手带你完成 EFK 栈在 K8s 中的完整部署,涵盖架构设计、配置详解、常见问题排查。


二、核心概念

2.1 日志采集架构

┌─────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                │
│                                                      │
│  ┌──────┐  ┌──────┐  ┌──────┐                       │
│  │ Pod  │  │ Pod  │  │ Pod  │  ... (应用日志)        │
│  └──┬───┘  └──┬───┘  └──┬───┘                       │
│     │         │         │                            │
│     ▼         ▼         ▼                            │
│  ┌─────────────────────────────────────┐             │
│  │  Filebeat DaemonSet (每个 Node 一个) │             │
│  │  采集 /var/log/containers/*.log     │             │
│  └──────────────┬──────────────────────┘             │
│                 │                                    │
│                 ▼                                    │
│          ┌──────────────┐                            │
│          │ Elasticsearch│ ← 存储 + 索引              │
│          └──────┬───────┘                            │
│                 │                                    │
│                 ▼                                    │
│          ┌──────────────┐                            │
│          │    Kibana    │ ← 可视化查询               │
│          └──────────────┘                            │
└─────────────────────────────────────────────────────┘

2.2 为什么选择 Filebeat 而非 Logstash?

对比项 Filebeat Logstash
资源占用 极低(Go 编写) 较高(JVM 运行)
部署方式 二进制/容器 需要 JVM 环境
吞吐量 高(轻量级) 可通过 pipeline 扩展
过滤能力 基础(需配置 module) 强大(丰富 filter 插件)
适用场景 日志采集端 日志聚合处理端

最佳实践:Node 端用 Filebeat 采集,中心端用 Logstash 做复杂数据处理(如有需要)。


三、实战步骤:在 K8s 中部署 EFK 栈

3.1 准备环境

确保集群满足以下条件:
– K8s 1.20+ 集群
– 每个 Node 有足够的磁盘空间(Elasticsearch 建议至少 10GB 可用)
– 已安装 kubectl 并配置好集群

3.2 部署 Elasticsearch

首先创建 Namespace 和 Elasticsearch 的 StatefulSet:

---
apiVersion: v1
kind: Namespace
metadata:
  name: logging
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: logging
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  ports:
    - port: 9200
      name: rest
    - port: 9300
      name: inter-node
  clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
  namespace: logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2
          env:
            - name: discovery.type
              value: single-node
            - name: ES_JAVA_OPTS
              value: "-Xms512m -Xmx512m"
            - name: xpack.security.enabled
              value: "false"
          ports:
            - containerPort: 9200
            - containerPort: 9300
          resources:
            requests:
              memory: "1Gi"
              cpu: "500m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
          volumeMounts:
            - name: data
              mountPath: /usr/share/elasticsearch/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

注意:生产环境建议使用 3 节点集群,并启用 xpack 安全认证。

3.3 部署 Kibana

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: logging
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
        - name: kibana
          image: docker.elastic.co/kibana/kibana:8.10.2
          env:
            - name: ELASTICSEARCH_HOSTS
              value: "http://elasticsearch:9200"
          ports:
            - containerPort: 5601
---
apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: logging
  labels:
    app: kibana
spec:
  type: ClusterIP
  ports:
    - port: 5601
      targetPort: 5601
  selector:
    app: kibana

3.4 部署 Filebeat(DaemonSet)

Filebeat 以 DaemonSet 方式部署,确保每个节点自动采集所有容器日志:

apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
  namespace: logging
  labels:
    app: filebeat
data:
  filebeat.yml: |
    filebeat.autodiscover:
      providers:
        - type: kubernetes
          node: ${NODE_NAME}
          hints.enabled: true
          hints.default_config:
            type: container
            paths:
              - /var/log/containers/*${data.kubernetes.container.id}.log

    processors:
      - add_kubernetes_metadata:
          host: ${NODE_NAME}
          matchers:
            - logs_path:
                logs_path: "/var/log/containers/"

    output.elasticsearch:
      hosts: '${ELASTICSEARCH_HOSTS:elasticsearch:9200}'
      index: "filebeat-%{[agent.version]}-%{+yyyy.MM.dd}"
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: filebeat
  namespace: logging
  labels:
    app: filebeat
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
    metadata:
      labels:
        app: filebeat
    spec:
      serviceAccountName: filebeat
      containers:
        - name: filebeat
          image: docker.elastic.co/beats/filebeat:8.10.2
          args: ["-e", "-strict.perms=false"]
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: ELASTICSEARCH_HOSTS
              value: "elasticsearch:9200"
          volumeMounts:
            - name: config
              mountPath: /usr/share/filebeat/filebeat.yml
              subPath: filebeat.yml
            - name: varlog
              mountPath: /var/log/containers
              readOnly: true
            - name: varlibdocker
              mountPath: /var/lib/docker/containers
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: filebeat-config
        - name: varlog
          hostPath:
            path: /var/log/containers
        - name: varlibdocker
          hostPath:
            path: /var/lib/docker/containers
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: filebeat
  namespace: logging
  labels:
    app: filebeat
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: filebeat
  labels:
    app: filebeat
rules:
  - apiGroups: [""]
    resources:
      - nodes
      - namespaces
      - pods
      - services
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: filebeat
  labels:
    app: filebeat
subjects:
  - kind: ServiceAccount
    name: filebeat
    namespace: logging
roleRef:
  kind: ClusterRole
  name: filebeat
  apiGroup: rbac.authorization.k8s.io

3.5 一键部署所有组件

# 创建日志收集 Namespace 并部署所有组件
kubectl apply -f elasticsearch.yaml
kubectl apply -f kibana.yaml
kubectl apply -f filebeat.yaml

# 验证部署状态
kubectl -n logging get pods -w

# 等待所有 Pod 为 Running 状态后,检查 Elasticsearch 健康状态
kubectl -n logging port-forward svc/elasticsearch 9200:9200 &
curl http://localhost:9200/_cluster/health?pretty

3.6 访问 Kibana

# 端口转发到本地访问 Kibana
kubectl -n logging port-forward svc/kibana 5601:5601 &

浏览器打开 http://localhost:5601,进入 Stack Management → Index Patterns,创建 filebeat-* 索引模式,即可在 Discover 页面查看所有容器日志。


四、常见问题

4.1 Elasticsearch Pod 一直 CrashLoopBackOff

原因vm.max_map_count 过低导致 Elasticsearch 无法启动。

解决

# 在每个 Node 上执行
sudo sysctl -w vm.max_map_count=262144

# 永久生效
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

4.2 Filebeat 采集不到日志

排查步骤
1. 检查 Filebeat Pod 日志:kubectl -n logging logs ds/filebeat
2. 确认 /var/log/containers/ 目录是否存在容器日志文件
3. 检查 RBAC 权限是否正确绑定
4. 验证 Filebeat 配置中的 output.elasticsearch.hosts 是否正确

4.3 Kibana 无法连接 Elasticsearch

检查

# 测试 Elasticsearch 是否可达
kubectl -n logging exec -it deploy/kibana -- curl -s http://elasticsearch:9200

# 检查 Kibana 环境变量
kubectl -n logging describe pod $(kubectl -n logging get pod -l app=kibana -o name) | grep ELASTICSEARCH

4.4 日志存储占用空间过大

解决方案
– 配置 ILM(索引生命周期管理)自动删除过期索引
– 通过 Logstash 或 Filebeat processors 过滤不必要的日志
– 采用 Curator 定时任务清理旧索引

# 使用 Curator 删除 7 天前的索引(示例)
curator --host elasticsearch delete_indices --filter_list 
  '{"filtertype":"age","source":"name","timestring":"%Y.%m.%d","unit":"days","unit_count":7}'

五、总结

本文详细介绍了 EFK 栈在 Kubernetes 中的完整部署流程:

组件 作用 部署方式
Elasticsearch 日志存储与搜索引擎 StatefulSet(3 副本)
Filebeat 日志采集与传输 DaemonSet(每个 Node 一个)
Kibana 日志可视化与查询 Deployment + Service

核心要点回顾:
1. Filebeat 以 DaemonSet 方式部署,自动采集 /var/log/containers/ 下的所有容器日志
2. 通过 add_kubernetes_metadata 处理器自动注入 Pod 元数据(Namespace、Pod 名、Label 等)
3. Elasticsearch 使用 StatefulSet 保证数据持久化,生产环境建议 3 节点集群
4. Kibana 通过 ClusterIP Service 对内暴露,生产环境应通过 Ingress 对外暴露

生产环境建议:Elasticsearch 节点应使用独立的高性能 SSD 存储,开启 xpack 安全认证,并配置 ILM 策略管理索引生命周期。Filebeat 可根据日志量调整 harvester_buffer_sizebackoff 参数。


下期预告

第 24 天:RBAC 权限控制:ServiceAccount、Role、ClusterRole 深度解析

我们将深入 K8s 的权限控制体系,从 ServiceAccount 创建到 Role 和 ClusterRole 的绑定策略,再到生产环境中的最小权限原则实践,彻底搞懂 K8s 的 RBAC 机制。


📚 系列目录:
第 1 天:Kubernetes 核心概念与架构全景解析
第 2 天:手把手搭建 K8s 集群(kubeadm 实战)
第 3 天:Pod 详解与生命周期管理
– … 更多内容请访问博客首页
第 22 天:监控体系搭建:Prometheus + Grafana + Kube-state-metrics
→ 第 23 天:日志收集实战:EFK/ELK 栈在 K8s 中部署 ← 本文
第 24 天:RBAC 权限控制(即将发布)

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容