
阿里妹導讀

-
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能力,同時還能保持應用程式的安全性和可靠性。
[1]https://github.com/open-telemetry/opentelemetry-java
[2]https://github.com/open-telemetry/opentelemetry-go
[3] 監控Golang應用:https://help.aliyun.com/zh/arms/application-monitoring/user-guide/monitoring-the-golang-applications/
[4] ARMS應用監控支援的Golang元件和框架:https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/go-components-and-frameworks-supported-by-arms-application-monitoring
[5] Golang探針效能壓測報告:https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/golang-probe-performance-pressure-test-report
雲上公網架構設計和安全管理
雲上公網的設計可以幫助企業更加統一、安全地管理自己的雲上網際網路出入口,同時可以實現統一監控運維和公網的成本最佳化。
點選閱讀原文檢視詳情。