12、容器日志搜集

12、容器日志搜集

1. K8s需要收集哪些日志

  1. 系统和K8s组件日志:
    • /var/log/messages:这是Linux系统中存储一般消息和错误信息的日志文件。
    • /var/log/kube-xxx.log:这些是Kubernetes 组件的日志文件。
  2. 业务应用程序日志:
    • “云原生——控制台日志”:在云原生环境中,应用程序通常以容器的形式运行。控制台日志是指从应用程序的控制台输出捕获的日志信息。
    • “非云原生——容器内日志文件”:对于非云原生的应用程序,它们可能将日志写入容器内的特定日志文件。

传统架构(ELK)中 Logstash 太“重”可能不做任何工作,500兆内存已经没了,而且维护起来相当麻烦。下面将会对k8s中采用的日志收集技术栈进行讲解。

image-20240506165933972

2. EFK架构

2.1 架构说明

img

Kubernetes官方提出了EFK(Elasticsearch + Fluentd + Kibana)的日志收集解决方案,相对于ELK中的Logstash,Fluentd采用“一锅端”的形式,可以直接将某些日志文件中的内容存储至Elasticsearch,然后通过Kibana进行展示。

Tips:Fluentd只能收集控制台日志(程序直接输出到控制台的日志),不能收集非控制台日志。

2.2 部署EFK

部署 Elasticsearch+Fluentd+Kibana,项目地址:git clone https://github.com/dotbalo/k8s.git ,将该目录下载下来(使用目录中的efk-7.10.2)。

提前下载镜像推送到本地Harbor仓库

docker pull alpine:3.6
docker pull quay.io/fluentd_elasticsearch/elasticsearch:v7.10.2
docker pull registry.cn-beijing.aliyuncs.com/dotbalo/kibana-oss:7.10.2

创建 EFK 所用的命名空间

kubectl create -f create-logging-namespace.yaml

创建 Elasticsearch 集群

#进入efk-7.10.2目录提前修改本地镜像地址
[root@k8s-master01 efk-7.10.2]# grep -n "image:" es-statefulset.yaml
70:        - image: 10.0.0.138:5000/logs/elasticsearch:v7.10.2
115:        - image: 10.0.0.138:5000/logs/alpine:3.6

#创建Service
kubectl create -f es-service.yaml

#创建Statefulset
kubectl create -f es-statefulset.yaml

创建 Kibana

#提前修改本地镜像地址
[root@k8s-master01 efk-7.10.2]# grep -n "image:" kibana-deployment.yaml
24:          image: 10.0.0.138:5000/logs/kibana-oss:7.10.2

#创建Service
kubectl create -f kibana-service.yaml

#创建Deployment
kubectl create -f kibana-deployment.yaml

由于在 Kubernetes 集群中,我们可能并不需要对所有的机器都采集日志,所以可以更改Fluentd 的部署文件如下,添加一个 NodeSelector,只部署至需要采集的主机即可。

[root@k8s-master01 efk-7.10.2]# grep -n "image:" fluentd-es-ds.yaml
72:        image: 10.0.0.138:5000/logs/fluentd:v3.1.0

[root@k8s-master01 efk-7.10.2]# grep -n -A1 "nodeSelector" fluentd-es-ds.yaml
105:      nodeSelector:
106-        fluentd: "true"

之后给需要采集日志的节点打上一个标签

kubectl label node k8s-master01 fluentd=true
kubectl label node k8s-master02 fluentd=true
kubectl label node k8s-master03 fluentd=true

查询打上该label的节点

image-20240506182003546

创建 Fluentd

kubectl create -f fluentd-es-ds.yaml -f fluentd-es-configmap.yaml

2.3 Kibana使用

查看全部Pod已经running

image-20240506190650395

接下来查看 Kibana 暴露的端口号,访问 Kibana。访问格式:节点IP:暴露的端口号/kibana

image-20240506190750456

16b21b55-6801-447e-a3cc-e20fd87fae78

点击 Explore on my own,之后再点击 Visualize:

image-20240506192042865

之后点击 Add your data → Create index pattern:

image-20240506192205982

762fce37-c9b5-4dc8-8455-4019e689f272

7cca86a7-b592-46c8-a63d-cc2bf534291b

