K8S日志收集

K8S日志收集

在生产环境下,Kubernetes (K8s) 的日志收集是维护系统稳定性和排查问题的关键环节

1、 Kubernetes 系统日志

(1)API Server 日志:记录所有对 Kubernetes API 的请求与响应。关键日志位置:/var/log/kube-apiserver.log。包括认证、授权和资源变更等信息。

(2)Controller Manager 日志:管理控制器的工作状态,跟踪资源状态的自动调谐。日志位置:/var/log/kube-controller-manager.log

(3)Scheduler 日志:记录 Pod 的调度决策过程。日志位置:/var/log/kube-scheduler.log

(4)Etcd 日志::记录集群状态数据的持久化和操作。日志位置:/var/log/etcd.log

2、节点级别日志

(1)Kubelet 日志:管理节点上的容器运行状态。日志位置:/var/log/kubelet.log

(2)容器运行时日志:包括 Docker 或 containerd 等容器运行时的操作记录。日志位置:Docker: /var/log/docker.log/Containerd: /var/log/containerd.log

(3)Kube-Proxy 日志:管理网络代理规则。日志位置:/var/log/kube-proxy.log

3、应用容器日志

(1)容器的标准输出和错误日志:默认存储在节点的 /var/lib/docker/containers(或其他容器运行时的日志路径)。可以通过 kubectl logs \ 查看。

(2)应用内自定义日志:由开发人员配置并输出,例如 log4j、fluentd。

4、网络日志

(1)CNI 插件日志:Calico、Flannel 或其他 CNI 插件的网络日志,用于监控网络连通性和性能问题。

(2)Ingress 和 Service 的访问日志:跟踪外部请求的流量和服务内部转发情况。

5、安全与审计日志

(1)API Server 审计日志:用于记录所有 API 操作的审计事件,方便安全合规性审查。配置:启用 audit-policy.yaml。

(2)系统安全日志:包括主机的 auth.log 或 syslog,用以监控未授权访问。

6、第三方组件日志

(1)日志收集工具的日志::如 Fluentd、Logstash、Filebeat 等收集服务的自身运行日志。

(2)监控系统日志:如 Prometheus、Grafana、ElasticSearch 的运行状态日志。

日志收集与集中管理

为了更高效管理日志,可以使用集中化日志管理工具,例如:

(1)ELK/EFK Stack:ElasticSearch + Logstash/Fluentd + Kibana

(2)Promtail + Loki + Grafana

(3)DataDog、Splunk 等 SaaS 解决方案 通过统一收集和分析日志,可以快速定位问题并优化系统性能。

ELK 和 EFK 是两种常见的日志收集和分析架构,它们的核心功能类似,但使用的组件不同。以下是它们的架构简述:


1. ELK 架构

全称:ElasticSearch + Logstash + Kibana

组件说明
  • ElasticSearch (ES)

    • 一个分布式搜索和分析引擎。
    • 用于存储和索引日志数据。
    • 提供快速的全文搜索和复杂查询功能。
  • Logstash

    • 数据收集和处理工具。
    • 支持多种输入插件(如文件、TCP、Kafka)和输出插件。
    • 可以在日志进入 ElasticSearch 前对数据进行解析、过滤和格式化。
  • Kibana

    • 数据可视化工具。
    • 提供友好的用户界面,用于查看、搜索和分析日志。
    • 可创建仪表盘展示日志趋势和重要指标。

数据流:容器或应用产生日志日志。Logstash 收集日志并进行格式化处理。ElasticSearch 存储和索引日志。Kibana 提供日志可视化界面。

优点:数据处理能力强,支持复杂日志处理逻辑。插件丰富,适应多种数据源。查询和可视化功能强大。

缺点:Logstash 资源消耗较高,可能影响性能。部署和维护复杂。

2. EFK 架构

全称:ElasticSearch + Fluentd + Kibana

