跳到主要内容

16 篇博文 含有标签「APO」

查看所有标签

APO使用场景之:统一的指标采集展示

· 阅读需 11 分钟
Autopilot Observability
APO 向导式可观测性平台

Cover 图

可观测性领域中的指标一直都占有非常重要的地位。Prometheus生态目前已经是事实上的标准,但是实际用户在落地Prometheus的时候可能存在以下的问题:

  • 虽然生态中有各种成熟的Exporter,但是各种Exporter的安装配置相对而言比较繁琐,管理比较麻烦
  • 跨集群的指标数据汇聚相对而言比较麻烦,很多时候需要二次开发,没有简单配置即可工作的工具
  • Prometheus 原生数据存储在大数据量时不稳定,业界有着很好的类似VictorioMetrics方案,但是很多人还未尝试使用
  • 业界也存在过万好评的大屏,能够更好体现指标价值,对于很多用户而言可能并不了解

在APO中能够很好的解决以上的问题,已经将指标生态的各种产品进行很好的整合。

Grafana Alloy介绍

Alloy是Grafana 发布替代之前Grafana Agent的开源产品。

简单的官方介绍:

“Grafana Alloy 是一个开源的 OpenTelemetry Collector 发行版,内置 Prometheus 管道,并支持度量、日志、追踪和性能剖析。”

更为详细的官方介绍:

“Alloy 为 OTel、Prometheus、Pyroscope、Loki 以及许多其他指标、日志、追踪和分析工具提供了原生管道。此外,您可以使用 Alloy 管道执行各种任务,例如在 Loki 和 Mimir 中配置警报规则。Alloy 完全兼容 OTel Collector、Prometheus Agent 和 Promtail。您可以将 Alloy 作为这些解决方案的替代方案,或将其与多个收集器和代理结合成混合系统。您可以在 IT 基础设施的任何地方部署 Alloy,并将其与 Grafana LGTM 堆栈、Grafana Cloud 的遥测后端或任何其他供应商的兼容后端配对。Alloy 灵活多变,您可以轻松配置以满足本地部署、仅云部署或两者结合的需求。”

APO是如何使用Grafana Alloy

从Grafana Alloy的官方介绍中可以看出Alloy很强大,但APO并未使用Alloy所有的功能,主要使用以下两个功能:

  • 集成管理各种Prometheus的exporter的功能,有兴趣的朋友可以翻之前文章介绍了如何使用Alloy一键配置完成exporter的指标采集
  • 管道功能:跨云,跨集群,跨网段的指标采集之后要传输到统一可观测性后台展示

集成管理Prometheus各种exporter功能

通过简单配置即可完成exporter的配置、安装部署:比如通过以下的配置,即可实现ElasticSearch 的exporter的部署和采集

# 采集 elasticsearch指标
prometheus.exporter.elasticsearch "example" {
address = "http://<elasticsearch-url>:9200"
basic_auth {
username = USERNAME
password = PASSWORD
}
}

prometheus.scrape "mysql" {
targets = prometheus.exporter.elasticsearch.example.targets
forward_to = [otelcol.receiver.prometheus.default.receiver]
}

数据的管道功能

管道功能,数据可以通过OpenTelemetry的collector完成数据的跨集群、跨网络、跨云的传输。

数据流向:

Alloy(采集指标)-> Otel Collector (网络边界)->(网络边界) Otel Collector -> VictoriaMetric

管道功能核心的逻辑在于通过简单配置OTEL collector

  • recievier
  • exporter

配置示例:

边缘侧 Collector 配置(负责接收指标并发送到中心 Collector):

边缘侧 Collector 将通过 OTLP 接收指标数据,并通过 OTLP 发送到中心侧 Collector。

配置示例(边缘侧 Collector):

receivers:
otlp:
protocols:
grpc: # 支持 gRPC 和 HTTP 协议
http:

exporters:
otlp:
endpoint: "http://center-collector:4317" # 中心 Collector 的接收地址
metrics:
resource_to_telemetry_conversion:
enabled: true # 将资源级信息转换为 Telemetry 数据

service:
pipelines:
metrics:
receivers: [otlp] # 从应用接收 OTLP 格式的指标数据
exporters: [otlp] # 导出到中心 Collector

中心侧 Collector 配置(负责从边缘侧 Collector 接收指标并写入存储系统):

中心侧 Collector 将通过 OTLP 接收边缘侧 Collector 发来的指标数据,并将其导出到最终的存储后端。

配置示例(中心侧 Collector):

yaml


Copy code
receivers:
otlp:
protocols:
grpc:
http:

exporters:
prometheus:
endpoint: "http://prometheus:9090/metrics" # Prometheus 的接收地址
namespace: "otel_metrics"

service:
pipelines:
metrics:
receivers: [otlp] # 从边缘侧 Collector 接收 OTLP 格式的指标数据
exporters: [prometheus] # 导出到 Prometheus

配置说明:

1.边缘侧 Collector:

  • receivers: 使用 otlp 接收应用程序发送的指标数据,支持 gRPC 和 HTTP 协议。
  • exporters: 使用 otlp 导出数据,endpoint 是中心侧 Collector 的接收地址。

2.中心侧 Collector:

  • receivers: 使用 otlp 从边缘侧 Collector 接收指标数据。
  • xporters: 使用 prometheus 将数据导出到VictorioMetrics。

APO如何看待Alloy其它功能

  • Alloy集成Loki而来的日志能力,在实际使用日志场景中可能不够用,实际日志都要完成非结构化转化成结构化这一步骤,但是Loki在此方向并不擅长
  • Pyroscope等Continues Profiling的数据目前在OpenTelemetry生态并未完全成熟,即便能够使用Alloy完成数据的采集,但是如何传输,存储,展示都成为问题,还有很多问题等着解决

Alloy的exporter集成能力是经过grafana agent项目能力沉淀而来,坑相对而言比较少。APO在实际使用Alloy也踩了些坑,通过不断调整配置,相信未来也会越来越稳定。

VictorioMetrics的使用

VM已经成为很多公司存储指标的首选,主要是相比prometheus其它生态产品而言

架构简洁性:

  • VictoriaMetrics: VictoriaMetrics 集群版的架构较为简单,支持单一二进制文件启动,减少了复杂的集群管理工作。它既可以用作单机部署,也可以扩展为分布式集群,支持水平扩展,且维护相对简单。

  • Thanos/Cortex: 这两者的架构相对复杂,通常需要多个组件(如 Querier、Store Gateway、Compactor 等)协同工作,且往往涉及到对象存储(如 S3、GCS 等)来进行长期存储。因此,它们的配置、部署和维护难度较高,适合需要长时间数据保留的大规模集群。