image-20240506191611547

3. Filebeat收集自定义文件日志

3.1 架构说明

img

EFK也有诸多限制,比如Fluentd只能收集控制台日志(程序直接输出到控制台的日志),不能收集非控制台日志,所以很难满足生产环境的需求。因为大部分情况下,没有遵循云原生理念开发的程序,往往都会输出很多日志文件,这些容器内的日志无法采集,除非在每个Pod内添加一个 Sidecar,将日志文件的内容进行tail -f转成控制台日志,但这也是非常麻烦的。

另一个问题是,大部分公司内都有很成熟的 ELK 平台,如果再搭建一个 EFK 平台,属于重复造轮子。当然,用来存储日志的 Elasticsearch 集群是不建议搭建在Kubernetes集群中的,因为会非常浪费 Kubernetes 集群资源,所以大部分情况下通过Fluentd采集日志输出到外部的Elasticsearch集群中

Fluentd功能有限,Logstash太重,所以就需要一个中和的工具进行收集,此时就可以采用一个比较轻量级的收集工具:Filebeat

了解Filebeat

在早期的 ELK 架构中,日志收集均以 Logstash 为主,Logstash 负责收集和解析日志,它对内存、CPU、IO 资源的消耗比较高,但是 Filebeat 所占系统的 CPU 和内存几乎可以忽略不计。

由于 Filebeat 本身是比较轻量级的日志采集工具,因此 Filebeat 经常被用于以 Sidecar 的形式配置在 Pod 中,用来采集容器内程序输出的自定义日志文件。当然,Filebeat 同样可以采用 DaemonSet 的形式部署在 Kubernetes 集群中,用于采集系统日志和程序控制台输出的日志。

具体实现

Filebeat 可以将其和应用程序部署至一个 Pod 中,通过 Volume 进行日志文件的共享,之后 Filebeat 即可采集里面的数据,并推送至日志平台。

为了减轻 Elasticsearch 的压力,引入 Kafka 消息队列来缓存日志数据,之后通过 Logstash 简单处理一下日志,最后再输出到 Elasticsearch 集群。

3.2 提前下载镜像推送本地仓库

在之前 clone 下来的项目文件进入到 filebeat 目录。

[root@k8s-master01 filebeat]# pwd
/root/efk-7.10.2/filebeat
[root@k8s-master01 filebeat]# ll
total 28
-rw-r--r-- 1 root root 2373 Aug 14 22:27 app-filebeat.yaml
-rw-r--r-- 1 root root  992 Aug 14 22:27 app.yaml
-rw-r--r-- 1 root root  530 Aug 14 22:27 es-svc.yaml
-rw-r--r-- 1 root root  491 Aug 14 22:27 filebeat-cm.yaml
drwxr-xr-x 5 root root  141 Aug 14 22:27 kafka
-rw-r--r-- 1 root root 1087 Aug 14 22:27 logstash-cm.yaml
-rw-r--r-- 1 root root  181 Aug 14 22:27 logstash-service.yaml
-rw-r--r-- 1 root root  930 Aug 14 22:27 logstash.yaml
drwxr-xr-x 4 root root  128 Aug 15 15:30 zookeeper

提前下载 Zookeeper、Kafka、logstash、filebeat 镜像到本地,推送到远程仓库。

#Zookeeper相关
docker pull bitnami/zookeeper:3.7.0-debian-10-r56
docker pull bitnami/bitnami-shell:10-debian-10-r97
#Kafka相关
docker pull bitnami/kafka:2.8.0-debian-10-r30
docker pull bitnami/kubectl:1.19.11-debian-10-r14
docker pull bitnami/bitnami-shell:10-debian-10-r98
docker pull bitnami/kafka:2.8.0-debian-10-r29
docker pull bitnami/kafka-exporter:1.3.1-debian-10-r14
docker pull bitnami/jmx-exporter:0.15.0-debian-10-r121
#logstash相关
docker pull registry.cn-beijing.aliyuncs.com/dotbalo/logstash:7.10.1
#filebeat相关
docker pull registry.cn-beijing.aliyuncs.com/dotbalo/filebeat:7.10.2

3.3 部署Kafka集群

Kafka依赖Zookeeper,所以需要先部署Zookeeper,修改values.yaml中的镜像和副本。

