基于filebeat二次开发Kubernetes日志采集

目前最为主流的容器编排工具主要有kubernetes、mesos、swarm,个人不评价谁好谁坏因为每个东西都有自己的优势。不过个人认为目前关注度最高的应该当属kubernetes,现在越来越多的公司采用kubernetes作为底层编排工具开发自己的容器调度平台。既然是一个PAAS平台那么就应该提供一个计算监控等一体的服务,因为是在kubernetes运行上面的容器大多数都是无状态服务,所以统一的日志管理又是其中必不可少的一部分。下面我们就讲一下如何基于filebeat开发属于自己的日志采集。

目前用的最多的日志管理技术应该是ELK,E应该没有太多的疑问基本上很多公司都是采用的这个作为存储索引引擎。L及logstash是一个日志采集工具支持文件采集等多种方式,但是基于容器的日志采集又跟传统的文件采方式略有不同,虽然docker本身提供了一些log driver但是还是无法很好的满足我们的需求。现在kubernetes官方有一个日志解决方案是基于fluentd的。至于为什么最后选择采用filebeat而没有用fluentd主要有一下几点:

  • 首先filebeat是go写的,我本身是go开发,fluentd是ruby写的很抱歉我看不太懂
  • filbeat比较轻量,filbeat现在功能虽然比较简单但是已经基本上够用,而且打出来镜像只有几十M
  • filbeat性能比较好,没有具体跟fluentd对比过,之前跟logstash对比过确实比logstash好不少,logtash也是ruby写的我想应该会比fluentd好不少
  • filbeat虽然功能简单,但是代码结构非常易于进行定制开发
  • 还有就是虽然用了很久fluentd但是fluentd的配置文件实在是让我很难懂

filebeat如何采集kubernetes日志

所以基于以上几点决定采用filebeat开发了自己的日志采集。
filebeat的Github地址是https://github.com/elastic/beats里面囊括了好几个项目其中就包括filebeat。

和其他的日志采集处理一样filebeat也有几个部分分别是input、processors、output,不过filebeat提供的能力还比较少,不过无所谓够用就好。