高效存储和压缩:

  • VictoriaMetrics: 其高效的数据压缩和存储引擎使其在处理大量数据时更加节省存储空间。它采用自定义的存储格式和时间序列压缩算法,特别擅长处理大规模高频率的时间序列数据。

  • Thanos/Cortex: 这两者依赖于 Prometheus 的存储块和外部对象存储来处理长时间的数据保留,并通过外部系统进行压缩。虽然通过对象存储解决了长期存储问题,但这种方式带来的延迟和复杂性较高,尤其是在查询大量历史数据时,可能会受到网络和存储系统性能的影响。

性能和查询速度:

  • VictoriaMetrics: 由于其优化的存储引擎和索引机制,VictoriaMetrics 在长时间范围的查询场景中通常表现更好。它可以处理大规模数据的高性能写入和快速查询,即使在单节点场景下也能保持良好的表现。

  • Thanos/Cortex: 这两者的查询性能取决于集群的规模和外部存储的读写性能,尤其在跨多个 Prometheus 实例进行查询时,由于依赖对象存储,查询速度相对较慢。此外,Cortex 使用分区和多租户设计,虽然增强了灵活性,但在某些场景下也会引入查询延迟。

完全兼容 Prometheus API:

VictoriaMetrics 完全兼容 Prometheus 的查询语言(PromQL)和数据采集接口,能够无缝替代 Prometheus,且支持从 Prometheus、Thanos、InfluxDB 等系统中直接导入数据,迁移成本低。

指标的统一展示

当各种prometheus exporter的数据存储在VictorioMetrics之中,可以利用生态已有的Grafana大屏直接展示,感谢StarsLiao的贡献,在其贡献的大屏中,有很多已经成为众多公司的选择,很多大屏有着上万的好评。APO中很多大屏都引入了大佬的作品。

1 图

2 图


总结

APO利用Prometheus和OpenTelemetry的成熟生态成果,快速完成指标的采集、传输和统一展示。虽然这些能力并不是APO的核心价值,但也是可观测性平台的核心支柱能力,也欢迎用户先将APO当成指标的采集、传输和统一展示的工具,当系统越来越复杂,需要集成Trace、日志等能力之时,用户可以不用迁移平台。

APO介绍:

国内开源首个 OpenTelemetry 结合 eBPF 的向导式可观测性产品

apo.kindlingx.com

https://github.com/CloudDetail/apo

APO在一个页面整合关联可观测性数据的设计思路

· 阅读需 16 分钟
Autopilot Observability
APO 向导式可观测性平台

Cover 图

可观测性能力是系统在运行过程中,通过收集、关联和分析不同类型的数据,来理解和解释系统行为的能力。其目标不仅是发现问题,还要提供足够的信息来分析和解决这些问题,甚至在问题发生之前预见潜在的风险。

划重点:关联分析不同类型的数据,帮助用户理解和解释系统行为的能力是可观测性系统建设的关键目标。

可观测性数据不是简单将Trace、Metric、log,三者数据做在一个产品里面,三者仍然是割裂的数据。OpenTelemetry的出现给三者内在有机关联带来了更多可能性,如何关联这些数据并且呈现仍然有许多挑战。本文探讨APO团队对如何关联可观测性的设计思路,目标是能够在一个页面关联微服务接口所有故障排查需要的相关数据,完成故障的定界定位。

思路一:简单关联独立展示(帮助用户减少了登录次数)

最容易想到的关联方式,就是将三者数据分为三个Tab显示,每个tab只负责展示自身的数据,数据之间仍然缺少关联和提示。

如果用户要做关联查询经常要完成这样的操作:

  1. 在Trace 页中筛选出Trace信息,确认该Trace可能有问题,然后拷贝该TraceID,相关的IP信息,servicename,podname等相关信息也拷贝出来用来查询指标(有时还需要打命令才能查询出pod所对应的node在哪,在虚拟机里面可能还需要在cmdb根据IP查出node的唯一标识)
  2. 在Log页中,如果log已经输出了TraceID,可以通过TraceID搜索到相关的日志,如果日志未输出TraceID,就比较难以查询到日志
  3. 在Metric页面,根据servciename、podname、ip信息、node唯一标识完成指标的查询

思路二:简单关联但是数据串联(帮助用户减少了拷贝TraceID的时间)

在简单关联中,很容易进一步想到,能不能在展示Trace的时候,通过TraceID直接查看日志,而不用去拷贝TraceID至log页中查询。目前很多工具已经做到了这一步的关联,但是很多工具也就停留在这一步,在这个思路上其实还可以进一步关联,也就是将"思路一"所有可能要人为操作的功能,提前帮助用户查询好,用户可以沿着各种链接跳转至不同的数据当中。

很多可观测性平台按照"思路二"完成数据的串联之后就结束了,但是用户在使用过程中会容易出现以下的问题:

  • 缺少全局统计信息,从单个Trace出发,虽然能在不同ROOT SPAN中查看指标、日志等相关信息,但是由于没有统计信息,很容易一叶障目。为了让大家理解更深刻,举例说明即便没有任何故障,延时落在P50的Trace表现和延时落在P99的Trace表现相差很大。

  • 由于没有统计信息导致、确定故障根因节点困难。假设业务操作入口--"下单接口"出现了20%的错误率同比升高,下单接口正常时大概有1%的错误率,现在错误率升高了,仅仅分析出错的Trace可能并不能很好的分析出问题,因为很难确定者错误的Trace是新增错误,还是以往就有的错误。

怎么办?不能从局部去排查问题,而是应该以微服务接口(Service+URL)的方式去查看数据, 因为微服务接口有其黄金指标,可以很快判断微服务接口是否异常,如果异常,接下来需要做的是在关联各种可能需要查看的数据至该微服务接口详情页中,这样就可以有全局信息,快速判断该微服务接口是不是故障根因。


思路三:以微服务接口(Service+URL)为入口,更好统计信息更多的关联数据、减少以偏概全的风险

根据黄金指标的统计信息,可以很快判断哪些微服务接口是有问题的,比如同比延时高,同比错误率高。那接下来的问题就是点击微服务接口(Service+URL)详情之后,如何关联数据。

