本文介绍了字节跳动云原生可观测团队在构建 OneAgent 方面的探索,涉及数据模型、流程管道、编排调度和构建体系等方面,还分享了 OneAgent 在字节跳动内部的几个应用案例,展示了其在数据采集效率、资源消耗和系统稳定性方面的优势。
字节跳动拥有海量的主机和微服务实例,涵盖物理机、虚拟机、容器等多种场景,每秒产生的千亿级别的可观测性数据,在可观测性数据采集和管道化处理等方面给技术团队带来了巨大挑战:
为了解决上述问题,技术团队决定构建 OneAgent 组件,提供 Logs、Metrics、Trace 和 Event 采集解决方案。
OneAgent 是字节可观测团队提供的新一代可观测性数据[M.T.L.E]采集和处理管道(DataPipeline),目标是让 OneAgent 成为集团和云上的首选可观测性基础设施,简化在云环境中接入可观测性系统的复杂度,同时让用户可以轻松地从可观测性数据中获取更多价值。
OneAgent 中的 One 指的是统一和复用,我们希望它作为一款可观测数据采集、预处理 Agent,能在系统中发挥以下作用:
通过调研了社区内现有的开源解决方案,我们最终决定和 iLogtail 社区达成了共建合作,采用开源 iLogtail 作为 OneAgent 的底座,同时将通用能力直接以开源的工作方式贡献回社区。
基于开源项目的框架,字节跳动云原生可观测团队打造的 OneAgent 本身分为 C++ 编写的 core 部分,和以 Go 为主的插件系统部分。
Core 是 iLogtail 的主体,主要负责 OneAgent 自身状态的管理,包括配置的加载和监听、流水线的启停、保存 CheckPoint、自监控、告警等,也包含了日志文件采集相关的逻辑。Go 插件系统则对接了多种可观测性生态,包括 Prometheus、OpenTelemetry、SkyWalking 等生态的诸多插件,大大拓宽了 iLogtail 的应用场景。
以语言为界,iLogtail 包含了 C++ 和 Go 两种流水线,两者之间可以通过 cgo 的方式来交互。
云原生可观测团队目前使用 OneAgent 的 Go 流水线来采集和处理 Metrics 和 Traces 数据,使用 C++ 流水线搭配 Go Flusher 插件做 Logs 文件采集。
在实际开发过程中,由于字节跳动拥有庞大的业务规模和复杂的业务场景,单纯的开源版本并不能完全满足现实需要。为此,我们对核心的数据模型、流水线、编排调度、构建体系都做了深度改造。
社区版本的 pipeline 是以日志作为底座的,使用 SLS-PB 数据模型。虽然也可以处理 Metrics、Traces 数据,但是都得先转化成日志,这往往会造成性能额外开销及兼容性问题。以下述 metric 为例,metric 的字段使用特殊约定的 key 存储,其中 metrics 的多个 labels 以固定格式拼接到一个字段中,value 则通过 string 格式存储。
{
"__name__":"net_out_pkt",
"__labels__":"cluster#$#ilogtail-test-cluster|hostname#$#master-1-1.c-ca9717110efa1b40|hostname#$#test-1|interface#$#eth0|ip#$#10.1.37.31",
"__time_nano__":"1680079323040664058",
"__value__":"32.764761658490045",
"__time__":"1680079323"
}
这带来一些问题:
为了解决上述问题,我们开发了基于新数据模型的 2.0 pipeline。2.0 Pipeline 中流动的每一条数据定义为 PipelineEvent,Metric、Trace、Log、Bytes,Profiling 等都是派生自 PipelineEvent 的具体事件,大幅提升了灵活性:
type PipelineEvent interface {
GetName() string SetName(string)
GetTags() Tags
GetType() EventType
GetTimestamp() uint64
GetObservedTimestamp() uint64
SetObservedTimestamp(uint64)
}
PipelineEvent 不但可以携带更丰富的信息,也更适合做 data pipeline 内的计算模型,大大提升了通用处理能力。
目前,我们已经把这项改造贡献给社区,社区也已经接受这个新的数据模型为核心处理模型,后续新的插件都将支持新的数据模型,存量的插件也在逐渐适配支持。
社区在 v1.4.0 之前要求插件必须放在项目主仓库内,但互联网时代,代码是企业最核心的资产之一,受限于字节跳动内部的安全合规要求,一些插件必须放在公司内部的私有仓库,不能贡献到开源社区。一般面对这种情况,我们只能 fork 一个新的仓库来增加一些内部专用的插件,但这会导致我们和社区上游渐行渐远,既也不能享受开源项目的新特性,也不利于把最终用户的反馈反哺社区。
针对这个问题,我们设计了一种产物构建机制,能够合并官方仓库和私有仓库的插件,一起构建出包。在方案上,我们重点参考了 OpenTelemetry Collector 的做法,使用 YAML 文件渲染成最终的 Go 文件。我们对仓库做了如下改造:
v1.4.0 之前,插件的引入都放在 plugins/all/all.go 下:
package allimport (
_ "github.com/alibaba/ilogtail/plugins/aggregator"
_ "github.com/alibaba/ilogtail/plugins/aggregator/baseagg"
...
)
v1.4.0 之后,项目主仓库添加 plugins.yml 文件,内置插件都加到这个文件里面进行注册,默认使用 plugins.yml 来生成内置插件的 all.go 。同时编译脚本支持传递多个 *_plugins.yml , 使用 external_plugins.yml 来加载外部仓库的插件,生成 external_all.go 文件。
plugin.yml:
plugins:
common:
- import: "github.com/alibaba/ilogtail/plugins/aggregator"
- import: "github.com/alibaba/ilogtail/plugins/aggregator/baseagg"
...
external_plugins.yml:
plugins: // 需要注册的plugins,按适用的系统分类
common:
- gomod: code.private.org/private/custom_plugins v1.0.0 // 必须,插件module
import: code.private.org/private/custom_plugins // 可选,代码中import的package路径
path: ../custom_plugins // 可选,replace 本地路径,用于调试
windows:
linux:project:
replaces: // 可选,array,用于解决多个插件module之间依赖冲突时的问题
go_envs: // 可选,map,插件的repo是私有的时候,可以添加如GOPRIVATE环境等设置
GOPRIVATE: *.code.org
git_configs: // 可选,map,私有插件repo可能需要认证,可以通过设置git url insteadof调整
url.https://user:[email protected]/user/.insteadof: https://github.com/user/
有了这个能力之后,各个公司能更自由地开发自己场景的插件,大大降低了开发门槛,大幅提升了开源项目的共建体验。
基于上述这两个特性,我们开发一系列 OneAgent 插件运用在不同场景,很好地支撑了业务的需求:
输入插件:
处理插件:
聚合插件:
输出插件:
拓展插件:
上述插件中的部分也已经贡献给开源社区。
本节将会介绍 3 个字节跳动使用 OneAgent 的案例。
字节跳动内部某业务的存储业务主要使用 BytedMetrics 1.0 SDK 进行打点,使用 Metrics Agent(ms2)和 Telegraf 作为指标采集的 Agent。这条采集链路过长并且资源消耗过高,并且产品侧指标存在采集延迟、指标丢点的问题。部分业务单个实例每分钟打点可达百万级,采集组件至多可以占用 16 核心,却仍然受限于性能问题,无法实时地上报监控指标,从而造成了产品侧指标采集延迟、丢点的问题。
ms2 是字节跳动自研的高性能指标采集 Agent,用 C++ 实现,主要用来聚合,清洗,降级数据以及投递数据,以 Daemonset 的形式部署在各个集群,终态会和 OneAgent 融合。详细介绍见文章:字节跳动百万级 Metrics Agent 性能优化的探索与实践
BytedMetrics SDK 1.0 是字节自研的第一代打点 SDK,依赖 ms2 做数据聚合。
BytedMetrics SDK 2.0 是字节自研的第二代高性能打点 SDK,具有数据聚合能力,性能领先 Prometheus SDK 2-5 倍。
Telegraf 是 InfluxData 公司开发的一款开源指标采集 Agent,用于主机指标和业务指标的采集,现在逐步被 OneAgent 替换。
为了解决上述问题,我们使用 OneAgent 作为新的指标采集 Agent,现阶段已经使用 OneAgent 平替 Telegraf,降低了采集 Agent 80%的 CPU 占用,解决了业务的监控丢点和延迟问题。
该业务用户的打点方式多种多样,涵盖了 BytedMetrics 1.0 SDK、BytedMetrics 2.0 SDK、InfluxDB SDK、Prometheus Exporter、OpenTelemetry SDK 等。过去需要部署多个 Agent 进行采集,这不仅导致了资源的浪费,还增加了运维成本。如今,OneAgent 已经能够接收除 BytedMetrics 1.0 SDK 以外的所有数据,成功收敛了数个采集 Agent。
随着业务逐渐接入 BytedMetrics 2.0 SDK,以及我们下一个阶段对 C++流水线的改造,最终 OneAgent 将取代 ms2,成为该业务唯一的可观测数据采集 Agent,从而进一步节省资源,这也是我们的最终目标。
字节跳动内部主流是 Go 微服务,主要使用侵入式的 BytedLogs SDK 来记录日志,SDK 通过 Unix Domain Socket 上报给本机的 TTLogAgent,可以实现无盘化采集日志。
TTLogAgent 是字节自研的日志采集 Agent,用 Go 实现,主要用来接收 Log SDK 上报的数据并发送到后端,同时也支持日志文件采集,日志降级,错误日志分流等功能,以 daemonset 的形式部署在各个集群,最终会被 OneAgent 替换。
Logs SDK 是字节自研的高性能日志 SDK,采用链式 API,性能属于业内第一梯队。
这也带来一些问题:
TTLogAgent 文件采集能力较弱,对日志的处理通用性也不够,不支持正则处理多行日志,无法开放给所有用户使用。在对比 TTLogAgent 与 iLogtail 的优劣之后,我们决定复用 OneAgent 的这部分能力,将 TTLogAgent 改造成 OneAgent 的一个插件。
OneAgent 采用了轮询(polling)与事件(inotify)并存的模式进行日志采集,既借助了 inotify 的低延迟与低性能消耗的特点,也通过轮询的方式兼顾了运行环境的全面性,可以实现毫秒级的日志采集的延迟控制,很好地应对字节跳动当前的日志压力。
改造后 TTLogAgent 变成了 OneAgent 的一个插件:
后续我们会把 TTLogAgent 的功能逐步迁移到 OneAgent,最终下线 TTLogAgent。
OneAgent 不但可以作为采集 Agent,也可以部署成服务作为 Proxy 或 Gateway。字节跳动内部部署了多个 OneAgent 服务,帮助用户接入不同的可观测性平台,实现内外一体的观测体验。
根据后续规划,未来我们会持续增强 OneAgent 能力,提升其稳定性和易用性。同时我们也会深度参与开源社区共建,和广大开发者共同打造领先的可观测数据采集器:
字节跳动百万级 Metrics Agent 性能优化的探索与实践:https://mp.weixin.qq.com/s/bl1HbC6ti6Pw2FGxgstfBw
节约资源、提升性能,字节跳动超大规模 Metrics 数据采集的优化之道:https://mp.weixin.qq.com/s/spHNCBWfgOCHSomLvp5aWA
OpenTelemetry Collector Connector: https://github.com/open-telemetry/opentelemetry-collector/blob/main/connector/README.md
云原生可观测团队
字节跳动云原生可观测(Cloud Native-Observability)团队提供日均数十 PB 级可观测性数据采集、存储和查询分析的引擎底座,致力于为业务、业务中台、基础架构建设完整统一的可观测性技术支撑能力。同时,团队也正通过火山引擎持续对外输出云上可观测技术能力。