[root@k8s-master01 filebeat]# grep -A 2 "10.0.0.138" zookeeper/values.yaml
  registry: 10.0.0.138:5000 #xxx.com
  repository: logs/bitnami/zookeeper  # bitnami/zookeeper
  tag: 3.7.0
--
    registry: 10.0.0.138:5000
    repository: logs/bitnami/bitnami-shell
    tag: 10-debian-10-r97
    
[root@k8s-master01 filebeat]# grep "replicaCount:" zookeeper/values.yaml
replicaCount: 3

安装zookeeper到logging命名空间下

helm install -n logging zookeeper zookeeper/

查看Pod情况和Service信息

image-20240506224309544

部署Kafka,修改values.yaml中的镜像和副本。

[root@k8s-master01 filebeat]# grep -A 2 "10.0.0.138" kafka/values.yaml
  registry: 10.0.0.138:5000
  repository: logs/bitnami/kafka
  tag: 2.8.0-debian-10-r30
--
      registry: 10.0.0.138:5000
      repository: logs/bitnami/kubectl
      tag: 1.19.11-debian-10-r14
--
    registry: 10.0.0.138:5000
    repository: logs/bitnami/bitnami-shell
    tag: 10-debian-10-r98
--
    registry: 10.0.0.138:5000
    repository: logs/bitnami/kafka
    tag: 2.8.0-debian-10-r29
--
      registry: 10.0.0.138:5000
      repository: logs/bitnami/kafka-exporter
      tag: 1.3.1-debian-10-r14
--
      registry: 10.0.0.138:5000
      repository: logs/bitnami/jmx-exporter
      tag: 0.15.0-debian-10-r121
[root@k8s-master01 filebeat]# grep "replicaCount:" kafka/values.yaml
replicaCount: 3

安装kafka到logging命名空间下

helm install -n logging kafka kafka/

查看 Pod 情况和 Service 信息

image-20240506230433481

3.4 部署Logstash服务

待 Kakfa、Zookeeper 的 Pod 都正常后,需先修改镜像地址,然后创建 Logstash 服务到 logging 命名空间下。

#修改镜像地址为本地仓库
[root@k8s-master01 filebeat]# grep "image" logstash.yaml
        image: 10.0.0.190/yinjay/logstash:7.10.1

#部署Logstash服务
kubectl create -f logstash-service.yaml -f logstash-cm.yaml -f logstash.yaml -n logging

需要注意 logstash-cm.yaml 文件中的一些配置:

  • input:数据来源,本次示例配置的是 Kakfa;
  • input.kafka.bootstrap_servers:Kafka 地址,由于是安装在集群内部的,可以直接使用Kafka 集群的 Service 接口,如果是外部地址,按需配置即可;
  • input.kafka.topics:Kafka 的 topic,需要和 Filebeat 输出的 topic 一致;
  • input.kafka.type:定义一个 type,可以用于 logstash 输出至不同的 Elasticsearch 集群;
  • output:数据输出至哪里,本次示例输出至 Elasticsearch 集群,在里面配置了一个判断语句,当 type 为 filebeat-sidecar 时,将会输出至 Elasticsearch 集群,并且 index 为 filebeat-xxx。

查看Pod情况和Service信息

#修改镜像地址为本地仓库
[root@k8s-master01 filebeat]# grep "image" logstash.yaml
        image: 10.0.0.138:5000/logs/logstash:7.10.1
#部署Logstash服务
kubectl create -f logstash-service.yaml -f logstash-cm.yaml -f logstash.yaml -n logging

需要注意 logstash-cm.yaml 文件中的一些配置:

  • input:数据来源,本次示例配置的是 Kakfa;
  • input.kafka.bootstrap_servers:Kafka 地址,由于是安装在集群内部的,可以直接使用Kafka 集群的 Service 接口,如果是外部地址,按需配置即可;
  • input.kafka.topics:Kafka 的 topic,需要和 Filebeat 输出的 topic 一致;
  • input.kafka.type:定义一个 type,可以用于 logstash 输出至不同的 Elasticsearch 集群;
  • output:数据输出至哪里,本次示例输出至 Elasticsearch 集群,在里面配置了一个判断语句,当 type 为 filebeat-sidecar 时,将会输出至 Elasticsearch 集群,并且 index 为 filebeat-xxx。