初步想法:可以将Trace页、Metric页、Log页作为独立tab集成至微服务接口的详情页中,接口层和应用层告警信息也能关联至详情页

这样在详情页中

Log页,可以提前过滤出该微服务接口的日志

Trace页,可以提前过滤出调用过该微服务接口的Trace

Metric页,由于微服务接口缺少实例等相关资源tag元数据,用户需要提前根据service,查出实例信息,然后查出Node信息,将实例和Node信息进行完整的展示

告警信息也可以关联进来,但是只能关联接口层和应用层面告警信息:比如Service实例应用级别的告警,比如延时、错误率、吞吐量、JVM告警等信息

进一步想法:提前将微服务接口的微服务实例和实例所属Node信息查询并关联,实例和Node之间的网络质量也可以关联进来

这样在详情页中,可以进一步显示:

Log页,可以提前过滤出该service+URL的日志

Trace页,可以提前过滤出service+URL的Trace

Metric页,微服务所有实例和所在Node的资源指标信息,所有建连的网络质量指标也可以关联进来

告警信息:除了应用层的告警信息,还可以关联资源层面的告警信息容器实例、node资源级别告警能被关联进来,用户对全局更有掌握

还有没有能够进一步的关联信息呢?能够缩小日志和Trace的排查范围,过滤出更容易让用户一击即中的日志和Trace呢?避免在海量的日志和Trace中不断试错

再进一步想法:分析相关的Trace,并提取Exception,关联时间段内所有Exception的日志信息,并展示Exception的传播链路

 图1

通过提前分析经过该微服务接口的trace,提取出所有的Exception信息,然后展示故障传播链路,并可以根据Exception信息关联含有该Exception的日志。同时提供日志出错的数量变化曲线,帮助用户更好的定位到底要查看哪些Trace和日志。在该tab中,通过时间轴选取的日志信息,全都含有Exception或者错误信息。

 图2 带有Exception的日志

这样用户排查日志和Trace的时候,是可以根据日志错误曲线、Exception种类信息导航至出错的日志和Trace,而不是查看所有的日志,或者搜索有Exception的日志,然后再去关联Trace一个一个查,从而帮助用户对错误有更深入的理解。传播链可以快速导航定位至下游依赖的服务接口。

微服务的接口详情页,还需要什么信息来辅助定界故障呢?

还进一步想法:根据URL级别拓扑,关联业务操作入口-快速实现故障影响面分析

很多可观测性工具只有应用级别的拓扑图,缺少URL级别的拓扑视图。应用级别的拓扑图其实是整个集群的业务执行拓扑,要从完整的拓扑中,区分出不同业务操作接口的执行路径有一定难度。

URL级别的拓扑能够反映某具体业务操作的执行路径。

业界王者Dynatrace的Service Flow本质上实现的就是URL级别的拓扑。

 图3 URL级别拓扑每个节点代表service+URL

 图4 同一个服务不同的URL会作为不同节点出现

URL级别拓扑结构的优势

1.精确的故障定位:

  • URL级别的拓扑结构允许你精确识别某个特定URL或API调用的故障及其在整个系统中的传播路径。这对于识别单个请求路径的性能问题、错误率或流量瓶颈尤其有用。

2.详细的依赖关系分析:

  • 通过URL级别的拓扑图,你可以看到每个请求如何穿过不同的服务和依赖组件。这有助于理解某个URL请求的依赖链条,从而识别哪个具体环节出现了问题。

3.更细粒度的影响分析:

  • URL级别的拓扑结构可以让你评估特定API调用或功能的影响范围,特别是在微服务架构中,不同的URL可能对应不同的服务或操作。这对于分析特定功能或业务逻辑的故障影响尤为关键。

基于URL级别拓扑结构的故障影响面分析

任意微服务接口,都存在于某业务操作的URL级别拓扑结构中,通过微服务接口逆查,就可以快速找到业务操作入口,然后可以根据业务操作入口的延时、错误率、吞吐量等同比指标快速判断业务操作入口有没有受到故障影响。

根据故障影响的严重程度,从而快速判断是否需要紧急介入,以及多少团队介入。(该功能将在APO9月版本迭代中发布)

基于URL级别拓扑结构关联中间件告警

根据URL级别拓扑接口,可以很清楚的判定某些中间件的告警是否和业务操作入口有关联,未来版本APO规划完成中间件指标监控之后,将中间件告警也关联进微服务接口详情页中,这样可以更好的判断微服务接口异常是否由于中间件告警而产生。

基于URL级别拓扑结构关联下游微服务接口告警

根据URL级别拓扑接口,可以很清楚的判定某些中间件的告警是否和业务操作入口有关联,未来版本APO规划关联下游微服务接口告警,这样提供多维信息判断是否需要排查下游接口,同时可以根据具体Trace信息来相互佐证,快速实现故障定界定位。

最后关联北极星指标完成延时问题的兜底

对于错误率上升的问题,通过关联exception和错误日志一般情况下能够实现对错误率上升故障的兜底解决。对于延时同比增加的问题,使用北极星指标一定能回答延时增加是由于什么原因导致的。关于北极星指标是什么,请参考链接 one.kindlingx.com

关联Trace和日志tab,帮助用户通过Trace和日志来佐证故障

当用户排查过以上的数据,基本上能回答告警影响面有多大,错误率上升和延时上升是什么原因了。通过快速查询Trace和日志可以用来佐证故障原因。


总结

APO是向导式可观测性产品,在一个页面关联了接口级的所有故障相关信息。

接口关联数据故障场景
接口自身的告警信息,应用层、资源层告警告警分析
接口的影响业务入口黄金指标影响面分析
接口的下游依赖告警关联级联告警影响分析
接口的实例和节点的资源指标饱和度分析
接口的网络指标网络质量分析
接口的代码Exception,以及含有Exception的日志错误闭环
接口执行的北极星指标延时闭环
接口执行的日志故障佐证
接口执行的trace故障佐证
接口所依赖的容器环境关键事件环境影响

APO介绍:

国内开源首个 OpenTelemetry 结合 eBPF 的向导式可观测性产品

apo.kindlingx.com

https://github.com/CloudDetail/apo

APO的告警关联和告警故障影响面功能介绍

· 阅读需 11 分钟
Autopilot Observability
APO 向导式可观测性平台

Cover 图