filebeat提供了一个add_kubernetes_metadata的processor,文件的采集路径就要配成/var/lib/docker/containers/*/*-json.log主要是监听kubernetes的apiserver把容器对应的pod的信息存到内存里面,从文件日志source里面(就是上面的那个路径)里面获取容器id匹配得到pod的信息。
因为json.log文件里面的日志都是json格式的所以需要对日志进行json格式化,filebeat有一个processor叫decode_json_fields这些processor都支持条件判断,可以通过条件判断来绝对是否要对某一条日志进行处理。filebeat默认的日志字段是message但是*-json.log解析出来以后的日志字段是log,如果同时配置了其他的日志采集这个时候所用的存储日志的字段就不一样了,所以需要对它们进行处理让它们使用同一个字段,但是filebeat并没有提供这个功能所以自己写了一个add_fields的功能。

整理后的配置文件如下:

filebeat.prospectors:
- type: log
  paths:
    - /var/lib/docker/containers/*/*-json.log
    - /var/log/containers/applogs/*

processors:
- add_kubernetes_metadata:
    in_cluster: false
    host: "127.0.0.1"
    kube_config: /root/.kube/config
- add_fields:
    fields:
      log: '{message}'
- decode_json_fields:
    when:
       regexp:
         log: "{*}"
    fields: ["log"]
    overwrite_keys: true
    target: ""
- drop_fields:
     fields: ["source", "beat.version", "beat.name", "message"]
- parse_level:
     levels: ["fatal", "error", "warn", "info", "debug"]
     field: "log"

logging.level: info
setup.template.enabled: true
setup.template.name: "filebeat-%{+yyyy.MM.dd}"
setup.template.pattern: "filebeat-*"
#setup.template.fields: "${path.config}/fields.yml"
setup.template.fields: "/fields.yml"
setup.template.overwrite: true
setup.template.settings:
   index:
     analysis:
       analyzer:
         enncloud_analyzer:
           filter: ["standard", "lowercase", "stop"]
           char_filter: ["my_filter"]
           type: custom
           tokenizer: standard
       char_filter:
         my_filter:
           type: mapping
           mappings: ["-=>_"]

output:
  elasticsearch:
    hosts: ["127.0.0.1:9200"]
    index: "filebeat-%{+yyyy.MM.dd}"

如果线上环境filebeat也是以daemonset的方式运行在kubernetes集群里面,所以in_cluster就需要设置成true,对应的kube_config则不需要配置了,host参数则是监听的某一个节点的pod,所以这个值应该是filebeat运行所在节点的pod的名称,当然也可以不写,那样的话就是监听全局的pod,不过这个对于filebeat来说是没必要的也是不好的。

add_fieldsprocessor可以添加自己想要的字段,值可以是字符串也可以是{message}格式,如果是这种格式则会从已有的字段里面取值进行填充。

parse_levelprocessor是用于一个匹配日志格式的功能,如果日志文件最前面出现的那个日志级别则这个日志加一个相应级别的字段。

filebeat还有对于template处理的功能的功能可以指定所用的mapping。

开发filebeat processor

使用的过程中主要是针对一些不满足的processor进行了开发,filebeat的代码结构非常清晰抽象也很好,可以很简单的进行开发。
filebeat的processor功能主要放在libbeat和filbeat同级的目录下,在这个目录下就叫processors。可以看到里面有actions,add_cloud_metadataadd_kubernetes_metadataadd_docker_metadata所以filebeat也只支持直接docker的processor的,比较普通的processor都是放在actions下面的所以如果我们需要开发一些简单的processor的话可以直接放到下面,包括decode_json和drop_event等也是放在下面的。以add_field为例:

package actions

import (
    "fmt"
    "regexp"
    "strings"

    "github.com/elastic/beats/libbeat/beat"
    "github.com/elastic/beats/libbeat/common"
    "github.com/elastic/beats/libbeat/processors"
)

type addFields struct {
    Fields map[string]string
    reg    *regexp.Regexp
}

func init() {
    processors.RegisterPlugin("add_fields",
        configChecked(newAddFields,
            requireFields("fields"),
            allowedFields("fields", "when")))
}

func newAddFields(c *common.Config) (processors.Processor, error) {
    config := struct {
        Fields map[string]string `config:"fields"`
    }{}
    err := c.Unpack(&config)
    if err != nil {
        return nil, fmt.Errorf("fail to unpack the add_fields configuration: %s", err)
    }

    f := &addFields{Fields: config.Fields, reg: regexp.MustCompile("{(.*)}")}
    return f, nil
}

func (f *addFields) Run(event *beat.Event) (*beat.Event, error) {
    var errors []string
    for field, value := range f.Fields {
        matchers := f.reg.FindAllStringSubmatch(value, -1)
        if len(matchers) == 0 {
            event.PutValue(field, value)
        } else {
            if len(matchers[0]) >= 2 {
                val, err := event.GetValue(strings.Trim(matchers[0][1], " "))
                if err != nil {
                    errors = append(errors, err.Error())
                } else {
                    event.PutValue(field, val)
                }
            }
        }
    }
    return event, nil
}

func (f *addFields) String() string {
    var fields []string
    for field, _ := range f.Fields {
        fields = append(fields, field)
    }
    return "add_fields=" + strings.Join(fields, ", ")
}

需要定义自己的struct, newAddFields方法通过配置文件初始化自己的struct。并在init里面通过RegisterPlugin把自己的processor注册进去。这个struct主要是要实现Run方法,这个方法就是对于每一条日志event的具体处理。

到这就基本上实现了对接kubernetes的对接改造就基本上完成了,当然还有其他很多工作可以做,比如golang本身的regex和encoding/json性能比较差,这些都是可以优化的地方。

我自己fork出来的地址是https://github.com/yiqinguo/beats增加了Makefile直接编译打镜像,和filebeat-ds.yml直接发到kubernetes集群里面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 141,390评论 1 297
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 60,682评论 1 254
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 93,205评论 0 211
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 40,718评论 0 171
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,406评论 1 250
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,590评论 1 170
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,271评论 2 267
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,052评论 0 163
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,801评论 6 227
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,404评论 0 212
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,176评论 2 212
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,489评论 1 222
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,156评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,031评论 2 210
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,402评论 3 201
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,583评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 25,927评论 0 163
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,352评论 2 228
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,478评论 2 229

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 133,994评论 18 139
  • kubernetes 简介 一个迅速过一遍kubernetes 非常不错的资源:基于Kubernetes构建Doc...
    bradyjoestar阅读 15,225评论 2 7
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,059评论 6 345
  • 前言 调研了ELK技术栈,发现新一代的logstash-forward即Filebeat,使用了golang,性能...
    大数据之心阅读 13,039评论 10 40
  • 曦和永远都会记得,月光下白衣老人的箫声,幽咽绵长。曾在莫斯科相依相伴未能相守的素素,她知道,莫斯科晚上的月光很凉,...
    苏长亭阅读 1,088评论 11 25