暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

kubernetes上的Fluent日志采集架构 - Fluent Bit、Fluentd 和 Elasticsearch

云原生CTO 2021-11-17
2472

CTO

 
 

 
 


 

Go
Rust
Python
Istio
containerd
CoreDNS
Envoy
etcd
Fluentd
Harbor
Helm
Jaeger
Kubernetes
Open Policy Agent
Prometheus
Rook
TiKV
TUF
Vitess
Argo
Buildpacks
CloudEvents
CNI
Contour
Cortex
CRI-O
Falco
Flux
gRPC
KubeEdge
Linkerd
NATS
Notary
OpenTracing
Operator Framework
SPIFFE
SPIRE
  Thanos




kubernetes上的Fluent日志采集架构 - Fluent Bit、Fluentd 和 Elasticsearch

在本文中,我将尝试解释我们如何使用 Fluent Bit
Fluentd
Elasticsearch
创建可靠的日志架构。

我们总结一下在本次在介绍当中的kubernetes
日志架构工作流,首先Fluent Bit
充当Kubernetes
集群中的守护进程集。运行在Kubernetes
收集并转发标准输出日志到每个节点Fluentd
,它位于Kubernetes
簇之外。Fluentd
修改、过滤和处理这些日志并将它们发送到Elasticsearch
集群。最后,Kibana
将这些日志可视化。

为什么日志很重要?

因为日志提供了对正在运行的应用程序行为的可见性和监控。最终,每个应用程序都会崩溃,服务器会宕机,或者用户会对错误感到沮丧。如果我们有一个良好的日志记录和监控基础设施,我提到的这些困难的解决方案就会变得更容易。日志记录是我们的遗留应用程序和当今现代应用程序中最关键的方面之一。因此,我们需要仔细考虑。

因为日志提供了对正在运行的应用程序行为的可见性和监控: https://12factor.net/logs

为什么要使用日志路由器?

我们的主要目标是;将日志视为事件流。根据十二因素应用程序方法,应用程序从不关心其输出流的路由或存储。

应用程序从不关心其输出流的路由或存储: https://12factor.net/logs

转发和处理日志以及存储它们不应该由我们的应用程序处理。如果我们使用日志路由器/转发器,我们的应用程序将不会处理不必要的责任。当我们的应用程序与日志存储和处理进行分离时,我们的代码将变得更简单。

什么是 Fluentd、Fluent Bit 和 Elasticsearch?

  • Fluentd
    2011
    年创建的基于 Ruby
    的开源日志收集器和处理器。 Fluentd
    使用大约 40 MB
    的内存,每秒可以处理超过 10,000
    个事件。有超过 500
    种不同的插件可用。Fluentd
    在操作上类似于elk
    堆栈上的logstash
  • Fluent Bit
    Fluentd
    由同一家公司开发,以实现高性能和低内存消耗。更适合在k8s
    环境下使用。( k8s
    Kubernetes
    的缩写)你可以在这里查看两者的比较。
  • Elasticsearch
    是一个分布式、可扩展、基于 JSON
    的搜索和分析引擎。它被广泛用作elk
    堆栈(Elasticsearch
    Kibana
    Beats
    Logstash
    )。Kibana
    Elasticsearch
    数据的可视化工具。

“我们软件系统中的可观察性一直很有价值,在这个云和微服务时代变得更加重要” - Martin Fowler

Fluentd: https://www.fluentd.org/architecture

Fluent Bit: https://docs.fluentbit.io/manual/about/what-is-fluent-bit

Elasticsearch: https://www.elastic.co/elasticsearch/

Kibana: https://www.elastic.co/kibana/

示范

在我们的实现过程中,首先我们需要一个容器化的应用程序,将日志作为JSON
发送到stdout
。然后 我们需要一个 Kubernetes
集群,安装 Fluentd
,以及安装 elk stack
。(我的演示建议是将 docker
用于 efk
堆栈。您可以在此处找到详细信息)。