一般IT系统都会配置一系列告警来提醒运维或开发人员,系统存在问题。将告警进行分类之后,至少会有以下种类的告警:

  • 系统运行资源告警(CPU、内存、磁盘)
  • 网络质量告警
  • Kubernetes事件告警
  • 应用级别告警(延时、错误率、吞吐量)
  • 中间件告警

可观测性系统比较棘手的问题是告警噪音,告警噪音在某些场景是真实有效的,而在某些场景下又成为了噪音。APO团队认为告警噪音很难避免,但是可以通过在业务接口维度进行区分,快速聚焦到对业务产生影响的告警之上。同时APO还提供了故障影响面功能,用以判断告警到底对用户直接操作的入口业务有没有影响,如果有影响,需要确定什么级别的人员该被叫进来作为主导者,以便更快地调动资源恢复业务。

传统告警的处理方式

运维人员根据其经验设置告警规则,每种告警其实都是有意义的,如果没有意义,完全可以不设置该告警。

但在实际场景中,经常会出现告警噪音,也就是这种本应该产生警示作用的告警,实际上并没有实现其效果,反而成为了噪音。

我们必须要承认一个事实:告警噪音是很难避免的。

告警噪音:通常指的是那些对实际系统健康或业务影响不大的告警

这类告警的出现原因有多种,主要包括以下几方面:

1. 阈值设置不合理(很难设置准确,因为相同阀值情况下,不同业务、相同业务不同压力都可能产生不同的后果)

传统的告警系统依赖于静态阈值,当某些指标(如CPU、内存等)超过预设的阈值时会触发告警。若阈值设定过于严格或不够灵活,就会导致许多不具备实际影响的波动也触发告警。例如,短时间的CPU使用率上升可能只是某个后台任务的正常行为,正常情况下并不会影响系统的稳定性,但如果阈值设置过低,系统仍然会产生告警。但是也有可能某些业务的延时波动就是该波动导致的。

2. 指标波动过大(绝大多数情况下,即便是无效告警,也是一种信息提示)

一些系统和应用在正常运行时,某些指标会有自然的波动。如果这些波动频繁触发告警,但并没有实际问题,这些告警会成为噪音。没有考虑系统的正常波动范围和基线,很容易导致无效告警,但是某些情况下,告警也确实会提醒故障,所以很多时候告警的阀值就会保持不动。

传统告警方式总结

传统告警非基于智能基线的告警有其存在的理由,阀值肯定是针对某些业务有效果的情况下才配置的,指标如果波动过大,也确实需要告警出来,只是作为告警应该分级处理。

如果告警系统没有设置优先级(例如将告警分为“高”、“中”、“低”优先级),那么所有告警都被平等处理,导致一些不重要的告警与关键告警混在一起,无法及时区分真正需要处理的问题。


APO的告警分类展示逻辑

APO的核心页面设计是基于服务端点也就是业务接口来展示的,通过分析业务接口来判断告警是否真的产生了故障影响有助于判断哪些告警是真的告警,而忽略那些告警噪音。

APO的告警排查使用思路:

  1. 重点识别哪些服务端点代表的接口有异常
  2. 在接口详情中找到有哪些告警
  3. 判断故障影响面,告警是否很严重?需要拉上哪些人立马排查

APO首页告知哪些接口产生了异常

1 图

APO首页列出了所有接口的同比影响,并智能排序,只有接口同比有异常的才会排在前面展示,如果接口同比没有异常很可能是排序在下一页中,用户可能都不需要关心。 2 图

告警与接口的影响关系

系统运行资源告警(CPU、内存、磁盘)

系统运行资源告警(CPU、内存、磁盘)在首页中会以基础设施状态告警呈现,只要接口同比有异常且基础设施状态亮红灯,就可以确认基础设施状态影响了该接口的正常执行。

3 图

如果在首页中所有的接口指标变化不大,说明该系统运行资源告警(CPU、内存、磁盘)暂时不需紧急介入,如果有时间可以关注为什么产生该告警。 4 图

网络运行质量告警

网络运行质量告警在首页中会以网络质量状态呈现,只要接口同比有异常且网络质量状态红灯,就可以确认网络质量状态影响了该接口的正常执行。

5 图

如果在首页中所有的接口指标变化不大,说明该网络告警影响面很小,暂时不需紧急介入,如果有时间可以关注为什么产生该告警。 6 图

Kubernetes事件告警

Kubernetes事件告警在首页中会以Kubernetes事件告警状态呈现,只要接口同比有异常且Kubernetes事件红灯,就可以确认K8S事件影响了该接口的正常执行。

7 图

如果在首页中所有的接口指标变化不大,说明该网络告警影响面很小,暂时不需紧急介入,如果有时间可以关注为什么产生该告警。 8 图

应用级别告警

应用级别的告警如果是延时、错误率、吞吐量的同比异常,那可以直接看查看接口详情,排查问题。

APO 的服务详情中的告警tab,列出来所有可能和业务接口有关的告警

例如:ts-travel-service出现了接口的延时同比上升或者错误率同比上升,可以在该服务中,查看所有该接口关联起来的告警信息,从而快速聚焦到这些告警之上。 9 图

告警之后的第一步行为:告警的影响面分析

通过学习SRE相关知识或者告警处理的第一直觉,应该是判断这个告警的影响范围,也就是完成影响面分析,从而快速判断该告警是否会从简单告警演变成P0的故障。

告警的影响面分析不管如何做,最终需要回答以下的问题:

  • 目前告警对哪些接口产生了影响?
  • 这些接口又影响了用户使用哪些业务操作

10 图


总结

告警噪音是很难消除的,而且告警噪音本身也是一种提示告知某些指标有异常。APO承认这个事实,推荐采用如下方式排查告警:

APO的告警排查使用思路:

  1. 重点识别哪些服务端点代表的接口有异常(如果没有接口有异常,说明告警都是告警噪音。当然要对告警分级,比如磁盘满,进程挂掉等场景都是严重告警和接口异常无关)
  2. 在接口详情中找到有哪些告警
  3. 判断故障影响面,告警是否很严重?需要拉上哪些人立马排查

APO介绍:

国内开源首个 OpenTelemetry 结合 eBPF 的向导式可观测性产品

apo.kindlingx.com

https://github.com/CloudDetail/apo

APO的接口级拓扑 VS Dynatrace Service-Flow

· 阅读需 9 分钟
Autopilot Observability
APO 向导式可观测性平台

Cover 图

