k8S中部署Filebeat+ELK日志系统

一. 概述

ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。新增了一个FileBeat,它是一个轻量级的日志收集处理工具(Agent),Filebeat占用资源少,适合于在各个服务器上搜集日志后传输给Logstash,官方也推荐此工具。
image

1.1 Elasticsearch 存储

Elasticsearch是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。

1.2 Filebeat 日志数据采集

filebeat是Beats中的一员,Beats在是一个轻量级日志采集器,其实Beats家族有6个成员,早期的ELK架构中使用Logstash收集、解析日志,但是Logstash对内存、cpu、io等资源消耗比较高。相比Logstash,Beats所占系统的CPU和内存几乎可以忽略不计。
Filebeat是用于转发和集中日志数据的轻量级传送工具。Filebeat监视您指定的日志文件或位置,收集日志事件。

1.3 Kafka

Kafka是一种高吞吐量的分布式发布订阅消息系统,它能帮助我们削峰。ELK也可以使用redis作为消息队列,但redis作为消息队列不是强项而且redis集群不如专业的消息发布系统kafka。

1.4 Logstash 过滤

Logstash 主要是用来日志的搜集、分析、过滤日志的工具,支持大量的数据获取方式。一般工作方式为c/s架构,client端安装在需要收集日志的主机上,server端负责将收到的各节点日志进行过滤、修改等操作在一并发往elasticsearch上去。

1.5 Kibana 展示

Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

二. 架构介绍

2.1 最简单的 ELK 架构

image
图中 Logstash 多个的原因是考虑到程序是分布式架构的情况,每台机器都需要部署一个 Logstash,如果确实是单服务器的情况部署一个 Logstash 即可。
此架构的优点是搭建简单,易于上手。缺点是Logstash消耗系统资源比较大,运行时占用CPU和内存资源较高。另外,由于没有消息队列缓存,可能存在数据丢失的风险,适合于数据量小的环境使用。

2.2 引入 Kafka 的典型 ELK 架构

image
为保证日志传输数据的可靠性和稳定性,引入Kafka作为消息缓冲队列,位于各个节点上的Logstash Agent(一级Logstash,主要用来传输数据)先将数据传递给消息队列,接着,Logstash server(二级Logstash,主要用来拉取消息队列数据,过滤并分析数据)将格式化的数据传递给Elasticsearch进行存储。最后,由Kibana将日志和数据呈现给用户。由于引入了Kafka缓冲机制,即使远端Logstash server因故障停止运行,数据也不会丢失,可靠性得到了大大的提升。
该架构优点在于引入了消息队列机制,提升日志数据的可靠性,但依然存在Logstash占用系统资源过多的问题,在海量数据应用场景下,可能会出现性能瓶颈。

2.3 FileBeats + Kafka + ELK 集群架构

image
日志采集器Logstash其功能虽然强大,但是它依赖java、在数据量大的时候,Logstash进程会消耗过多的系统资源,这将严重影响业务系统的性能,而filebeat就是一个完美的替代者,它基于Go语言没有任何依赖,配置文件简单,格式明了,同时filebeat比logstash更加轻量级,所以占用系统资源极少,非常适合安装在生产机器上。这就是推荐使用filebeat,也是 ELK Stack 在 Agent 的第一选择。
此架构适合大型集群、海量数据的业务场景,它通过将前端Logstash Agent替换成filebeat,有效降低了收集日志对业务系统资源的消耗。同时,消息队列使用kafka集群架构,有效保障了收集数据的安全性和稳定性,而后端Logstash和Elasticsearch均采用集群模式搭建,从整体上提高了ELK系统的高效性、扩展性和吞吐量。我所在的项目组采用的就是这套架构,由于生产所需的配置较高,且涉及较多持久化操作,采用的都是性能高配的云主机搭建方式而非时下流行的容器搭建。

三. 部署(简化版本)

通过sidecar模式进行接入Filebeat(就是一个pod中部署两个容器,一个跑应用,一个采集日志,用emptdir的共享目录形式),把收集上来的信息直接发送到Elasticsearch中,在kibana中使用Kibana Query Language(KQL)语法进行查询
Elasticsearch 8.5.0
Filebeat 8.5.0
Kibana 8.5.0