https://docs.fluentd.org/container-deployment/docker-compose

您可以将Minikube
用作 Kubernetes
环境。我们要为 Fluentd
Elasticsearch
使用单独的服务器的原因是,其中一个的任何瓶颈都不会影响另一个。这样,我们就可以实现松耦合的日志架构。

stdout
是指由Linux
和其他类 Unix
操作系统中的命令行程序生成的标准化数据流。

作为起点,我们将向基于 dotnet
Web API
应用程序添加库,以将日志转换为 JSON
。我们将使用 Serilog
及其插件,它是最受欢迎的库之一。

$ dotnet add package Serilog
$ dotnet add package Serilog.Sinks.Console
$ dotnet add package Serilog.Formatting.Elasticsearch
$ dotnet add package Serilog.AspNetCore

之后,我们必须如下配置我们的 Program.cs

 public class Program
    {
        public static async Task Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.Console(new ElasticsearchJsonFormatter() { })
                .CreateLogger();

            await BuildWebHost(args).Build().RunAsync();
        }

        public static IWebHostBuilder BuildWebHost(string[] args)
        {
            return WebHost.CreateDefaultBuilder(args)
                .ConfigureLogging((hostingContext, config) => { config.ClearProviders(); })
                .UseKestrel()
                .UseSerilog()
                .UseStartup<StartUp>();
        }
    }

您可以使用以下 Deployment YAML
Service YAML
在您的 k8s
集群或 minikube
环境中运行 dockerized
来演示应用程序。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fluent-demo-k8s
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: fluent-demo-k8s
    spec:
      containers:
      - name: fluent-demo-k8s
        imagePullPolicy: Always
        image: fluent-demo-k8s-image
        ports:
        - containerPort: 8080


apiVersion: v1
kind: Service
metadata:
  name: fluent-demo-k8s
  labels:
    run: fluent-demo-k8s
spec:
  ports:
  - port: 8080
    protocol: TCP
  selector:
    app: fluent-demo-k8s
  type: NodePort

使用以下命令在 k8s
环境中运行我们的 dotnet
应用程序。

kubectl create -f deployment.yml 
kubectl create -f service.yml

您可以使用kubectl get svc
命令列出服务并获取映射到 8080
的端口。为了从浏览器访问我们的应用程序,运行kubectl cluster-info
命令并获取运行 k8s master
IP
地址,并使用映射服务端口的 pod
访问它。

(示例:k8s master IP
192.168.57.80
,映射服务端口为 32077
。您可以从 => http://192.168.57.80:32077
访问该应用程序)

现在是时候在 k8s
上安装 Fluent Bit
作为守护进程了。首先,我们将创建一个名为logging
的新命名空间。

apiVersion: v1
kind: Namespace
metadata:
  name: logging

kubectl create -f namespace.yml


接下来,我们将创建一个名为fluent-bit
的服务帐户并为 pod
提供身份。


apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluent-bit
  namespace: logging

kubectl create -f serviceaccount.yml

我们需要定义集群角色并将这个角色绑定到fluent-bit
服务帐户。我们可以使用下面的 YAML
文件进行这些操作。

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: fluent-bit-read
rules:
- apiGroups: [""]
  resources:
  - namespaces
  - pods
  verbs: ["get""list""watch"]

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: fluent-bit-read
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: fluent-bit-read
subjects:
- kind: ServiceAccount
  name: fluent-bit
  namespace: logging

kubectl create -f clusterrole.yml 
kubectl create -f clusterrolebinding.yml


之后,我们需要为 Fluent Bit
创建ConfigMap
, 它允许我们将特定于环境的配置与我们的容器镜像分离。我需要在下面的配置文件中解释一些参数。在[INPUT]
部分,我们使用Fluent Bit
的尾部插件,它允许我们监视多个文本文件并读取Path
模式中的每个匹配文件。

