阿里妹导读
SDK方案
eBPF方案
编译期自动注入方案
SDK方案
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()。
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周会发布一个版本
eBPF方案
pixie(https://github.com/pixie-io/pixie)
beyla(https://github.com/grafana/beyla)
opentelemetry-go-instrumentation(https://github.com/open-telemetry/opentelemetry-go-instrumentation)
deepflow(https://github.com/deepflowio/deepflow)
编译时插桩方案
在经过词法分析、语法分析后生成一些.a的中间态文件,最终通过Link的方式将.a文件生成为二进制文件。通过这个步骤可以看出我们可以在编译前端到编译后端中间进行hook的操作,因此我们将对应的编译流程改成如下方式:
通过AST语法树分析,查找到监控的埋点,根据提前定义好的埋点规则,在编译前插入需要的监控代码,然后经过完成的Go编译流程将代码注入到最终的二进制中,这个方案与程序员手写代码完全没有区别,由于经过了完整的编译流程,不会产生一些不可预料的错误。
当前的编译语句: 当前的编译语句:
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即可完成监控能力注入。
总结
本文讲解了阿里云编译器团队和可观测团队为了实现Go应用监控为什么选择编译时插桩的原因,同时还介绍了其他的监控方案,以及它们的优缺点。我们相信阿里云Go Agent(Instgo)是一个非常强大的工具,可以帮助我们实现针对Go应用更好的APM能力,同时还能保持应用程序的安全性和可靠性。
云上公网架构设计和安全管理
云上公网的设计可以帮助企业更加统一、安全地管理自己的云上互联网出入口,同时可以实现统一监控运维和公网的成本优化。