3.1 Filebeat

Configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat
  namespace: default
data:
  log: |-
    filebeat.inputs:
      - type: log
        enabled: true
        paths:
          - /app/storage/logs/access.log
        processors:
          - decode_json_fields:
              fields: [ 'message' ]
              target: "d"
              overwrite_keys: false
              process_array: false
              max_depth: 1
        tags: ["access"]

      - type: log
        enabled: true
        paths:
          - /app/storage/logs/error.log
        processors:
          - decode_json_fields:
              fields: [ 'message' ]
              target: "d"
              overwrite_keys: false
              process_array: false
              max_depth: 1
        tags: [ "error" ]

    output.elasticsearch:
      hosts: ["http://elasticsearch:9200"]
      indices:
        - index: "flow-access-%{[agent.version]}-%{+yyyy.MM.dd}"
          when.contains:
            tags: "access"
        - index: "flow-error-%{[agent.version]}-%{+yyyy.MM.dd}"
          when.contains:
            tags: "error"

    setup.template.enabled: true
    setup.template.name: "flow"
    setup.template.pattern: "flow-*"
    setup.template.overwrite: false
    setup.ilm.enabled: false

创建一个deployment资源,包含两个容器

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flow
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      workload.user.cattle.io/workloadselector: apps.deployment-default-flow
  template:
    metadata:
      labels:
        workload.user.cattle.io/workloadselector: apps.deployment-default-flow
    spec:
      containers:
        - image: registry.cn-zhangjiakou.aliyuncs.com/lain-txl/flow   # 应用镜像
          imagePullPolicy: Always
          name: flow
          ports:
            - containerPort: 80
              name: 80tcp30002
              protocol: TCP
          volumeMounts:
            - mountPath: /app/application.yml
              name: application
              subPath: application
            - mountPath: /app/storage/logs   # 共享的日志目录
              name: storage-logs
        - args:
            - -c
            - /config/filebeat.yml
          image: docker.elastic.co/beats/filebeat:8.5.0
          imagePullPolicy: Always
          name: flow-log
          volumeMounts:
            - mountPath: /config/filebeat.yml    # Filebeat 配置
              name: log
              subPath: log
            - mountPath: /app/storage/logs    # 共享的日志目录
              name: storage-logs
      volumes:
        - emptyDir: {}
          name: storage-logs
        - configMap:
            defaultMode: 429
            name: flow
          name: application
        - configMap:
            defaultMode: 429
            name: filebeat
          name: log

3.2 Elasticsearch

Configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: elasticsearch
  namespace: default
data:
  es1: |-
    cluster.name: "docker-cluster"
    network.host: 0.0.0.0
    node.name: "es1"
    cluster.initial_master_nodes: "es1"

    #----------------------- BEGIN SECURITY AUTO CONFIGURATION -----------------------
    #
    # The following settings, TLS certificates, and keys have been automatically
    # generated to configure Elasticsearch security features on 12-11-2022 11:29:11
    #
    # --------------------------------------------------------------------------------

    # Enable security features
    xpack.security.enabled: false

    xpack.security.enrollment.enabled: true

    # Enable encryption for HTTP API client connections, such as Kibana, Logstash, and Agents
    xpack.security.http.ssl:
      enabled: false

    # Enable encryption and mutual authentication between cluster nodes
    xpack.security.transport.ssl:
      enabled: false

    # Create a new cluster with the current node only
    # Additional nodes can still join the cluster later

    #----------------------- END SECURITY AUTO CONFIGURATION -------------------------

    http.cors.enabled: true
    http.cors.allow-origin: "*"
    http.host: 0.0.0.0

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    workload.user.cattle.io/workloadselector: apps.deployment-default-elasticsearch
  name: elasticsearch
  namespace: default
spec:
  selector:
    matchLabels:
      workload.user.cattle.io/workloadselector: apps.deployment-default-elasticsearch
  template:
    metadata:
      labels:
        workload.user.cattle.io/workloadselector: apps.deployment-default-elasticsearch
    spec:
      containers:
        - image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
          imagePullPolicy: IfNotPresent
          name: elasticsearch
          ports:
            - containerPort: 9200
              name: 9200tcp
              protocol: TCP
          volumeMounts:
            - mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
              name: elasticsearch
              subPath: es1
      volumes:
        - configMap:
            defaultMode: 429
            name: elasticsearch
          name: elasticsearch

