编译时插桩,Go应用监控的最佳选择

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
应用实时监控服务-用户体验监控,每月100OCU免费额度
简介: 本文讲解了阿里云编译器团队和可观测团队为了实现Go应用监控选择编译时插桩的原因,同时还介绍了其他的监控方案以及它们的优缺点。

可观测性是以系统的指标、日志、链路追踪、持续剖析四大数据支柱为基础,从宏观到微观,通过不同数据之间互相关联,衍生出如数据监控、问题分析、系统诊断等一系列的能力。

image.png

Java[1]可以通过字节码增强的技术实现无侵入的应用监控(开源社区有非常多的无侵入Agent实现方案,技术非常成熟),可以轻松获取到关键监控数据,相比Java,Go因为语言的特点,应用运行的时候已经被编译成一个二进制文件,无法再做类似Java字节码增强的方式进行动态插桩,在应用监控领域的生态并不完善,可观测的四大数据支柱无法通过无侵入的方式来实现,使得用户的接入成本变高,当前针对Go应用的可观测能力,有3种解决方案:


  • SDK方案
  • eBPF方案
  • 编译期自动注入方案

以下分别来介绍这几个方案以及对应的开源实现:


1.SDK方案

在可观测领域,随着OpenTracing 被OTel 收编,目前被广泛使用的SDK就是OTel Go SDK[2],通过在业务代码的每个需要的地方进行手动增加埋点,如下所示:


package main

import (
  "context"
  "fmt"
  "go.opentelemetry.io/otel"
  "go.opentelemetry.io/otel/attribute"
  "go.opentelemetry.io/otel/sdk/trace"
  "io"
  "net/http"
)

func init() {
  tp := trace.NewTracerProvider()
  otel.SetTracerProvider(tp)
}

func main() {
  for {
    tracer := otel.GetTracerProvider().Tracer("")
    ctx, span := tracer.Start(context.Background(), "Client/User defined span")
    otel.GetTextMapPropagator()
    req, err := http.NewRequestWithContext(ctx, "GET", "http://otel-server:9000/http-service1", nil)
    if err != nil {
      fmt.Println(err.Error())
      continue
    }
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
      fmt.Println(err.Error())
      continue
    }
    defer resp.Body.Close()
    b, err := io.ReadAll(resp.Body)
    if err != nil {
      fmt.Println(err.Error())
      continue
    }
    fmt.Println(string(b))
    span.SetAttributes(attribute.String("client", "client-with-ot"))
    span.SetAttributes(attribute.Bool("user.defined", true))
    span.End()
  }
}

先定义好一个TraceProvider,然后在发起请求的地方获取tracer,使用tracer.Start创建一个span,然后发起请求,在请求结束后使用span.End()。


这是一个简单的http的请求,如果是复杂的业务应用,会涉及多个调用,比如调用redis、mysql、mq、es等中间件,需要在每个调用的地方都进行埋点,同时还需要处理好span Context的传递、baggage的传递,以及及时调用span End。


OTel 的spanContext都是通过context进行传递,如下所示:

func testContext() {
  tracer := otel.Tracer("app-tracer")
  opts := append([]trace.SpanStartOption{}, trace.WithSpanKind(trace.SpanKindServer))
  rootCtx, rootSpan := tracer.Start(context.Background(), getRandomSpanName(), opts...)
  if !rootSpan.SpanContext().IsValid() {
    panic("invalid root span")
  }

  go func() {
    opts1 := append([]trace.SpanStartOption{}, trace.WithSpanKind(trace.SpanKindInternal))
    _, subSpan1 := tracer.Start(rootCtx, getRandomSpanName(), opts1...)
    defer func() {
      subSpan1.End()
    }()
  }()

  go func() {
    opts2 := append([]trace.SpanStartOption{}, trace.WithSpanKind(trace.SpanKindInternal))
    _, subSpan2 := tracer.Start(rootCtx, getRandomSpanName(), opts2...)
    defer func() {
      subSpan2.End()
    }()
  }()
  rootSpan.End()
}


上述的2个新创建的协程里面使用了rootCtx,这样2个协程里面创建的span会是rootSpan的子span,在业务代码中也需要类似的方式进行传递,如果不正确传递context会导致调用链路无法串联在一起,也可能会造成链路错乱。


同时OpenTelemetry Go SDK 目前保持着2周到4周会发布一个版本

https://github.com/open-telemetry/opentelemetry-go/releases,更新速度非常快,经常会有前后不兼容的情况,业务升级OTel Go SDK会导致代码也需要进行修改,成本非常高。