在可观测性系统中,几乎所有的产品都会提供拓扑功能。大部分用户在初看这个拓扑之时都会觉得非常有用,但是一旦真实落地使用,就感觉这个拓扑比较鸡肋。这篇文章重点探讨APO团队是如何考虑让用户能够更好的使用拓扑,真正发挥出拓扑的价值。

应用级别拓扑定义

GPT介绍应用级别拓扑:

应用级别拓扑是一种用于表示应用程序内部及其与其他应用程序或系统之间关系的可视化模型。它描述了应用程序中的各个组件(如服务、数据库、消息队列等)之间的交互方式,包括调用关系、数据流动和依赖关系。应用级别拓扑的目标是帮助开发和运维团队更好地理解和监控应用程序的架构、性能和健康状况。

应用级别拓扑的关键要素:

  1. 服务或组件:表示应用程序中的各个服务或模块,例如Web服务、数据库服务、缓存服务等。
  2. 依赖关系:显示应用程序对外部系统或资源的依赖,例如第三方API、外部数据库等。
  3. 数据流动:描述数据在应用程序中的流动路径,从数据的输入源到输出目标。
  4. 性能指标:包括延迟、吞吐量、错误率等关键性能指标,帮助监控应用程序的运行状态。

应用级别拓扑有助于在复杂的分布式系统中跟踪请求的执行路径,识别性能问题和瓶颈,并在发生故障时快速定位问题的根源。


提供了应用级别拓扑的产品

常见的开源apm、npm软件都提供了应用级别拓扑

12-1 pinpoint的应用级别拓扑

12-2 skywalking的应用级别拓扑

12-3 deepflow基于流量的应用级别拓扑


应用级别拓扑鸡肋的原因

应用级别拓扑肯定是有用的,但是实际落地比较鸡肋的原因如下:

  • 在小规模环境中比较有用,一旦达到几十个至上百个应用节点,拓扑结构就是一张蜘蛛网要清晰看出某个具体应用服务的依赖关系比较困难。
  • 应用级别拓扑结构粒度较粗,难以精准判断依赖关系影响:应用级别拓扑反应的程序与程序之间的依赖关系,并不是接口层,比如一个应用提供了10个接口,其中某一个接口调用了redis,在应用级别拓扑结构就会依赖redis,但是其余的接口其实并不依赖redis,很难回答以下的问题:
  1. redis有告警了,影响哪些业务操作?根据应用拓扑只能猜测redis告警,可能影响应用,具体是否受影响要在深入人为排查。
  2. 业务操作入口(用户直接使用的接口)执行缓慢,如何找到到底哪个依赖服务导致的呢?在应用拓扑中,得顺着应用拓扑所有路径去排查每个应用(几乎需要排查所有被监控的应用,因为所有的应用最后都可能形成一个拓扑结构)。现在的优化措施是在应用拓扑结构中优先排查应用告警的应用,这样希望能够尽早找到故障节点,但是有时仍然需要排查不少的应用,才能撞到故障根因节点。
  3. 业务操作入口(用户直接使用的接口)执行缓慢的另外一种排查思路,就是查看业务操作入口的Trace,这样看Trace缺失了统计信息,应用级别拓扑本应该发挥统计数据价值无用武之地。

业界王者Dynatrace的ServiceFlow解决了应用拓扑鸡肋的问题

以下是Dynatrace 的Service Flow图

12-4

初看和应用拓扑差不多,反应的也是应用节点直接的依赖调用关系,但是实际上和应用拓扑有以下的不同。

精准反应应用调用关系:相同的应用节点在拓扑中会按照调用顺序出现多次

12-5

ServiceFlow精准的反应应用接口之间的调用关系。相同业务操作入口的拓扑图才会被归属在同一个ServiceFlow中。

基于ServcieFlow 可以精准的找到依赖关系,比如redis告警,到底是如何影响业务调用链的。

基于ServiceFlow可以清晰的看出来业务操作入口的影响是由于下游依赖的哪些应用接口执行导致的。

在大规模业务中,会将对入口业务操作影响贡献不大的应用收缩,从而在一张图中清晰看出调用关系

12-6


APO中的接口URL级别拓扑和ServiceFlow异曲同工

12-7

APO中的URL接口级别拓扑是根据相同业务操作入口的Trace拓扑不断分析统计而成,虽然看上去像应用级别拓扑,但是实际是URL接口级别拓扑,这样可以清楚看出应用为了执行此类业务的调用关系。

如果redis告警,在此拓扑中未发现有redis,说明redis告警不会影响此业务,不需要像应用级别拓扑再次猜测redis告警到底影响了哪些业务。

APO也实现了同样的效果:精准反应应用调用关系------相同的应用节点在拓扑中会按照调用顺序出现多次

12-8

根据业务入口延时相似度对拓扑结构的收缩,保证一张图可以清晰显示业务调用关系

APO利用延时曲线相似度来收缩相似度非常低的接口服务。

业务操作入口的延时曲线与下游依赖的节点延时曲线完全不相似,说明两者应该不是很相关,可以收缩起来。

相似度较高的曲线,比如下图,紫色的延时曲线明显和绿色延时曲线很相似,还有一些曲线是有波动的,但是还有很多曲线是很平稳的。那些曲线平稳的肯定对此业务操作入口的波动贡献很小,所以可以收缩。

12-9

12-10


未来基于URL接口级别拓扑能够实现以下功能

在APO的规划当中会实现以下的功能:

  • 每个微服务接口点击详情之后,自动关联业务操作入口从而快速判断故障影响面
  • 按照URL接口界别拓扑,自动化精准关联下游依赖节点告警

APO介绍:

国内开源首个 OpenTelemetry 结合 eBPF 的向导式可观测性产品

https://apo.kindlingx.com

https://github.com/CloudDetail/apo

APO选择ClickHouse存储Trace的考量

· 阅读需 6 分钟
Autopilot Observability
APO 向导式可观测性平台

Cover 图

OpenTelemetry生态已经很成熟,但对用户而言,选择OpenTelemetry仍然需要考虑以下几个问题:

  • 探针的成熟度
  • 海量Trace数据的存储和展示的问题

本文重点讨论海量Trace数据的存储与展示问题,APO定位是一个OpenTelmetry的发行版,本文将重点讨论APO团队是如何考虑这个问题的。

现有OpenTelemetry的Trace存储方案

OpenTelemetry生态过于灵活,选择众多,这也给用户带来了幸福的烦恼。