组件说明
  • ElasticSearch (ES)

    • 功能与 ELK 架构中相同。
  • Fluentd

    • 日志收集和聚合工具。
    • 使用轻量化设计,资源占用低。
    • 支持多种输入和输出插件,可以直接将日志发送到 ElasticSearch。
  • Kibana

    • 功能与 ELK 架构中相同。

    数据流:应用或系统生成日志。Fluentd 收集日志并将其发送至 ElasticSearch。ElasticSearch 存储和索引日志。Kibana 提供日志可视化界面。

    优点:Fluentd 更轻量级,适合资源受限的环境。简化部署流程,配置较容易。社区活跃,插件生态丰富。

    缺点:数据处理功能不如 Logstash 强大。在需要复杂日志解析时可能表现不足。

特性 ELK EFK
日志收集器 Logstash Fluentd
资源占用 较高,适合高性能集群 较低,适合资源受限环境
数据处理能力 强大,支持复杂过滤和转换 较弱,适合轻量日志处理
易用性 配置复杂,部署相对困难 配置简单,部署更轻量

在实际应用中,EFK 更适合 Kubernetes 这样的容器化场景,而 ELK 则更适合需要复杂日志处理的大型传统系统。

使用EFK收集控制台日志

拉取资源,读者可以自由选择是否去除控制平面的污点,作者环境为3master+2node,内存为5Gib,2Vcpu

[root@master-01 ~]# kubectl get nodes
NAME        STATUS   ROLES    AGE   VERSION
master-01   Ready    <none>   22d   v1.30.6
master-02   Ready    <none>   22d   v1.30.6
master-03   Ready    <none>   22d   v1.30.6
node-01     Ready    <none>   22d   v1.30.6
node-02     Ready    <none>   22d   v1.30.6
[root@master-01 ~]# kubectl describe node | grep Taints
Taints:             <none>
Taints:             <none>
Taints:             <none>
Taints:             <none>
Taints:             <none>
[root@master-01 ~]# git clone https://github.com/dotbalo/k8s.git
Cloning into 'k8s'...
remote: Enumerating objects: 1369, done.
remote: Counting objects: 100% (324/324), done.
remote: Compressing objects: 100% (218/218), done.
remote: Total 1369 (delta 128), reused 224 (delta 92), pack-reused 1045 (from 1)
Receiving objects: 100% (1369/1369), 5.42 MiB | 913.00 KiB/s, done.
Resolving deltas: 100% (568/568), done.

切换到目录下,查看需要创建的资源

[root@master-01 ~]# cd k8s/efk-7.10.2/
[root@master-01 efk-7.10.2]# ls
create-logging-namespace.yaml  es-service.yaml  es-statefulset.yaml  filebeat  fluentd-es-configmap.yaml  fluentd-es-ds.yaml  kafka  kibana-deployment.yaml  kibana-service.yaml

创建logging命名空间

[root@master-01 efk-7.10.2]# kubectl create -f create-logging-namespace.yaml
namespace/logging created

创建elasticsearch集群相关资源

[root@master-01 efk-7.10.2]# kubectl create -f es-service.yaml
service/elasticsearch-logging created
[root@master-01 efk-7.10.2]# kubectl create -f es-statefulset.yaml
serviceaccount/elasticsearch-logging created
clusterrole.rbac.authorization.k8s.io/elasticsearch-logging created
clusterrolebinding.rbac.authorization.k8s.io/elasticsearch-logging created
statefulset.apps/elasticsearch-logging created

创建kibana相关资源

[root@master-01 efk-7.10.2]# kubectl create -f kibana-deployment.yaml -f kibana-service.yaml
deployment.apps/kibana-logging created
service/kibana-logging created

创建fluentd相关资源,通过label控制fluentd采集日志的节点,在需要采集日志的节点上添加fluentd=true的标签

[root@master-01 efk-7.10.2]# grep 'nodeSelector' -A 1 fluentd-es-ds.yaml
      nodeSelector:
        fluentd: "true"
