云优采集接口(网易杭州研究院云计算技术部高级研发工程师(组图))
优采云 发布时间: 2021-11-05 10:08云优采集接口(网易杭州研究院云计算技术部高级研发工程师(组图))
本文由作者授权发布,未经许可请勿转载。
作者:付毅,网易杭州研究院云计算技术部高级研发工程师
一、背景云原生技术大潮已经到来,技术变革迫在眉睫。
在这一技术趋势下,网易推出了青州微服务平台,集成了微服务、Service Mesh、容器云、DevOps等组件。在公司内部得到了广泛的应用,也支持了很多外部客户的云原生转型。和迁移。
其中,日志记录是一个通常被忽视的部分,但却是微服务和 DevOps 的重要组成部分。没有日志,就无法排查服务问题。同时,日志的统一采集也是很多业务数据分析、处理、审计的基础。
但是在云原生容器化环境中,日志 采集 变得有点不同。
二、容器日志的痛点采集传统主机模式对于传统的物理机或虚拟机部署服务,日志采集的作用很明显。
业务日志直接输出到主机,服务运行在固定节点上,手动或使用自动化工具在节点上部署日志采集agent,添加agent配置,即可启动采集 记录 NS。同时,为了方便后续的日志配置修改,还可以引入配置中心来发布代理配置。
在 Kubernetes 环境中,情况没有在 Kubernetes 环境中那么简单。
一个Kubernetes节点上运行着很多不同的服务容器,容器日志存储方式也有很多种,比如stdout、hostPath、emptyDir、pv等。 由于Pods的主动或被动迁移以及频繁的销毁和创建在 Kubernetes 集群中,我们不能像传统方式那样手动向每个服务发布日志 采集 配置。另外,由于日志数据采集会集中存储,所以在查询日志时需要根据Namespace、Pod、Container、Node,甚至容器的环境变量和Label的维度进行检索和过滤。
以上都是不同于传统日志采集配置方式的需求和痛点。原因是传统方法不是为Kubernetes设计的,无法感知Kubernetes,无法与Kubernetes集成。
经过近几年的快速发展,Kubernetes 不仅是容器编排的事实上的标准,甚至可以看作是新一代的分布式操作系统。在这种新型操作系统中,控制器的设计思想驱动着整个系统的运行。控制器的抽象说明如下图所示:
由于Kubernetes良好的扩展性,Kubernetes设计了自定义资源CRD的概念。用户可以自己定义各种资源,借助一些框架开发控制器,使用控制器将我们的期望变成现实。
基于这个思路,对于日志采集,哪个日志服务需要采集,需要什么样的日志配置是用户的期望,而这一切都需要我们开发一个日志采集控制器达到。
三、 探索和架构设计有以上解决思路。除了开发一个控制器之*敏*感*词*绕这个想法进行一些选择分析。
日志采集代理选择日志采集控制器只负责连接Kubernetes并生成采集配置,而不是真正的日志采集。市面上有很多日志采集代理,比如传统ELK技术栈的Logstash、CNCF毕业项目Fluentd、最近上线的Loki、beats系列的Filebeat等。下面我们来简单分析一下。
1.Logstash是基于JVM的,它的内存占用可以达到每分钟数百MB甚至GB,有点沉重,所以我们先排除了它。
2.Fluentd 以 CNCF 为后盾,看起来不错,插件也很多,但是基于 Ruby 和 C,对于青州团队的技术栈来说,还是让人拭目以待。虽然 Fluentd 也推出了纯粹基于 C 语言的 Fluentd-bit 项目,内存占用非常小,看起来很诱人,但是 C 语言的使用和无法动态重载配置的问题仍然没有关闭。
3.Loki 上线不久,目前功能有限,部分压测数据显示性能不是很好,暂且观望。
4.Filebeat属于Logstash、Kibana、Elasticsearch,轻量级日志采集agent,推出替代Logstash,基于Golang编写,完美契合青州团队的技术栈,实测性能和资源占用率均优方面,因此成为青州期刊采集代理的首选。
Agent集成方式对于log采集agent,Kubernetes环境下一般有两种部署方式。
1. sidecar 方法,与业务Container部署在同一个Pod中。这种方式中,Filebeat 只为业务Container 记录采集 日志,并且只需要配置Container 的日志配置,简单且隔离性好,但最大的问题是每个服务必须有一个Filebeat 到采集,而且通常一个节点上有很多Pod,内存等开销也不乐观。
2. 另一个也是最常见的是在每个节点上部署一个 Filebeat 容器。相比较而言,内存占用要小很多,对Pod也没有侵入性,更符合我们平时的使用习惯。同时,一般采用Kubernetes的DaemonSet部署,无需Ansible等传统自动化运维工具,部署和运维效率大大提高。所以我们优先使用Daemonset来部署Filebeat。
整体架构选择Filebeat作为日志采集代理。集成自研的日志控制器后,从节点的角度,我们看到的架构如下:
1. 日志平台将特定的 CRD 实例下发给 Kubernetes 集群,日志控制器 Ripple 负责来自 Kubernetes 的 List&Watch Pod 和 CRD 实例。
2. 通过 Ripple 过滤和聚合,最终生成一个 Filebeat 输入配置文件。配置文件描述了服务的采集Path路径、多行日志匹配等配置。同时,它也默认在日志元信息中配置PodName、Hostname等。
3.Filebeat 会根据 Ripple 生成的配置自动重新加载 采集 节点上的日志并将其发送到 Kafka 或 Elasticsearch。
由于 Ripple *敏*感*词* Kubernetes 事件,因此可以感知 Pod 的生命周期。无论 Pod 是否销毁或调度到任何节点,它仍然可以自动生成相应的 Filebeat 配置,无需人工干预。
Ripple可以感知Pod挂载的日志卷,无论是docker Stdout的日志,还是使用HostPath、EmptyDir、Pv来存储日志,都可以在节点上生成日志路径并告诉Filebeat去采集@ >.
Ripple 可以同时获取 CRD 和 Pod 信息,所以除了默认在日志配置中添加 PodName 等元信息外,还可以结合容器环境变量、Pod 标签、Pod Annotation 等来标记日志方便后续的日志过滤、检索和查询。
另外,我们在Ripple中加入了定时清理日志等功能,保证日志不丢失,进一步提升日志采集的功能和稳定性。
四、基于Filebeat的实用功能扩展。总的来说,Filebeat 可以满足大部分 logging采集 的需求,但还是难免有些特殊场景需要我们自定义 Filebeat。当然,Filebeat 本身的设计也提供了很好的扩展性。
Filebeat 目前只提供了 Elasticsearch、Kafka、Logstash 等几种类型的输出客户端,如果我们希望 Filebeat 直接发送到其他后端,我们需要自定义我们自己的输出。同样,如果您需要过滤日志或添加元信息,您也可以创建一个自制的处理器插件。
无论是添加输出还是编写处理器,Filebeat 提供了基本相同的总体思路。一般来说,有3种方式:
1.直接 Fork Filebeat 并在现有源代码上开发。output和processor都提供了类似Run、Stop等的接口,你只需要实现这类接口,然后在init方法中注册对应的插件初始化方法即可。当然,由于Golang中的init方法是在包导入的时候调用的,所以需要在初始化Filebeat的代码中手动导入。
2. 把Filebeat的main.go复制一份,导入我们自研的插件库,重新编译。本质上和方法1没有太大区别。
3.Filebeat 还提供了基于 Golang 插件的插件机制。需要将自研插件编译成.so共享链接库,然后在Filebeat启动参数中通过-plugin指定库的路径。但实际上,一方面,Golang 插件并不成熟和稳定。另一方面,自主开发的插件仍然需要依赖相同版本的libbeat库,也需要相同的Golang版本进行编译。坑可能比较多,所以不推荐。
如果想了解更多关于Filebeat的设计,可以参考这篇文章文章:容器日志采集 犀利工具:Filebeat深度分析与实践
为了支持各业务方的对接,我们目前扩展开发了grpc输出,支持多个Kafka集群的输出。
三维监控 但是,真正的难点是业务端实际使用后,采集找不到日志,多行日志配置或者采集二进制大文件导致各种问题比如 Filebeat OOM。我们在Filebeat和日志采集的综合监控上投入了更多的时间,例如:
1. 接入青州监控平台,具备磁盘IO、网络流量传输、内存使用、CPU使用、Pod事件告警等,保证完美的基础监控。
2. 新增日志平台数据全链路延时监控。
3.采集Filebeat自带的日志,通过该日志文件开始采集和采集结束时上报,避免每次配置时都需要SSH到各个节点查看日志故障排除。
4. 自研 Filebeat 导出器,接入 Prometheus,采集 报告自己的指标数据。
通过三维监控和增强,极大的方便了我们问题的排查,降低了运维和人工成本,也保证了服务的稳定性。
五、Golang 的性能优化以及相应的调优性能优化是一个永恒的话题。虽然“过早优化是万恶之源”,但实际开发过程中还是需要时刻保持优化意识。很多时候,我们看到了太多的GC原理、内存优化、性能优化,但是很多时候我们写完代码,完成一个项目的时候,却无从下手。实践是检验真理的唯一标准。因此,自己去调查和探索,是改善姿势、找到关键问题的最短途径。云原生日志采集系统也是如此。
好消息是,对于性能优化,Golang 贴心地为我们提供了三个关键:
1.去基准测试
2.去pprof
3.去追踪
这些键在日志 采集 性能优化场景中也是有效的。这是一个简单的例子。
以sync.Pool为例,sync.Pool一般用于临时对象的保存和复用,减少内存分配,减少GC压力。应用场景很多,比如号称比Golang官方Http快10倍的FastHttp,大量使用sync.Pool,Filebeat使用sync.Pool将批量日志数据聚合成批发送,当Nginx-Ingress-控制器渲染生成 Nginx 配置,同时使用 sync.Pool 优化渲染效率。青州的日志控制器 Ripple 也使用了 sync.Pool 来优化渲染 Filebeat 配置时的性能。
首先使用go benchmark来测试在没有使用sync.Pool的情况下通过go模板渲染Filebeat配置的方法。
你可以看到结果显示了每个执行方法的时间和分配的内存。
然后使用 go pprof 观察 go benchmark 生成的 profile 文件的整体性能数据。
go pprof其实有很多数据供我们观察,这里只展示内存分配信息。可以看出,基准测试在此期间申请了超过5G的内存。
接下来我们使用go trace查看压测过程中的goroutine、堆内存、GC等信息。
这里只截取了600ms到700ms的时间段,从图中可以清楚的看出100ms内发生了170次GC。
同样的方法和步骤,使用sync.Pool后压测结果。
分配的内存总量减少到160MB,同期GC次数也减少到5次,优化效果非常明显。
当然,Golang 的作用还不止这些。从Docker到Kubernetes,从Istio到Knative,基于Golang的开源项目已经成为云原生生态的主力军。Golang 的简单性和高效性也不断吸引新的项目采用它作为开发语言。
对于青州微服务平台,除了使用Golang编写Filebeat插件和开发日志的控制器采集之外,我们还有很多基于Golang的组件,比如Service Mesh、容器云等。
六、总结与展望在云原生时代,日志作为可观察性的一部分,是排查和解决问题的基础,也是后续大数据分析处理的开始。
在这个领域,虽然有很多开源项目,但仍然没有强大而统一的日志采集agent。或许,这一盛开的景象还会继续。因此,青州团队自研的日志代理Ripple的设计也提出了更多的抽象,保留了与其他日志代理接口的能力。未来我们计划支持更多的日志采集代理,打造更丰富、更健壮的云原生日志采集系统。