直接使用Jaeger+ElasticSearch方案

Jaeger作为老牌的Tracing方案,其使用习惯已经被很多用户所接受,Jaeger与OpenTelemetry同属于CNCF组织下的开源项目,所以两者也是结合最紧密的。

目前使用OpenTelemetry方式最快的方式使用的是Jaeger+ElasticSearch方案,该方案成熟。但是由于ElasticSearch的存储查询效率并不高,当规模较大的时候成本较大,所以很多用户期望有更加高效的存储方案。

新起的开源Signoz与Uptrace的做法

Signoz与Uptrace是近几年OpenTelemetry生态的发行版本,这两者都选择了ClickHouse作为存储方案,ClickHouse由于其强大的压缩和查询能力,成为很多可观测性方案的标准做法。

Signoz与Uptrace做法相同:自定义ClickHouse的表结构

自定义ClickHouse的表结构的好处在于,所有的内容完全能够自己掌控,但是坏处是其他生态产品很少会基于该自定义表结构进行演进,从而没有办法与其他生态集成。

大量已经习惯了使用Jaeger用户的在使用Signoz和Uptrace的时候都有一定的学习成本,比如需要理解:

  • Span自身花的时间应该如何查找
  • Span的tag应该如何才能查看

这对于没有接触过Jaeger的用户而言是可行的,选择Signoz和Uptrace没有太多差别,但对于已经熟悉Jaeger的用户不大友好。

ClickHouse官方的Exporter方式

ClickHouse官方针对OpenTelemetry生态推出了Exporter,解决了Trace如何落地到ClickHouse的问题,但是并未搭配界面使用,这意味着用户使用ClickHouse官方Exporter的用户,需要定制页面完成Trace的展示和分析工作,这对于绝大部分用户而言并不友好。

Jaeger 2.0 基于ClickHouse的实现Tracing存储方案

具体可以参考文章:迈向 Jaeger v2:更多 OpenTelemetry!

虽然当前Jaeger 1.X版本并没有正式支持ClickHouse,而是在1.57之前通过RemoteStorage 的插件方式支持,具体见链接,在最新的1.58之后,RemoteStorage 就不再支持了。


APO对Trace存储的思考

不同表结构对性能影响没有显著差别

我们团队调研过Jaeger官方关于ClickHouse不同表结构对于Trace插入和查询的影响(主要对比Jaeger RemoteStorage 的表结构和ClickHouse的官方exporter表结构),虽然表结构对性能影响有些许差异,但在插入、查询、压缩比方面各有千秋,而且性能差异对大部分用户也是能接受的。具体见链接

用户习惯最重要

由于APO在向导式界面已经屏蔽了Trace的细节,先通过指标和告警引导用户到需要查看的Trace时,用户才通过TraceID查看Trace。此时,我们认为用户能够以最小的成本理解Trace细节最重要,所以我们引入了Jaeger来展示Trace细节,并没有重新开发页面或者选择signoz、uptrace的方案。

APO对Jaeger RemoteStorage的扩展

Jaeger2.0 已经明确会支持ClickHouse了,在Jaeger 2.0发版之前,APO做了Jaeger RemoteStorage的扩展,使其能够支持1.58以后的版本。具体实现项目见链接


总结

ClickHouse不同的表结构对性能会有差异,但是只要使用ClickHouse其存储和查询效率就会比ElasticSearch高很多,所以在这种情况下,用户的体验就是最重要的。

对于用户而言,每天接触的新产品新功能很多,能够在新产品上无缝嫁接其已具备的成熟体验可能是最重要的。

告别ELK,APO提供基于ClickHouse开箱即用的高效日志方案——APO 0.6.0发布

· 阅读需 17 分钟

Cover 图

ELK一直是日志领域的主流产品,但是ElasticSearch的成本很高,查询效果随着数据量的增加越来越慢。业界已经有很多公司,比如滴滴、B站、Uber、Cloudflare都已经使用ClickHose作为ElasticSearch的替代品,都取得了不错的效果,实现了降本增效,费用节约大多在50%以上。但是目前使用ClickHose作为日志方案,存在以下问题。

  • 主流的Vector+ClickHose并未实现开箱即用,有许多的管理配置工作
  • 绝大多数方案不支持近似全文检索的功能(该功能很重要)
  • 使用双数组或者Map的表结构查询效率不高
  • ClickVisual是最接近的开箱即用的日志方案,也存在以下问题:

○强依赖Kafka,对于某些中小用户而言方案不够灵活,不友好

○未引入Vector,原生的ClickHose Kafka引擎在大流量情况下可能导致ClickHose内存爆掉(感谢社区大佬 十四反馈)

主流的Vector+ClickHouse方案并未实现开箱即用

目前业界很多公司都是基于Vector+ClickHouse的方案来实现日志的采集和存储,该方案需要管理维护的工作量相对而言比较高,适用于动手能力强的公司。

维护工作:为每种日志手动维护一张表

每个公司的部门团队可能日志规范都不完全一致,如果需要对日志内容进行快速搜索定位故障,就需要提前想好ClickHouse的表结构,然后调整Vector的配置文件,最终实现Vector根据不同日志格式,parse成不同的日志表字段,写入不同的日志表。

比如每种日志都得建立以下类似的表结构,才能完成日志按照ip、url等字段的索引实现快速搜索。但是另外一个部门的日志也许就不需要IP和url字段,那么该部门得重新设计表结构。

CREATE TABLE log
(
`ip` String,
`time` Datetime,
`url` String,
`status` UInt8,
`size` UInt32,
`agent` String
)
ENGINE = MergeTree
ORDER BY date(time)

使用双数组或者Map的表结构查询效率不高

为了能够规避这些维护工作,所以很多公司对固定日志表结构进行了调整,常见的有两种方案,一种是双数组方案,另外一种就是Map方案。

Uber和Signoz的日志实现方案都是基于双数组

其日志表结构类似于下面这种

CREATE TABLE <table_name>
(
//Common metadata fields.
_namespace String,
_timestamp Int64,
hostname String,
zone String,
...

//Raw log event.
_source String,

//Type-specific field names and field values.
string.names Array(String),
string.values Array(String),
number.names Array(String),
number.values Array(Float64),
bool.names Array(String),
bool.values Array(UInt8),

//Materialized fields
bar.String, String
foo.Number Float64,
...
)
...

滴滴、B站等日志实现是基于Map结构