查看Pod情况和Service信息

image-20240506232945826

3.5 注入Filebeat Sidecar

下面可以看到在 Deployment 部署文件中,添加了 Volumes 配置,并配置了一个名为 logpath 的 volume,将其挂载到了应用容器的 /opt/ 目录和 Filebeat 的 /data/log/app/ 目录,这样同一个Pod内的两个容器就实现了目录的共享。

[root@k8s-master01 filebeat]# cat app-filebeat.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
    env: release
spec:
  selector:
    matchLabels:
      app: app
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  # minReadySeconds: 30
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - name: filebeat
          image: 10.0.0.138:5000/logs/filebeat:7.10.2
          resources:
            requests:
              memory: "100Mi"
              cpu: "10m"
            limits:
              cpu: "200m"
              memory: "300Mi"
          imagePullPolicy: IfNotPresent
          env:
            - name: podIp
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: podName
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: podNamespace
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
            - name: podDeployName
              value: app
            - name: TZ
              value: "Asia/Shanghai"
          securityContext:
            runAsUser: 0
          volumeMounts:
            - name: logpath
              mountPath: /data/log/app/
            - name: filebeatconf
              mountPath: /usr/share/filebeat/filebeat.yml
              subPath: usr/share/filebeat/filebeat.yml
        - name: app
          image: 10.0.0.138:5000/logs/alpine:3.6
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: logpath
              mountPath: /opt/
          env:
            - name: TZ
              value: "Asia/Shanghai"
            - name: LANG
              value: C.UTF-8
            - name: LC_ALL
              value: C.UTF-8
          command:
            - sh
            - -c
            - while true; do date >> /opt/date.log; sleep 2;  done
      volumes:
        - name: logpath
          emptyDir: {}
        - name: filebeatconf
          configMap:
            name: filebeatconf
            items:
              - key: filebeat.yml
                path: usr/share/filebeat/filebeat.yml

filebeat 配置文件的 ConfigMap,其中 fields 字段是用来添加自定义字段到每个日志事件的元数据中的配置选项之一。它允许你为每个事件添加额外的信息,以便更好地对日志进行分类、过滤或分析。