[root@master-01 efk-7.10.2]# kubectl label nodes node-01 fluentd=true
node/node-01 labeled
[root@master-01 efk-7.10.2]# kubectl create -f fluentd-es-ds.yaml -f fluentd-es-configmap.yaml
serviceaccount/fluentd-es created
clusterrole.rbac.authorization.k8s.io/fluentd-es created
clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created
daemonset.apps/fluentd-es-v3.1.1 created
configmap/fluentd-es-config-v0.2.1 created

等待镜像拉取,资源初始化完成后,查看容器状态

[root@master-01 efk-7.10.2]# kubectl get pods -n logging
NAME                              READY   STATUS    RESTARTS   AGE
elasticsearch-logging-0           1/1     Running   0          32h
fluentd-es-v3.1.1-8r2tl           1/1     Running   0          32h
kibana-logging-55bccd5698-4fhqv   1/1     Running   0          32h

附带Fluentd ConfigMap内配置的解析,供读者参考

 system.conf: |-
    <system>
      root_dir /tmp/fluentd-buffers/
    </system>
  containers.input.conf: |-
    <source>
      @id fluentd-containers.log
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/es-containers.log.pos
      tag raw.kubernetes.*
      read_from_head true
      <parse>
        @type multi_format
        <pattern>
          format json
          time_key time
          time_format %Y-%m-%dT%H:%M:%S.%NZ
        </pattern>
        <pattern>
          format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
          time_format %Y-%m-%dT%H:%M:%S.%N%:z
        </pattern>
      </parse>
    </source>
字段 含义
@id 表示引用该日志源的唯一标识符,该标识可用于进一步过滤和路由结构化日志数据
@type Fluentd 内置的指令,tail 表示 Fluentd 从上次读取的位置通过 tail 不断获取数据,另外一个是 http 表示通过一个 GET 请求来收集数据
path tail 类型下的特定参数,告诉 Fluentd 采集 /var/log/containers 目录下的所有日志,这是 docker 在 Kubernetes 节点上用来存储运行容器 stdout 输出日志数据的目录
pos_file 检查点,用于记录读取日志文件的偏移量(offset),如果 Fluentd 程序重新启动了,它将使用此文件中的位置来恢复日志数据收集
tag 用来将日志源与目标或者过滤器匹配的自定义字符串,Fluentd 匹配源/目标标签来路由日志数据
read_from_head 从日志文件的开头开始读取(而不是文件尾部),适用于首次运行时需要完整读取日志的场景
format 指定为json格式,或者使用正则表达式解析日志。
time_key 指定时间字段的键名为 time
time_format 定义时间的格式,如:2024-11-20T15:45:30.123Z
(?<time>.+):提取时间字段。
(?<stream>stdout|stderr):提取日志流类型(标准输出或标准错误)。
[^ ]*:跳过无用字段。
(?<log>.*):提取日志消息。

路由配置,将日志数据发送到Elasticsearch

<match **>
      @id elasticsearch
      @type elasticsearch
      @log_level info
      type_name _doc
      include_tag_key true
      host elasticsearch-logging
      port 9200
      logstash_format true
      <buffer>
        @type file
        path /var/log/fluentd-buffers/kubernetes.system.buffer
        flush_mode interval
        retry_type exponential_backoff
        flush_thread_count 2
        flush_interval 5s
        retry_forever
        retry_max_interval 30
        chunk_limit_size 2M
        total_limit_size 500M
        overflow_action block
      </buffer>
    </match>