引入Map结构能够动态实现日志关键字段搜索

CREATE TABLE ck_bamai_stream.cn_bmauto_local
(
`logTime` Int64 DEFAULT 0, --Log打印的时间
`logTimeHour` DateTime MATERIALIZED toStartOfHour(toDateTime(logTime / 1000)),--将Log
`odinLeaf` String DEFAULT '',
`uri` LowCardinality(String) DEFAULT '',
`traceid` string DEFAULT '',
`cspanid` String DEFAULT '',
`dltag` String DEFAULT '',
`spanid` String DEFAULT '',
`message` String DEFAULT '',
`otherColumn` Map<String,String>
`_sys_insert_time` DateTime MATERIALIZED now()
)
ENGINE =MergeTree
PARTITION BY toYYYYMMDD(logTimeHour)
ORDER BY(logTimeHour,odinLeaf,uri,traceid)
TTL _sys_insert_time +toIntervalDay(7),_sys_insert_time + toIntervalDay(3)To VOLUME 'hdfs
SETTINGS index_granularity = 8192,min_bytes_for_wide_part=31457280
Create Table <log_app_name> ON CLUSTER ...
{
_timestamp Datetime64(3),
`log,level` String CODC(ZSTD(1)),
`log.msg` String CODC(ZSTD(1)),
`log.trace_id` String CODC(ZSTD(1)),
...
string_map MapV2(String, Nullable(String))
CODEC(ZSTD(1))
number_map MapV2(String, Nullable(Float64))
CODEC(ZSTD(1))
bool_map MapV2(String, Nullable(UInt8))
}
ENGIN = ReplicatedMergeTree(...)
PARTITION BY toYYYYMMDD(_timestamp)
ORDER BY timestamp
TTL toDateTime(timestamp) + toIntervalDay(...),
toDateTime(timestamp) + toIntervalDay(...) TO VOLUME `cold_volume`

Map的动态字段搜索效率低

https://clickhouse.ac.cn/docs/knowledgebase/improve-map-performance

根据社区反馈,map底层实现为线性数组,map查询效率通常低于列查询3~10倍,特别是日志量规模越大,map查询效率越低。

同时支持Map类型的最低clickhosue版本为21.11

  • 列式存储优势: ClickHouse 的核心优势在于它是列式存储数据库,这意味着当执行查询时,只需要读取查询中涉及的列,而不必加载不相关的列。列式存储还能够通过数据类型特定的压缩技术显著减少 IO 操作,从而加快查询速度

  • 基于 Map 的查询: Map 是一种键值对数据结构,在查询时需要额外的开销来解析嵌套结构,并且无法像列式存储那样直接跳过不相关的数据。虽然 ClickHouse 对 Map 数据类型有一些优化,但它在处理复杂结构时往往会比简单的列查询慢

双数组的搜索效率也不高

虽然 ClickHouse 对Array有一定的优化,但双数组结构仍然比单纯的列查询开销大。

影响性能的因素:

  • 多级解析开销:查询双数组时,需要进行多层嵌套解析。例如,访问数组中的子数组意味着需要遍历父数组,然后进一步解析子数组的结构,这比单纯读取一个列复杂得多
  • 随机存取:双数组的访问模式往往比简单的列查询更加随机化。访问数组中的元素可能导致更多的跳转,影响缓存命中率,从而降低性能
  • 内存使用和数据存储:嵌套数组会使得 ClickHouse 的数据存储和内存管理更加复杂,因为数组中的数据长度不固定,导致压缩效果比单纯列差,数据块的大小也更加难以优化

性能上的差距取决于具体的查询模式和数据结构:

  • 在简单查询场景(例如,读取一个基本的列数据),单纯的列查询会比双数组快得多,特别是在处理大规模数据时。性能差距可能达到 数倍甚至十倍 以上,尤其是当查询不涉及嵌套结构时。
  • 在复杂查询场景(例如,查询涉及嵌套数组、需要频繁地进行数组拆解和操作),双数组的查询性能通常明显低于单纯的列查询。查询双数组的额外解析和处理开销,会使查询时间增加。根据不同的嵌套深度和数据量,性能可能下降 数倍。

ClickHouse的官方文档中日志方案也由于引入了Map效率不高

ClickHouse官方blog :

https://ClickHouse.com/blog/storing-log-data-in-ClickHouse-fluent-bit-vector-open-telemetry

提到有以下几种表结构:

OTEL的日志字段表