3.3 Kibana

Configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: kibana
  namespace: default
data:
  kibana: |-
    server.host: "0.0.0.0"
    server.shutdownTimeout: "5s"
    elasticsearch.hosts: [ "http://elasticsearch:9200" ]
    elasticsearch.requestTimeout: 90000
    xpack.encryptedSavedObjects.encryptionKey: 8fd1bcc0bfd4bffeea55c71cdf4213d3
    xpack.reporting.encryptionKey: 692b708a150cb15f28f02590f9cec02d
    xpack.security.encryptionKey: 7713f26f14f8aa5b683ca146524fa97e
    xpack.reporting.kibanaServer.hostname: localhost
    i18n.locale: "zh-CN"

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    workload.user.cattle.io/workloadselector: apps.deployment-default-kibana
  name: kibana
  namespace: default
spec:
  selector:
    matchLabels:
      workload.user.cattle.io/workloadselector: apps.deployment-default-kibana
  template:
    metadata:
      labels:
        workload.user.cattle.io/workloadselector: apps.deployment-default-kibana
    spec:
      containers:
        - image: docker.elastic.co/kibana/kibana:8.5.0
          imagePullPolicy: IfNotPresent
          name: kibana
          ports:
            - containerPort: 5601
              name: 5601tcp30001
              protocol: TCP
          volumeMounts:
            - mountPath: /usr/share/kibana/config/kibana.yml
              name: kibana
              subPath: kibana
      volumes:
        - configMap:
            defaultMode: 429
            name: kibana
          name: kibana

四. 验证

image

可以使用KQL搜索过滤
image

五. 总结

综上,通过上面部署命令来实现 ELK 的整套组件,包含了日志收集、过滤、索引和可视化的全部流程,基于这套系统实现分析日志功能。同时,通过水平扩展 Kafka、Elasticsearch 集群,可以实现日均亿级的日志实时存储与处理,但是从细节方面来看,这套系统还存着许多可以继续优化和改进的点:

  • 日志格式需优化,每个系统收集的日志格式需要约定一个标准,比如各个业务系统在定义log4j或logback日志partern时可以按照【时间】【级别】【全局Traceid】【线程号】【方法名】【日志信息】统一输出。
  • Logstash的正则优化,一旦约定了日志模式,编写Logstash的自定义grok正则就能过滤出关键属性存放于ES,那么基于时间、traceId以及方法名的查询则不在堆积于message,大大优化查询效率。
  • TraceId埋点优化,分布式与微服务架构中,一个Restful请求的发起可能会经过多达十几个系统的处理流程,任何一个环节都有error可能,需要有一个全局ID进行全链路追踪,这里需要结合Java探针把tiraceId埋入日志模板里,现有PinPoint、SkyWalking与ZipKin都能为全局ID提供成熟的解决方案。
  • ES存储优化,按照线上机器的业务量来看,每天TB级的日志数据都写入ES会造成较大的存储压力,时间越久的日志利用价值则越低,可以按照7天有效期来自动清理ES索引优化存储空间,参考【ES清理脚本】(https://www.cnblogs.com/richardzgt/articles/9685112.html)。
  • 运维优化,一个复杂日志平台在运维方面有着巨大的成本,这里涉及到了Kafka、ZooKeeper、ELK等多个集群环境的维护,除了提供统一的集群操作指令以外,也需要形成对整套日志平台环境的监控视图。
  • 性能优化,多组件、混合语言、分布式环境与集群林立的复杂系统,性能问题老生常谈,实践出真知,遇到了再补充!

参考:
企业级日志系统架构ELK
filebeat+ELK+kafka 架构体系 运行原理
详解Kibana中的KQL语法
Kibana:Kibana Query Language - KQL
使用 fluent-bit 采集文件

posted @ 2022-11-13 01:33  Azure沫  阅读(5967)  评论(1编辑  收藏  举报