[root@k8s-master01 filebeat]# cat filebeat-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeatconf
data:
  filebeat.yml: |-
    filebeat.inputs:
    - input_type: log
      paths:
        - /data/log/*/*.log
      tail_files: true
      fields:
        pod_name: '${podName}'
        pod_ip: '${podIp}'
        pod_deploy_name: '${podDeployName}'
        pod_namespace: '${podNamespace}'
    output.kafka:
      hosts: ["kafka:9092"]
      topic: "filebeat-sidecar"
      codec.json:
        pretty: false
      keep_alive: 30s

Tips:需要注意 paths 是配置的共享目录,output.kafka 需要和 logstash 的 kafka 为同一个集群,并且 topic 和 logstash 消费的 topic 为同一个。

创建测试的 Deployment,查看 Pod 下 filebeat 容器中的 /data/log/app/date.log。

kubectl apply -f filebeat-cm.yaml -f app-filebeat.yaml -n logging

image-20240506235137276

将之前的 EFK 架构部署好

image-20240507001604656

3.6 Kibana建立索引并查看数据(实现Pod名称和Namespace检索日志)

image-20240507001626961

2e5ff600-2fef-4f34-8253-b83d723484c0

492a9693-288f-4cc3-b443-997fe3bbcb73

image-20240507002655327

3.7 清理

#清理kafka、zookeeper、filebeat、lostash
[root@k8s-master01 filebeat]# pwd
/root/efk-7.10.2/filebeat
kubectl delete -f filebeat-cm.yaml -f app-filebeat.yaml -n logging
kubectl delete -f logstash-service.yaml -f logstash-cm.yaml -f logstash.yaml -n logging
helm delete -n logging kafka
helm delete -n logging zookeeper

#清理Elasticsearch、Fluentd、Kibana
[root@k8s-master01 efk-7.10.2]# pwd
/root/efk-7.10.2
kubectl delete -f kibana-service.yaml -f kibana-deployment.yaml
kubectl delete -f fluentd-es-ds.yaml -f fluentd-es-configmap.yaml
kubectl delete -f es-statefulset.yaml -f es-service.yaml
kubectl delete -f create-logging-namespace.yaml

4. Loki架构

4.1 架构说明

img

无论是ELK、EFK还是Filebeat,都需要用到Elasticsearch来存储数据,Elasticsearch本身就像“一座大山”,维护难度和资源使用都是偏高的。对于很多公司而言,特别是新创公司,可能并不想大费周章地去搭建一个ELK、EFK或者其他重量级的日志平台,刚开始的人力投入可能是大于收益的,所以就需要一个更轻量的日志收集平台。

一个基于Kubernetes平台的原生日志收集平台Loki Stack应运而生,相对于上述技术栈,Loki的安装、架构、使用都比较简单,能够满足大部分的工作需求。

可以看到主要包含如下组件:

  • Loki:主服务器,负责日志的存储和查询,参考了Prometheus的服务发现机制,将标签添加到日志流,而不是像其他平台一样进行全文索引。
  • Promtail:负责收集日志并将其发送给Loki,主要用于发现采集目标以及添加对应Label,最终发送给Loki。
  • Grafana:用来展示或查询相关日志,可以在页面查询指定标签Pod的日志。

Loki不对日志进行全文索引,仅索引相关日志的元数据,所以Loki操作起来更简单、更省成本。而且Loki是基于Kubernetes进行设计的,可以很方便地部署在Kubernetes上,并且对集群的Pod进行日志采集,采集时会将Kubernetes集群中的一些元数据自动添加到日志中,让技术人员可以根据命名空间、标签等字段进行日志的过滤,可以很快速地定位到相关日志。

4.2 部署Loki

添加helm仓库并拉取helm包

helm repo add grafana https://grafana.github.io/helm-charts
helm pull grafana/loki-stack

helm包版本是loki-stack-2.9.11,提前拉取需要部署服务的镜像到本地Harbor仓库,对以下文件中的镜像地址进行修改。

#一下镜像版本的查看
#loki-stack/values.yaml 下看grafana的配置,tag 是8.3.5
#loki-stack/charts/promtail/Chart.yaml 下看appVersion 是2.8.3

#/root/efk-7.10.2/loki-stack/values.yaml
docker pull bats/bats:1.8.2

#/root/efk-7.10.2/loki-stack/charts/grafana/values.yaml
docker pull grafana/grafana:8.3.5
docker pull bats/bats:1.4.1
docker pull curlimages/curl:7.85.0
docker pull busybox:1.13.1
docker pull quay.io/kiwigrid/k8s-sidecar:1.19.2
docker pull grafana/grafana-image-renderer

#/root/efk-7.10.2/loki-stack/charts/promtail/values.yaml
docker pull docker.io/grafana/promtail:2.8.3
docker pull docker.io/jimmidyson/configmap-reload:v0.8.0

#/root/efk-7.10.2/loki-stack/charts/loki/values.yaml
docker pull grafana/loki:2.6.1

创建 Loki Namespace、 Loki Stack

#/root/efk-7.10.2/loki-stack/下进行
kubectl create ns loki
helm install loki . --set grafana.enabled=true --set grafana.service.type=NodePort -n loki

image-20240507013038036

查看 Pod 状态、以及查看 Grafana 的 Service 暴露的端口号

image-20240507013808740

#查看 Grafana 密码(账号 admin)
kubectl get secret --namespace loki loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

image-20240507014040284

image-20240507015018989

Tips:Loki默认没持久化数据,可以进行修改配置文件实现持久化存储。

LogQL 的官方文档:https://grafana.com/docs/loki/latest/logql/。

4.3 清理

[root@k8s-master01 loki-stack]# pwd
/root/efk-7.10.2/loki-stack
[root@k8s-master01 loki-stack]# helm delete -n loki loki
[root@k8s-master01 loki-stack]# kubectl delete ns loki

注:本篇学习笔记内容参考杜宽的《云原生Kubernetes全栈架构师》,视频、资料文档等,大家可以多多支持!还有YinJayChen语雀k8s训练营“我为什么这么菜”知乎博主等资料文档,感谢无私奉献!

posted @   zzbao  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示