CREATE TABLE otel.otel_logs
(
`Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
`TraceId` String CODEC(ZSTD(1)),
`SpanId` String CODEC(ZSTD(1)),
`TraceFlags` UInt32 CODEC(ZSTD(1)),
`SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
`SeverityNumber` Int32 CODEC(ZSTD(1)),
`ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
`Body` String CODEC(ZSTD(1)),
`ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
//数据索引
INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,
INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_log_attr_value mapValues(LogAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
INDEX idx_body Body TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY toDate(Timestamp)
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1

Vector 字段表

CREATE TABLE vector.vector_logs
(
`file` String,
`timestamp` DateTime64(3),
`kubernetes_container_id` LowCardinality(String),
`kubernetes_container_image` LowCardinality(String),
`kubernetes_container_name` LowCardinality(String),
`kubernetes_namespace_labels` Map(LowCardinality(String), String),
`kubernetes_pod_annotations` Map(LowCardinality(String), String),
`kubernetes_pod_ip` IPv4,
`kubernetes_pod_ips` Array(IPv4),
`kubernetes_pod_labels` Map(LowCardinality(String), String),
`kubernetes_pod_name` LowCardinality(String),
`kubernetes_pod_namespace` LowCardinality(String),
`kubernetes_pod_node_name` LowCardinality(String),
`kubernetes_pod_owner` LowCardinality(String),
`kubernetes_pod_uid` LowCardinality(String),
`message` String,
`source_type` LowCardinality(String),
`stream` Enum('stdout', 'stderr')
)
ENGINE = MergeTree
ORDER BY (`kubernetes_container_name`, timestamp)

fluent字段表

CREATE TABLE fluent.fluent_logs
(
`timestamp` DateTime64(9),
`log` String,
`kubernetes` Map(LowCardinality(String), String),
`host` LowCardinality(String),
`pod_name` LowCardinality(String),
`stream` LowCardinality(String),
`labels` Map(LowCardinality(String), String),
`annotations` Map(LowCardinality(String), String)
)
ENGINE = MergeTree
ORDER BY (host, pod_name, timestamp)

日志需要近似全文检索

基于ElasticSearch的日志方案,由于可以基于ElasticSearch实现的日志内容分词,所以很容易实现全文检索,但是基于ClickHouse就很难实现该功能。

那是不是基于ClickHouse的方案就完全没有办法呢?

ClickHouse的索引介绍

  • tokenbf_v1 按非字母数字字符(non-alphanumeric)拆分。相当于按符号分词,而通常日志中会有大量符号

在大牛的文章中,

https://juejin.cn/post/7130514546069864456

详细介绍了全文检索的实现,有兴趣的可以仔细看下大牛的文章。

最理想的日志方案应该满足什么条件?

我们认为理想的基于ClickHouse的日志方案应该满足以下几条:

  • 使用列来进行检索,而不是map或者双array,保证高效的查询效率
  • 用户不需要为了不同部门的日志内容,进行维护单独的表结构
  • 支持对原始日志内容进行近似的全文检索

我们调研了国内外几乎所有基于ClickHouse的日志方案,最后发现国内开源项目ClickVisual项目的思路最相近,ClickVisual几乎可以做到开箱即用。


ClickVisual的方案不足

ClickVisual工作原理:

  • 针对每种日志格式定义不同的parse规则,ClickVisual为每种规则生成一张新的日志表。该日志表存的就是解析之后的日志,之后的日志查询都是针对该日志表。因为解析之后的日志已经按照ClickHouse列存储了,所以关键字段查询是非常快的
  • 基于Kafka表引擎,读取Kafka原始日志,落库至临时表中
  • 魔术开始的地方: 基于ClickHouse的物化视图,将原始日志中的新增日志_raw_log_内容按照日志解析规则parse成日志列格式,并将解析好的日志存入该规则对应的日志表中

临时表

CREATE TABLE default.test_stream
(
`status` String,
`timestamp` Float64,
`message` String CODEC(ZSTD(1))
)
ENGINE = Kafka
SETTINGS kafka_broker_list = '127.0.0.1:9092',
kafka_topic_list = 'test',
kafka_group_name = 'default_test',
kafka_format = 'JSONEachRow',
kafka_num_consumers = 1,
kafka_skip_broken_messages = 0

物化视图

CREATE MATERIALIZED VIEW default.test_view TO default.test
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String,
// 日志表的列
`level` Nullable(String)
//根据需要调整列
...
) AS
SELECT
status,
toDateTime(toInt64(timestamp)) AS _time_second_,
fromUnixTimestamp64Nano(toInt64(timestamp * 1000000000)) AS _time_nanosecond_,
message AS _raw_log_,
// 物化视图处理成列
toNullable(toString(replaceAll(JSONExtractRaw(message, 'level'), '"', ''))) AS level
// 根据需要添加更多解析规则
...
FROM default.test_stream
WHERE 1 = 1

按照日志解析规则将_raw_log_parse成新的真实日志表

CREATE TABLE default.test
(
`status` String,
`_time_second_` DateTime,
`_time_nanosecond_` DateTime64(9),
`_raw_log_` String CODEC(ZSTD(1)),
// 该列通过物化视图解析得到
`level` Nullable(String),
// 根据需要添加更多列
...
INDEX idx_raw_log _raw_log_ TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(_time_second_)
ORDER BY _time_second_
TTL toDateTime(_time_second_) + toIntervalDay(1)
SETTINGS index_granularity = 8192

每当需要分析新的索引字段,clickvisual会执行Add Colum为日志表添加新的列,同时更新物化视图添加新的解析处理规则。

ClickVisual的不足:

根据上述的原理:

  • 不支持高效的近似全文检索,工作原理可以看出由于ClickVisual并未对_raw_log_进行跳数索引,所以也就导致ClickVisual不能高效的支持近似的对原始日志全文检索
  • 由于ClickVisual完全依赖Kafka表引擎来实现日志的摄入,虽然ClickVisual也支持引入ClickHouse的已有日志表结构进行查询,但是很可能并不是直接针对列查询,而是针对map数据查询,只有通过Kafka引擎来摄入的日志才能生成新日志表结构,最终查询才是针对列式查询,才能有较高的查询效率
  • Kafka表引擎读写日志速度无法控制,如果日志量非常多,导致ClickHouse物化视图工作过程中内存爆掉

APO 日志设计方案

ClickVisual已经非常接近理想日志方案了,只是我们需要对ClickVisual的逻辑进行调整。

  1. 不使用ClickHouse的Kafka表引擎来完成日志的摄取工作,而是改成Vector的方式完成日志的摄取工作。这样就不再依赖Kafka,对于中小用户日志规模没有那么大的用户,可以直接使用,而不需要维护Kafka。虽然去掉了Kakfa,同时增加了Vector,但是Vector的运维工作相比kafka而言,Vector几乎不需要运维
  2. 引入了Vector之后,可以通过配置Vector来调整参数,确保在大量日志洪锋的时候,也不至于将ClickHouse内存打爆
  3. 用户如果真的需要引入Kafka,也有已经维护好的Kafka,完全可以使用Vector先将原始日志写入Kafka,然后使用Vector从Kafka读取出来,继续实现后续的日志处理
  4. APO引入了ClickHouse null表引擎,来实现原始日志(从Vector写入的) 转换成按照日志解析格式解析之后的真实日志表。
  5. 所有的查询都是针对真实日志表的列查询,所以性能比较高
  6. 在真实日志表中,额外存储了_raw_log_,配合跳数索引完成 近似日志全文检索。

欢迎使用APO全量日志功能 1图


APO v0.6.0更新日志:

新增功能

  • 支持全量日志的采集、处理与展示功能

缺陷修复

  • 修复服务端点存在特殊字符时,无法获取到依赖延时曲线的问题
  • 修复无故障场景下频繁采集故障现场数据的问题
  • 修复部分场景下数据库调用无指标的问题
  • 修复传统服务器场景下,网络质量状态无法关联到告警的问题
  • 修复传统服务器场景下,OneAgent配置注入失败的问题

其他

  • 允许在创建 ClickHouse 表时选择是否创建副本
  • 向下兼容 ClickHouse 版本,当前支持最低版本为 22.8.x

APO介绍:

国内开源首个 OpenTelemetry 结合 eBPF 的向导式可观测性产品

apo.kindlingx.com

https://github.com/CloudDetail/apo