字段 含义
\<match **> 标识一个目标标签,后面是一个匹配日志源的正则表达式,我们这里想要捕获所有的日志并将它们发送给 Elasticsearch,所以需要配置成**
@id 目标的一个唯一标识符
@type 支持的输出插件标识符,我们这里要输出到 Elasticsearch,所以配置成 elasticsearch,这是 Fluentd 的一个内置插件
@log_level 指定要捕获的日志级别,我们这里配置成 info,表示任何该级别或者该级别以上(INFO、WARNING、ERROR)的日志都将被路由到 Elsasticsearch
type_name 定义 Elasticsearch 索引的文档类型
include_tag_key 将 Fluentd 的标签(tag)包含到日志记录中
host/port 定义 Elasticsearch 的地址,也可以配置认证信息,我们的 Elasticsearch 不需要认证,所以这里直接指定 host 和 port 即可
logstash_format Elasticsearch 服务对日志数据构建反向索引进行搜索,将 logstash_format 设置为 true,Fluentd 将会以 logstash 格式来转发结构化的日志数据
\ Fluentd 允许在目标不可用时进行缓存,比如,如果网络出现故障或者 Elasticsearch 不可用的时候。缓冲区配置也有助于降低磁盘的 IO
@type 缓冲类型设置为文件缓存,日志会临时存储在磁盘中,避免因网络问题导致数据丢失
path 缓冲文件存储路径
flush_mode 设置缓冲区的刷新模式为定期刷新
retry_type 定义重试机制为指数退避(exponential backoff),逐步增加重试间隔时间
flush_thread_count 设置 2 个线程用于处理缓冲区刷新的任务
flush_interval 每隔 5 秒刷新一次缓冲区数据到 Elasticsearch
retry_forever 设置重试机制为永不放弃,直到成功写入 Elasticsearch
retry_max_interval 最大重试间隔
chunk_limit_size 缓冲区块的最大大小为 2MB
total_limit_size 最大缓冲区大小为500MB,超过此限制,将触发溢出策略
overflow_action 溢出策略为block,暂停写入缓冲区

过滤和拼接规则

    <filter **>
      @id filter_concat
      @type concat
      key message
      multiline_end_regexp /\n$/
      separator ""
    </filter>
    # Fixes json fields in Elasticsearch
    <filter kubernetes.**>
      @id filter_parser
      @type parser
      key_name log
      reserve_data true
      remove_key_name_field true
      <parse>
        @type multi_format
        <pattern>
          format json
        </pattern>
        <pattern>
          format none
        </pattern>
      </parse>
    </filter>
参数 含义
\<filter **> 匹配所有日志(** 表示通配符,匹配所有标签)
multiline_end_regexp 匹配多行日志的结束标志,这里是以换行符 \n 结束
separator 拼接时不添加任何分隔符
reserve_data 保留原始日志数据,不覆盖其他字段
remove_key_name_field 解析成功后移除原始的 log 字段,避免冗余

当所有容器处于Running状态,Ready状态为1/1时(视情况而定),可以通过kibana暴露的nodeport端口进行访问,访问的方式为https://$(配置了kube-proxy的节点IP):$(NodePort暴露端口)/kibana

[root@master-01 efk-7.10.2]# kubectl get svc -n logging
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearch-logging   ClusterIP   None            <none>        9200/TCP,9300/TCP   23h
kibana-logging          NodePort    10.96.115.179   <none>        5601:32251/TCP      23h
[root@master-01 efk-7.10.2]# curl http://192.168.132.236:32251/kibana

访问成功后,依次单击Explore on my own→Visualize→Add your data→Create index pattern。在弹出的界面添加索引logstash*,随后点击Next step按钮。

Explore on my own

visualize

add you data

create index pattren

define an index

继续填写时间字段,填写完成后依次单击左侧菜单栏→Discover按钮即可观察到采集的数据

configure setting

UI

使用Filebeat收集自定义文件日志

使用Fluentd无法采集容器内部的日志,可以通过Filebeat和emptyDir的方式采集容器内部的日志数据,从而输出到Kibana实现可视化

Filebeat

创建zookeeper和kafka容器

[root@master-01 ~]# cd k8s/efk-7.10.2/filebeat/
[root@master-01 filebeat]# helm install zookeeper zookeeper/ -n logging
NAME: zookeeper
LAST DEPLOYED: Thu Nov 21 08:46:32 2024
NAMESPACE: logging
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
** Please be patient while the chart is being deployed **

ZooKeeper can be accessed via port 2181 on the following DNS name from within your cluster:

    zookeeper.logging.svc.cluster.local