2.eBPF方案

eBPF(扩展的伯克利数据包过滤器)作为Linux内核中的一个高效且灵活的虚拟机,允许开发者自定义运行程序,并通过特定接口将这些程序加载到内核空间执行。这一特性使得eBPF成为了构建各类系统监控解决方案的理想选择之一。


image.png

近年来,基于eBPF技术开发的各种开源项目如雨后春笋般涌现出来,其中包括


等知名项目。它们共同致力于利用eBPF的强大能力来实现诸如性能分析(Profiling)、网络流量监测(Network Monitoring)、度量指标收集(Metric Collection)及分布式追踪(Distributed Tracing)等功能。


eBPF可以通过在不同位置的挂载点完成对数据流的抓取,比如tracepoint、kprobe等,也可以使用uprobe针对用户态函数进行hook,以协议解析为例,随着业务复杂度的提升以及不同使用场景的要求,用户态的协议非常多,有RPC类型的http、https、grpc、dubbo等,还有中间件的mysql、redis、es、mq、ck等,要通过eBPF抓取的数据完成数据解析并实现指标的统计难度非常大。


以使用eBPF监控Go应用为例,因其独特的并发模型而广泛采用异步处理机制,若想精确地进行跨协程上下文传递或深入到应用程序内部进行细粒度的跟踪,则通常还需要额外引入SDK来进行辅助支持,完成不同协程之间的上下文传递。


尽管上述项目在功能上存在一定程度的相似性,但由于eBPF自身的一些限制因素,比如eBPF 通常仅限于具有提升权限的 Linux 环境,同时针对内核的版本有要求,对于某些应用场景尤其是涉及到复杂应用层逻辑追踪时,单独依靠eBPF往往难以达到理想效果。


就性能开销而言,eBPF相对于进程内的Agent稍显落后,因为 uprobe 的触发需要在用户空间和内核之间进行上下文切换,这对于访问量特别大的一些接口难以承受。


3.编译时插桩方案

在这个方案前我们在eBPF方案做了非常多的探索,希望使用eBPF一劳永逸的解决非Java语言的各种监控问题,特别是Go应用(在当前除了Java外使用最广泛的语言),经过长时间的探索,发现无法达成如Java一样实现完全无死角的监控能力,这也正让我们开始思考通过其他方式解决这个问题,基于Go toolexec能力,编译时插桩实现Go的应用监控变得可行。


Go应用的编译流程如下:

image.png

使用简单的go build 即可获得最终可以执行的二进制文件,go build 的过程通过以下的:

image.png

在经过词法分析、语法分析后生成一些.a的中间态文件,最终通过Link的方式将.a文件生成为二进制文件。通过这个步骤可以看出我们可以在编译前端到编译后端中间进行hook的操作,因此我们将对应的编译流程改成如下方式:

image.png

通过AST语法树分析,查找到监控的埋点,根据提前定义好的埋点规则,在编译前插入需要的监控代码,然后经过完成的Go编译流程将代码注入到最终的二进制中,这个方案与程序员手写代码完全没有区别,由于经过了完整的编译流程,不会产生一些不可预料的错误。


使用阿里云可观测Go Agent能力,只需要下载一个编译工具instgo,然后修改一下编译语句即可快速接入,如下所示:


当前的编译语句:

当前的编译语句:

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go 

使用Aliyun Go Agent:

wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/instgo/instgo-linux-amd64" -O instgo
chmod +x instgo
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./instgo go build main.go


通过wget下载instgo编译工具,只需要简单修改在go build前添加instgo即可完成监控能力注入。


我们可以在插入的代码中实现跟Java应用监控完全一样的监控能力,如链路追踪、指标统计、持续剖析、动态配置、代码热点、日志Trace关联等等,在插件丰富度上我们支持了40+的常见插件[4],包含了RPC框架、DB、Cache、MQ、Log等,在性能上,5%的消耗即可支持1000 qps[5],通过动态开关控制、Agent版本灰度等实现生产的可用性和风险控制能力。


4.总结

image.png

本文讲解了阿里云编译器团队和可观测团队为了实现Go应用监控为什么选择编译时插桩的原因,同时还介绍了其他的监控方案,以及它们的优缺点。我们相信阿里云Go Agent(Instgo)是一个非常强大的工具,可以帮助我们实现针对Go应用更好的APM能力,同时还能保持应用程序的安全性和可靠性。