Kubernetes
将容器日志文件存储在/var/log/containers
中,这些日志文件与 Path
属性映射。使用Tag
Tag_Regex
配置,我们正在设置一个标签,该标签将放置在 read
regex
行上,以从文件名中提取字段。在[输出]
部分,我们使用转发插件将捕获的日志发送到 Fluentd
。至少在 [PARSER]
部分,我们正在解析事件记录中的字段。(这部分我尝试简单解释一些配置参数。详细文档请看这里。)

Fluent Bit
的尾部插件: https://docs.fluentbit.io/manual/pipeline/inputs/tail

配置参数: https://docs.fluentbit.io/manual/concepts/data-pipeline

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: logging
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         5
        Log_Level     info
        Parsers_File  parsers.conf
        Daemon        off
        
    @INCLUDE all_container_input.conf
    @INCLUDE output-fleuntd-forward.conf
  all_container_input.conf: |
    [INPUT]
        Name              tail
        Tag               <container_name>-<namespace_name>
        Tag_Regex         (?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace_name>[^_]+)_(?<container_name>.+)-
        Parser            docker
        Path              /var/log/containers/*
        DB                /var/log/flb_kube.db
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On
        Refresh_Interval  5
        Docker_Mode       On
  output-fleuntd-forward.conf: |
    [OUTPUT]
        Name          forward
        Match         *
        Host          ${FLUENTD_HOST}
        Port          ${FLUENTD_PORT}
        Retry_Limit   False
  parsers.conf: |
    [PARSER]
        Name        json
        Format      json
        Time_Key    time
        Time_Format %d/%b/%Y:%H:%M:%S %z
    [PARSER]
        Name        docker
        Format      json
        #Time_Key   time
        Time_Key    @timestamp
        Time_Format %Y-%m-%dT%H:%M:%S.%L
        Time_Keep   Off # on

kubectl create -f configmap.yml

日志记录是一个跨领域的问题。最好在插件组件中完成 - Robert C. Martin

最后,我们需要让 Fluent Bit
Kubernetes
中作为DaemonSet
运行。这确保所有节点都运行 Fluent Bit pod
的副本。另外,我们需要在这个文件中定义Fluentd
服务器的IP
地址和端口信息作为环境变量。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
  labels:
    k8s-app: fluent-bit-logging
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      name: fluent-bit
  template:
    metadata:
      labels:
        name: fluent-bit
        k8s-app: fluent-bit-logging
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - name: fluent-bit
        image: fluent/fluent-bit:1.6.0
        imagePullPolicy: Always
        ports:
          - containerPort: 2020
        env:
        - name: FLUENTD_HOST
          value: "127.0.0.1" #YOUR FLUENTD HOST IP
        - name: FLUENTD_PORT
          value: "24224"        
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluent-bit-config
          mountPath: /fluent-bit/etc/
      terminationGracePeriodSeconds: 10
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluent-bit-config
        configMap:
          name: fluent-bit-config
      serviceAccountName: fluent-bit
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule

kubectl create -f daemonset.yml

您还可以在单个清单文件中编辑和应用上述所有配置。我更喜欢使用单独的文件来使一切变得更简单易懂。

到目前为止一切顺利,但我们还有一些工作要做。我们必须配置我们的 Fluentd
来捕获 Fluent Bit pod
转发的日志。之后,Fluentd
将处理、过滤、聚合这些日志,并将它们发送到 Elasticsearch
。坚持住,我们快完成了!

在这一部分,我们将使用td-agent
Treasure Agent(td-agent
)是Fluentd
的一个稳定分发包,由Treasure Data
和云原生计算基金会共同维护。(td-agent
Fluentd
之间有什么区别?另外, 我假设你已经安装了Fluentd
)因为我们使用td-agent
包安装 Fluentd
,配置文件应该在/etc/td-agent/td-agent.conf
(如果你使用 Ruby Gem
docke
r 容器安装 Fluentd
,你应该有看这里) Fluentd
假设我们的配置文件具有 UTF-8
ASCII
编码。这是我们的示例td-agent.conf
文件:

Treasure Data: https://www.treasuredata.com/

云原生计算基金会共同维护: https://www.cncf.io/projects/

td-agent 和 Fluentd 之间有什么区别?: https://www.fluentd.org/faqs

安装了Fluentd: https://docs.fluentd.org/installation

# source: where all the data come from
# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
  @type forward
  bind 0.0.0.0
  port 24224
</source>

#filter: Event processing pipeline
<filter *.**>
  @type parser
  key_name log
  reserve_data true
  remove_key_name_field true
  emit_invalid_record_to_error false
  <parse>
    @type json
  </parse>
</filter>

# kube-system related logs excluded
<match *kube-system**>
  @type null
</match>

# match: Tell fluentd what to do with other logs!
<match *.**>
  @type elasticsearch
  @log_level info
  include_tag_key true
  host 127.0.0.1 #YOUR ELASTICSEARCH HOST
  port 9200 #DEFAULT ELASTICSEARCH PORT
  scheme http
  ssl_verify false
  logstash_format true
  logstash_prefix ${tag}-k8s
  reload_connections false
  reconnect_on_error true
  reload_on_failure true
  request_timeout 15s
  flush_interval 10s
 <buffer>
    timekey 10s
    flush_thread_count 5
    flush_interval 10s
    chunk_limit_size 16m
    queue_limit_length 96
    flush_mode interval
    retry_max_interval 30
    retry_forever false
    flush_at_shutdown true
  </buffer>
</match>

我们试着解释一下配置文件中的一些参数。source
指令确定输入源。match
指令确定输出目的地。filter
指令确定事件处理管道。(更详细的解释在这里)在这个简短的解释之后,通过运行以下命令,我们必须通过 systemd
重新加载我们的 td-agent
服务。(如果您遇到任何问题,您可以查看日志 /var/logs/td-agent/td-agent.log

更详细的解释在这里: https://docs.fluentd.org/configuration/config-file

sudo systemctl reload td-agent

我们的日志应该已经开始流入 Elasticsearch
。让我们在 Kibana
上查看它们。点击management
-> stack management
Kibana
侧边栏。在打开的屏幕上选择索引模式。(在某些安装中,您可能会发现此部分带有 management
-> index patterns

当我们切换到 Kibana
上的 Discovery
部分时,我们可以查看我们的日志。

结论

与软件世界中的任何解决方案一样,这种结构也需要权衡取舍。Fluent Bit
可以收集、处理和过滤所有日志,并将它们直接转发到 Elasticsearch
。在这种情况下,当系统负载过重时,这可能会导致我们的Kubernetes
集群成为资源消耗的瓶颈。即使 Fluent Bit pod
出现问题也可能导致我们的 Kubernetes
集群崩溃。这就是为什么我们减少了 Fluent Bit
的责任,并赋予 Fluent Bit
转发日志的任务,因此我们在 Kubernetes
集群中节省了大量内存使用。作为回报,我们遇到了 Fluentd
,我们必须对其进行组织和维护。

Fluent Bit
Fluentd
是有据可查的解决方案。因此,如果您遇到任何问题,我认为您可以轻松解决它。为了不让文章复杂化,我没有提到一些安装(Fluentd & ELK
)。您可以在此处找到一个简单的演示示例。

https://github.com/senvardarsemih/fluent-logging-demo

如果您已经阅读了到目前为止,我感谢您的耐心和支持。我希望它对你有用。

进一步阅读[1]

参考资料

[1]

参考地址: https://medium.com/hepsiburadatech/fluent-logging-architecture-fluent-bit-fluentd-elasticsearch-ca4a898e28aa

文章转载自云原生CTO,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论