To connect to your ZooKeeper server run the following commands:

    export POD_NAME=$(kubectl get pods --namespace logging -l "app.kubernetes.io/name=zookeeper,app.kubernetes.io/instance=zookeeper,app.kubernetes.io/component=zookeeper" -o jsonpath="{.items[0].metadata.name}")
    kubectl exec -it $POD_NAME -- zkCli.sh

To connect to your ZooKeeper server from outside the cluster execute the following commands:

    kubectl port-forward --namespace logging svc/zookeeper 2181:2181 &
    zkCli.sh 127.0.0.1:2181
[root@master-01 filebeat]# helm install kafka kafka/ -n logging
NAME: kafka
LAST DEPLOYED: Thu Nov 21 09:18:14 2024
NAMESPACE: logging
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
** Please be patient while the chart is being deployed **

Kafka can be accessed by consumers via port 9092 on the following DNS name from within your cluster:

    kafka.logging.svc.cluster.local

Each Kafka broker can be accessed by producers via port 9092 on the following DNS name(s) from within your cluster:

    kafka-0.kafka-headless.logging.svc.cluster.local:9092

To create a pod that you can use as a Kafka client run the following commands:

    kubectl run kafka-client --restart='Never' --image docker.io/bitnami/kafka:2.8.0-debian-10-r30 --namespace logging --command -- sleep infinity
    kubectl exec --tty -i kafka-client --namespace logging -- bash

    PRODUCER:
        kafka-console-producer.sh \

            --broker-list kafka-0.kafka-headless.logging.svc.cluster.local:9092 \
            --topic test

    CONSUMER:
        kafka-console-consumer.sh \

            --bootstrap-server kafka.logging.svc.cluster.local:9092 \
            --topic test \
            --from-beginning

等待容器成功运行

[root@master-01 filebeat]# kubectl get pods -n logging
NAME                                   READY   STATUS    RESTARTS        AGE
elasticsearch-logging-0                1/1     Running   0               6h40m
elasticsearch-logging-1                1/1     Running   1 (6h38m ago)   6h39m
elasticsearch-logging-2                1/1     Running   0               6h37m
fluentd-es-v3.1.1-wgqhc                1/1     Running   1 (6h39m ago)   6h40m
kafka-0                                1/1     Running   0               5h20m
kibana-logging-55bccd5698-9wh2g        1/1     Running   0               6h39m
zookeeper-0                            1/1     Running   0               5h26m

注意:在上一个章节的代理不太稳定,因为作者更换了一种更为有效的方法,使用clash verge(clash for windows)也可以,前提需要能科学上网。单击左侧导航的设置按钮,打开Tun模式、服务模式、系统代理、局域网连接,单击端口设置,记录下Http(s)代理端口。

clash verge

在需要使用代理的虚拟机上,写入以下配置,并刷新配置文件。虚拟机就可以直接拉取docker.io开头的镜像了

[root@master-01 filebeat]# vim /etc/profile
export https_proxy="http://192.168.132.1:7899"
export no_proxy='192.168.132.169,192.168.132.236,127.0.0.1,localhost'
[root@master-01 filebeat]# source /etc/profile

创建Logstash服务

[root@master-01 filebeat]# kubectl create -f logstash.yaml -f logstash-cm.yaml -f logstash-service.yaml -n logging
deployment.apps/logstash-deployment created
configmap/logstash-configmap created
service/logstash-service created

针对Logstash配置文件进行解析

  logstash.conf: |
    # all input will come from filebeat, no local logs
    input {
      kafka {
              enable_auto_commit => true
              auto_commit_interval_ms => "1000"
              bootstrap_servers => "kafka:9092"
              topics => ["filebeat-sidecar"]
              type => ["filebeat-sidecar"]
              codec => json
          }
    }

    output {
       stdout{ codec=>rubydebug}
       if [type] == "filebeat-sidecar"{
           elasticsearch {
             hosts => ["elasticsearch-logging-0.elasticsearch-logging:9200","elasticsearch-logging-1.elasticsearch-logging:9200"]
             index => "filebeat-%{+YYYY.MM.dd}"
          }
       } else{
          elasticsearch {
             hosts => ["elasticsearch-logging-0.elasticsearch-logging:9200","elasticsearch-logging-1.elasticsearch-logging:9200"]
             index => "other-input-%{+YYYY.MM.dd}"
          }
       }
    }