为了推广编译时注入的方案,同时为Go开发者提供更多的选择,提升效率,我们的Agent进行了开源[7],欢迎大家加入我们的钉钉群(开源群:102565007776,商业化群:35568145),共同提升编译时插桩在Go应用监控的能力。


参考链接:

参考一

参考二

 监控Golang应用参考三

 ARMS应用监控支持的Golang组件和框架参考四

 Golang探针性能压测报告参考五

参考六

参考七





来源  |  阿里云开发者公众号

作者  |  古琦



相关实践学习
通过云拨测对指定服务器进行Ping/DNS监测
本实验将通过云拨测对指定服务器进行Ping/DNS监测,评估网站服务质量和用户体验。
相关文章
|
9天前
|
调度 云计算 芯片
云超算技术跃进,阿里云牵头制定我国首个云超算国家标准
近日,由阿里云联合中国电子技术标准化研究院主导制定的首个云超算国家标准已完成报批,不久后将正式批准发布。标准规定了云超算服务涉及的云计算基础资源、资源管理、运行和调度等方面的技术要求,为云超算服务产品的设计、实现、应用和选型提供指导,为云超算在HPC应用和用户的大范围采用奠定了基础。
179604 21
|
1天前
|
弹性计算 人工智能 安全
对话 | ECS如何构筑企业上云的第一道安全防线
随着中小企业加速上云,数据泄露、网络攻击等安全威胁日益严重。阿里云推出深度访谈栏目,汇聚产品技术专家,探讨云上安全问题及应对策略。首期节目聚焦ECS安全性,提出三道防线:数据安全、网络安全和身份认证与权限管理,确保用户在云端的数据主权和业务稳定。此外,阿里云还推出了“ECS 99套餐”,以高性价比提供全面的安全保障,帮助中小企业安全上云。
对话 | ECS如何构筑企业上云的第一道安全防线
|
18天前
|
人工智能 自然语言处理 前端开发
从0开始打造一款APP:前端+搭建本机服务,定制暖冬卫衣先到先得
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。
9519 25
|
4天前
|
机器学习/深度学习 分布式计算 供应链
阿里云先知安全沙龙(上海站) ——大模型基础设施安全攻防
大模型基础设施的安全攻防体系涵盖恶意输入防御和基础设施安全,包括框架、三方库、插件、平台、模型和系统安全。关键漏洞如CVE-2023-6019(Ray框架命令注入)、CVE-2024-5480(PyTorch分布式RPC)及llama.cpp中的多个漏洞,强调了代码安全性的重要性。模型文件安全方面,需防范pickle反序列化等风险,建议使用Safetensors格式。相关实践包括构建供应链漏洞库、智能化漏洞分析和深度检测,确保全方位防护。
|
6天前
|
JSON 分布式计算 数据处理
加速数据处理与AI开发的利器:阿里云MaxFrame实验评测
随着数据量的爆炸式增长,传统数据分析方法逐渐显现出局限性。Python作为数据科学领域的主流语言,因其简洁易用和丰富的库支持备受青睐。阿里云推出的MaxFrame是一个专为Python开发者设计的分布式计算框架,旨在充分利用MaxCompute的强大能力,提供高效、灵活且易于使用的工具,应对大规模数据处理需求。MaxFrame不仅继承了Pandas等流行数据处理库的友好接口,还通过集成先进的分布式计算技术,显著提升了数据处理的速度和效率。
|
22天前
|
Cloud Native Apache 流计算
资料合集|Flink Forward Asia 2024 上海站
Apache Flink 年度技术盛会聚焦“回顾过去,展望未来”,涵盖流式湖仓、流批一体、Data+AI 等八大核心议题,近百家厂商参与,深入探讨前沿技术发展。小松鼠为大家整理了 FFA 2024 演讲 PPT ,可在线阅读和下载。
5159 15
资料合集|Flink Forward Asia 2024 上海站
|
1月前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
14天前
|
Docker 容器
|
2天前
|
机器学习/深度学习 人工智能 安全
通义视觉推理大模型QVQ-72B-preview重磅上线
Qwen团队推出了新成员QVQ-72B-preview,这是一个专注于提升视觉推理能力的实验性研究模型。提升了视觉表示的效率和准确性。它在多模态评测集如MMMU、MathVista和MathVision上表现出色,尤其在数学推理任务中取得了显著进步。尽管如此,该模型仍存在一些局限性,仍在学习和完善中。
|
17天前
|
消息中间件 人工智能 运维
12月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
1324 76