• kubernetes日志收集 fluent-operator 动态索引名的实现


    发自个人博客: https://uublog.com/article/20230510/kubernetes-fluent-operator-dynamic-index-name/

    日志收集,考虑到我们没有主动上报日志,格式也不太统一,容器化的时候,对于我们而言采集容器标准输出的日志是更快捷达到目的的方式。

    大致对比了filebeat、fluentd、fluentbit、vector、logstash后,锁定了flientbit。

    虽然它功能不是最强大的,但是基本功能能满足我们的业务需求,采集输入端多样化,也能做简单的filter,支持output的端也很丰富。其次,它吃资源比较少,性能也不错。

    我们只需要它收集标准输出的日志,输出到es即可。

    而在k8s中,有kubesphere共献给fluent的fluent-operator。该opeator提供了fluentbit only、fluentd only、fluentbit+fluentd 这几种采集方式。对于我们而言,就fluentbit 就够了。

    我选择了helm方式安装,下载了它chart (https://github.com/fluent/fluent-operator/releases/download/v2.2.0/fluent-operator.tgz)包,解压到/app/fluent-operator/ ,直接修改里面values.yaml,这样好方便未来管理自己的部署配置。

    简单按照文档,修改了fluentbit的input和output后,直接安装即可

    helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd
    
    
    • 1
    • 2

    按照服务名区分索引名

    日志是能收集了,但是所有的服务的日志,当天都放到一个索引,类似 “k8s-logs-” 这样的索引中。本地还好,如果生产环境的话,那一天单个索引就十分庞大了。而且对于不同服务日志保留天数做差异化保留的时候也不好处理,也不好直观展示各个服务日志大小。

    所以能按照k8s--格式的话,对我们会更为理想点。截止目前的版本,没有现成的配置可以实现这个。经过研究,可以利用fluentbit的 logstash_prefix_key 这个来实现。logstash_prefix是固定的前缀,logstash_prefix_key则可以动态读取 key来作为索引名。

    1.修改fluent-operator,让其支持logstash_prefix_key

    经过测试,fluent-operator当前版本的模板并没有对该字段进行处理,所以要进行修改让它支持这个字段。

    我需要output到es,所以我就修改了es相关的output,其它输出源的可以自己检查下。

    修改 ./templates/fluentbit-output-elasticsearch.yaml

    在Values.fluentbit.output.es.logstashPrefix之后增加 .Values.fluentbit.output.es.logstashPrefixKey 这段的配置,如下:

    {{- if .Values.fluentbit.output.es.logstashPrefix }}
        logstashPrefix: {{ .Values.fluentbit.output.es.logstashPrefix | default "ks-logstash-log" | quote }}
    {{- end }}
    {{- if .Values.fluentbit.output.es.logstashPrefixKey }}
        logstashPrefixKey: {{ .Values.fluentbit.output.es.logstashPrefixKey | default "ks-logstash-log-key" | quote }}
    {{- end }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.让它能获取app name作为服务名

    ./values.yaml 修改fluentbit部分,开启kubernetes labels,因为labels的app标签就是我们的服务名。

    filter:
      kubernetes:
        enable: true
        labels: true
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 拼接索引名

    由于logstash_prefix_key只能接受 key,且不支持嵌套的对象的key。

    举个直观的例子,假设你的对象是这样的:

    {
      "file": "systemd.log",
      "kubernetes": {
         "labels": {
            "app": "demo-service"
         }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    k8s-$kubernets["labels"]["app"] 和 像 $kubernets["labels"]["app"] 这样的值,是取不到任何内容的。像上面的例子,只接受 $file 这个值。

    所以这里要实现我们的目标,我们得自己拼接出一个新的key,作为索引名。类似下面:

    {
      "file": "systemd.log",
      "app_name": "k8s-demo-service",
      "kubernetes": {
         "labels": {
            "app": "demo-service"
         }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我是这样实现的,利用lua的filter,通过取出kubernetes的labels,拼接新的值。

    我修改的是 cat ./templates/fluentbit-containerd-config.yaml 增加了一个 add_k8s_app_name_field 函数

    {{- if .Values.Kubernetes -}}
    {{- if .Values.fluentbit.enable -}}
    {{- if .Values.fluentbit.filter.containerd.enable -}}
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: fluent-bit-containerd-config
    data:
      containerd.lua: |
        function containerd( tag, timestamp, record)
               if(record["logtag"]~=nil)
               then
               timeStr = os.date("!*t",  timestamp["sec"])
                t = string.format("%4d-%02d-%02dT%02d:%02d:%02d.%sZ",
                timeStr["year"], timeStr["month"], timeStr["day"],
                timeStr["hour"], timeStr["min"], timeStr["sec"],
                timestamp["nsec"]);
                record["time"] = t;
                record["log"] = record["message"];
                record["message"] =  nil;
                return 1, timestamp, record
                else
                return 0,timestamp,record
               end
        end
    
        function add_k8s_app_name_field(tag, timestamp, record)
            retcode = 0
            prefix = 'k8s' 
            app_name = record['kubernetes']['labels']['app']
            if  app_name ~= nil then
                app_name = prefix .. '-' .. app_name
                
                if app_name ~= nil then
                    record['app_name'] = app_name
                    retcode = 2
                end
            end
            
            return retcode, timestamp, record
        end
    {{- end }}
    {{- end }}
    {{- end }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    修改 templates/fluentbit-clusterfilter-kubernetes.yaml 增加新增的lua filter函数 对kubernetes标签进行处理

    cat templates/fluentbit-clusterfilter-kubernetes.yaml

    {{- if .Values.Kubernetes -}}
    {{- if .Values.fluentbit.enable -}}
    {{- if .Values.fluentbit.filter.kubernetes.enable -}}
    apiVersion: fluentbit.fluent.io/v1alpha2
    kind: ClusterFilter
    metadata:
      name: kubernetes
      labels:
        fluentbit.fluent.io/enabled: "true"
        fluentbit.fluent.io/component: logging
    spec:
      match: kube.*
      filters:
      - kubernetes:
          kubeURL: https://kubernetes.default.svc:443
          kubeCAFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          kubeTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
          {{- $params := omit .Values.fluentbit.filter.kubernetes "enable" }}
          {{- if .Values.fluentbit.output.stdout.enable }}
          {{- $_ := set $params "k8sLoggingExclude" true -}}
          {{- end }}
          {{- with $params }}
          {{- . | toYaml | nindent 6 }}
          {{- end }}
      - lua:
          script:
            key: containerd.lua
            name: fluent-bit-containerd-config
          call: add_k8s_app_name_field
          timeAsTable: true
      - nest:
          operation: lift
          nestedUnder: kubernetes
          addPrefix: kubernetes_
      - modify:
          rules:
          - remove: stream
          - remove: kubernetes_pod_id
          - remove: kubernetes_docker_id
          - remove: kubernetes_container_hash
          - remove: kubernetes_labels
      - nest:
          operation: nest
          wildcard:
          - kubernetes_*
          nestUnder: kubernetes
          removePrefix: kubernetes_
    {{- end }}
    {{- end }}
    {{- end }}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    主要增加了

    - lua:
        script:
          key: containerd.lua
          name: fluent-bit-containerd-config
        call: add_k8s_app_name_field
        timeAsTable: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    到这里基本就满足所有的条件了,为了不影响systemd的日志收集和归类,也给它的lua filter增加app_name字段.

    ./templates/fluentbit-lua-config.yaml

    new_record["app_name"] = "systemd"
    
    • 1

    经过前面的修改后,只要在values.yaml 设置 fluentbit.output.es.logstashPrefixKey=”$app_name” 即可

    4. 应用变更

    我修改过的values.yaml,去除了无关部分后主要如下:

    Kubernetes: true
    fluentbit:
      crdsEnable: true
      enable: true
      image:
        repository: "hub.xxxx.com/library/fluent-bit"
        tag: "v2.0.11"
      input:
        tail:
          enable: true
          refreshIntervalSeconds: 10
          memBufLimit: 50MB
          path: "/var/log/containers/*.log"
          skipLongLines: false
        systemd:
          enable: true
          path: "/var/log/journal"
          includeKubelet: true
      output:
        es:
          enable: true
          # 如果多个host的话,用hosts
          host: "es.xxx.local"
          port: 9200
          logstashFormat: true
          logstashPrefixKey: "$app_name"
      filter:
        kubernetes:
          enable: true
          labels: true
          annotations: false
          k8sLoggingExclude: true
        containerd:
          enable: true
        systemd:
          enable: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    再来helm更新一下,搞定

    helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd
    
    • 1

    效果如下:

    yellow open k8s-kiali-2023.05.10                iWEHK0gKR6GoPKjO3gH6Eg 1 1      30      0 105.9kb 105.9kb
    yellow open k8s-nginx-deploy-2023.05.10         OCzPIKWgRneSn27J-o4k9g 1 1      16      0  76.3kb  76.3kb
    yellow open k8s-istiod-2023.05.10               GlA1dI7aQDqPFD-5ZMX6iw 1 1     797      0 329.5kb 329.5kb
    yellow open k8s-reviews-2023.05.10              41Ifiq3cTQGBKdJ4Fn2MJQ 1 1      18      0  86.6kb  86.6kb
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    Linux-基本指令03
    51单片机入门——数模\模数转换
    C# 向us7ascii编码的oracle数据库插入中文数据????问号乱码的解决方案
    Python并发编程之进程间通信
    [附源码]Python计算机毕业设计Django基于协同过滤的资讯推送平台
    把jar包打成docker镜像并推送到Docker Hub
    mybatis入门
    2022-08-01 田龙跃 学习笔记
    mysql 删除索引 索引长度还在,数据内存 索引内存没有减少
    ES & Kibana 安装
  • 原文地址:https://blog.csdn.net/MichaelJScofield/article/details/136214832