字段 含义
input 数据来源,本次示例配置的是 Kakfa
enable_auto_commit 启用自动提交偏移量,确保 Kafka 的消费者组可以记录消费的进度
auto_commit_interval_ms 偏移量提交的时间间隔,设置为 1000ms
bootstrap_servers Kafka 地址,由于是安装在集群内部的,可以直接使用Kafka 集群的 Service 接口,如果是外部地址,按需配置即可
topics Kafka 的 topic,需要和 Filebeat 输出的 topic 一致
type 定义一个 type,可以用于 logstash 输出至不同的 Elasticsearch 集群
codec 指定解码器,将 Kafka 消息解析为 JSON 格式
output 数据输出至哪里,本次示例输出至 Elasticsearch 集群,在里面配置了一个判断语句,当 type 为 filebeat-sidecar 时,将会输出至 Elasticsearch 集群,并且 index 为 filebeat-xxx

注入Filebeat Sidecar。写入配置文件,其中Pod内包含两个Containerd,第一个Containerd名称为app,容器内部带有一个死循环,会不断打印当前时间并写入到/opt/date.log文件内(此时如果不配置Filebeat,Kibana无法读取app容器内部的日志信息,模拟一些传统的日志文件类型)。第二个Containerd名称为filebeat,通过挂载emptyDir来共享app容器内的日志文件,从而进行采集。

[root@master-01 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:
    ...省略部分输出...
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - name: filebeat
          image: registry.cn-beijing.aliyuncs.com/dotbalo/filebeat:7.10.2
          resources:
            requests:
            ....省略部分输出..
          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: registry.cn-beijing.aliyuncs.com/dotbalo/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的配置文件定义了采集的日志路径并且使用fields字段为日志添加自定义字段,并将内容输出(output.kafka)到kafka进行处理

[root@master-01 filebeat]# vim 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

创建资源,查看容器创建状态

[root@master-01 filebeat]# kubectl create -f  app-filebeat.yaml -f filebeat-cm.yaml -n logging
deployment.apps/app created
configmap/filebeatconf created
[root@master-01 filebeat]# kubectl get pods -n logging
NAME                                   READY   STATUS    RESTARTS        AGE
app-5bc7495dd6-pnvlb                   2/2     Running   0               6h14m
elasticsearch-logging-0                1/1     Running   0               7h56m
elasticsearch-logging-1                1/1     Running   1 (7h54m ago)   7h55m
elasticsearch-logging-2                1/1     Running   0               7h53m
fluentd-es-v3.1.1-wgqhc                1/1     Running   1 (7h54m ago)   7h55m
kafka-0                                1/1     Running   0               6h36m
kibana-logging-55bccd5698-9wh2g        1/1     Running   0               7h55m
logstash-deployment-5f5bdf7bf7-xdhz6   1/1     Running   0               6h32m
zookeeper-0                            1/1     Running   0               6h41m

查看容器内部是否有日志输出

[root@master-01 filebeat]# kubectl exec -it -n logging app-5bc7495dd6-pnvlb -c app -- tail -10 /opt/date.log
Thu Nov 21 07:55:01 UTC 2024
Thu Nov 21 07:55:03 UTC 2024
Thu Nov 21 07:55:05 UTC 2024
Thu Nov 21 07:55:07 UTC 2024
Thu Nov 21 07:55:10 UTC 2024
Thu Nov 21 07:55:12 UTC 2024
Thu Nov 21 07:55:14 UTC 2024
Thu Nov 21 07:55:16 UTC 2024
Thu Nov 21 07:55:18 UTC 2024
Thu Nov 21 07:55:20 UTC 2024

打开Kibana的U界面,依次单击左侧导航栏的三条杠→Stack Management→Index Patterns→Createindex pattern,只有在Create index pattern界面下方看到filebeat-2024.xx.xx的索引字段才能认为日志采集成功,如果没有SVC看到该字段,需要读者检查filebeat和app容器的连通性,能否共享日志文件,filebeat输出到kafka的端口和名称是否正常,各个服务的SVC端点是否正常,elasticsearch和kafka服务是否正常。

create new index

在Index pattern name的文本框内填写filebeat*,随后单击Next Step按钮

filebeat_1

选择时间标签后,单击Create index pattern按钮创建索引

timestamp

创建完成后,即可在Discover界面看到app容器的日志信息切片,读者也可以学习KQL语法对真正生产环境下的复杂信息进行筛选

kql

Loki架构简述

Loki 是一个轻量级的日志聚合系统,专注于 元数据索引,并与 Prometheus 和 Grafana 紧密集成。以下是 Loki 的主要架构组件:

  1. Client (Promtail/Fluentd/Filebeat)

    • Promtail 是 Loki 的官方日志收集工具,类似于 Filebeat。
    • 它从日志文件或容器日志中收集日志,添加 Kubernetes 等元数据,并推送到 Loki。
  2. Loki 服务: Loki 的核心架构包含以下组件:

    • Distributor: 接收日志数据,并将其分片后分发到多个 Ingester。
    • Ingester: 临时存储日志数据并将其持久化到对象存储(如 S3 或 GCS)或块存储(如 Cassandra)。
    • Querier: 根据查询请求从存储和 Ingesters 中检索数据。
    • Storage: 使用云对象存储(S3、GCS)、块存储(Cassandra、Bigtable)或文件系统存储日志。
  3. Query & Visualization (Grafana)

    • Grafana 提供查询和展示 Loki 数据的功能,通过 LogQL(类似 PromQL)查询日志。

Loki 与 EFK/ELK 的区别

  • Loki:专注于日志的轻量存储,仅索引元数据(如日志流的标签),而不对日志内容全文索引。
  • EFK/ELK:适用于大规模日志的复杂查询和全文索引场景。
日志索引机制
  • Loki

    • 仅索引日志流的标签(如 Kubernetes 的 pod_namenamespace 等)。
    • 日志内容以原始形式存储,查询时按需读取。
    • 索引简单,占用存储小,性能较高,但不支持复杂的全文搜索。
  • EFK/ELK

    • 使用 Elasticsearch 对日志内容进行全文索引。
    • 提供强大的查询功能(如模糊查询、正则匹配)。
    • 适合需要复杂分析和搜索的场景,但索引开销大,存储和计算资源占用较多。
存储和扩展性
  • Loki

    • 依赖对象存储(如 S3)、块存储(如 Cassandra),支持高效存储。
    • 天然支持云原生架构,易于水平扩展。
  • EFK/ELK

    • Elasticsearch 存储索引和日志,存储资源需求大,扩展时需要更多计算和内存资源。
性能和资源占用
  • Loki

    • 不需要建立复杂的索引,资源占用低。
    • 更适合云原生环境和 Kubernetes 的日志管理。
  • EFK/ELK

    • 性能依赖 Elasticsearch 的集群配置,硬件需求较高。
    • 在存储海量日志时可能会遇到性能瓶颈。
查询语言
  • Loki:使用 LogQL 查询语言,类似于 Prometheus 的 PromQL,支持简单的过滤和聚合。
  • EFK/ELK:Elasticsearch 提供了丰富的 DSL 查询语法,支持复杂的搜索和统计分析。
可视化工具
  • Loki:与 Grafana 原生集成,通过 Grafana 提供强大的日志和指标联动查询。
  • EFK/ELK:通常使用 Kibana,适合复杂的日志分析和多维度数据展示。
部署和管理
  • Loki
    • 部署简单,组件较少。
    • 轻量级,适合资源有限的环境。
  • EFK/ELK
    • 部署较复杂,特别是在集群和分布式存储配置方面。
    • 需要更多运维经验和资源。

Loki 和 EFK/ELK 的选择场景

  1. 使用 Loki 的场景

    • 云原生环境(如 Kubernetes)下的日志管理。
    • 需要与 Prometheus 集成,通过指标联动查询日志。
    • 对日志查询的需求简单,不需要全文索引和复杂分析。
    • 资源有限的环境,需要低成本的日志解决方案。
  2. 使用 EFK/ELK 的场景

    • 需要对日志内容进行全文检索。
    • 需要复杂的日志分析(如模糊搜索、统计、关联分析)。
    • 有足够的硬件资源支持高性能 Elasticsearch 集群。

总结:

  • Loki 以轻量化为优势,适用于日志内容检索需求较低的场景,资源占用小,部署简单。
  • EFK/ELK 更强大,但资源需求大,适用于需要全文搜索和复杂日志分析的场景。

添加loki仓库,随后安装loki

[root@master-01 ~]# helm repo add grafana https://grafana.github.io/helm-charts
"grafana" has been added to your repositories
[root@master-01 ~]# helm install loki grafana/loki-stack --set grafana.enabled=true --set grafana.service.type=NodePort -n loki
NAME: loki
LAST DEPLOYED: Thu Nov 21 18:11:17 2024
NAMESPACE: loki
STATUS: deployed
REVISION: 1
NOTES:
The Loki stack has been deployed to your cluster. Loki can now be added as a datasource in Grafana.

See http://docs.grafana.org/features/datasources/loki/ for more detail.

等待一段时间,等待容器全部创建完成,并且处于就绪状态

[root@master-01 ~]# kubectl get pods -n loki
NAME                            READY   STATUS    RESTARTS   AGE
loki-0                          1/1     Running   0          13m
loki-grafana-7fb8487767-npqzg   2/2     Running   0          13m
loki-promtail-grtzb             1/1     Running   0          13m
loki-promtail-gt7xm             1/1     Running   0          13m
loki-promtail-mh9mg             1/1     Running   0          13m
loki-promtail-q9h5v             1/1     Running   0          13m
loki-promtail-tsnkg             1/1     Running   0          13m

查看Grafana服务暴露的端口

[root@master-01 ~]# kubectl get svc -n loki
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
loki              ClusterIP   10.96.165.233   <none>        3100/TCP       30s
loki-grafana      NodePort    10.96.112.167   <none>        80:32174/TCP   30s
loki-headless     ClusterIP   None            <none>        3100/TCP       30s
loki-memberlist   ClusterIP   None            <none>        7946/TCP       30s

解密存放在Secrets内的Grafana密码

[root@master-01 ~]# kubectl get secrets -n loki loki-grafana -oyaml
apiVersion: v1
data:
  admin-password: bUFDRjBtWDYwbFBlSTdlTVIyTVlLNXdrRmdkaWp0azNXVk54MmxWSQ==
  admin-user: YWRtaW4=
  ldap-toml: ""
kind: Secret
metadata:
  annotations:
    meta.helm.sh/release-name: loki
    meta.helm.sh/release-namespace: loki
  creationTimestamp: "2024-11-21T10:11:21Z"
  labels:
    app.kubernetes.io/instance: loki
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: grafana
    app.kubernetes.io/version: 10.3.3
    helm.sh/chart: grafana-6.43.5
  name: loki-grafana
  namespace: loki
  resourceVersion: "2091857"
  uid: 347d8d16-0551-4347-891d-a400d6f9584e
type: Opaque
[root@master-01 ~]# echo 'bUFDRjBtWDYwbFBlSTdlTVIyTVlLNXdrRmdkaWp0azNXVk54MmxWSQ==' | base64 --decode

通过IP+SVC暴露的端口访问Grafana,依次单击左侧导航栏的三道杠→Explore,选择Loki

Explore

读者可以通过图形化界面进行简单条件过滤,也可以通过类似正则表达式的语法进行筛选,此处附上官网链接,不在此处过多介绍

Explore

https://grafana.com/docs/loki/latest/query/