Go ウェブアプリの計装
Blog posts are not updated after publication. This post is more than a year old, so its content may be outdated, and some links may be invalid. Cross-verify any information before relying on it.
このブログ記事では、事前知識なしで OpenTelemetry Go を使ってトレースを作成し可視化する方法をハンズオン形式で学びます。
まず、Mongo と Gin フレームワークを使用したシンプルな To-Do アプリを作成します。 そして、トレーシングデータを Jaeger Tracing に送信して可視化します。 関連するすべてのファイルは、この GitHub リポジトリ にあります。

Hello world: OpenTelemetry Go の例
まず、To-Do サービスを作成し、2つのライブラリ(Gin と Mongo)をインストールして、計装の仕組みを理解します。
ステップ1: To-Do アプリ用の main.go ファイルを作成する
Gin と Mongo-driver をインストールする
go get -u github.com/gin-gonic/gin go get go.mongodb.org/mongo-driver/mongogin と mongo を設定して “/todo” でリッスンする
Mongo にシードするための To-Do をいくつか作成する
package main import ( "context" "net/http" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) var client * mongo.Client func main() { connectMongo() setupWebServer() } func connectMongo() { opts: = options.Client() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //データベースに To-Do をシードする docs: = [] interface {} { bson.D { { "id", "1" }, { "title", "Buy groceries" } }, bson.D { { "id", "2" }, { "title", "install Aspecto.io" } }, bson.D { { "id", "3" }, { "title", "Buy dogz.io domain" } }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) } func setupWebServer() { r: = gin.Default() r.GET("/todo", func(c * gin.Context) { collection: = client.Database("todo").Collection("todos") //重要: c ではなく c.Request.Context() をコンテキストとして渡すこと - TBD cur, findErr: = collection.Find(c.Request.Context(), bson.D {}) if findErr != nil { c.AbortWithError(500, findErr) return } results: = make([] interface {}, 0) curErr: = cur.All(c, & results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }
これでシンプルな To-Do アプリの準備ができました。OpenTelemetry を導入しましょう。
ステップ2: OpenTelemetry Go をインストールする
Go アプリを計装するために OpenTelemetry を設定します。
OTel SDK をインストールするには、以下を実行します。
go get go.opentelemetry.io/otel / go.opentelemetry.io/otel/sdk /Gin と Mongo のライブラリを計装してトレースを生成する。
Gin と Mongo の計装: otelgin と otelmongo をインストールする
go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin / go get go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo
Gin の計装: gin.Context
先ほどコンテキスト伝搬の考え方について触れました。 これは、分散サービス間でメタデータを転送し、システム内のイベントを関連付ける方法です。
Gin フレームワークには、独自の型である gin.Context があり、HTTP ハンドラーにパラメーターとして渡されます。 しかし、mongo のオペレーションに渡すべきコンテキストは、gin.Context.Request.Context で利用可能な標準 Go ライブラリの Context オブジェクトです。
//c ではなく c.Request.Context() をコンテキストとして渡すこと
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
MongoDB のオペレーションに Context を渡すようにしてください。 詳しくはこちらのイシューを確認してください。
これで、To-Do アプリの準備と計装が完了しました。 OpenTelemetry を最大限に活用しましょう。 トレースを可視化できることが、このテクノロジーの真のトラブルシューティング能力を発揮するところです。
可視化には、オープンソースの Jaeger Tracing を使用します。
Jaeger による可視化
OpenTelemetry Go と Jaeger Tracing: トレースを Jaeger にエクスポートする
Jaeger Tracing は、分散トレーシングの「スタック」全体(クライアント、コレクター、UI)を管理するオープンソースプロジェクト群です。 Jaeger UI は、トレースを可視化するために最もよく使われるオープンソースです。
セットアップは次のようになります。
Jaeger エクスポーターをインストールする
go get go.opentelemetry.io/otel/exporters/jaegertracing フォルダーと jaeger.go ファイルを作成する
ファイルに以下のコードを追加する
package tracing import ( "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" ) func JaegerTracerProvider()(*sdktrace.TracerProvider, error) { exp, err: = jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces"))) if err != nil { return nil, err } tp: = sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("todo-service"), semconv.DeploymentEnvironmentKey.String("production"), )), ) return tp, nil }main.go ファイルに戻り、先ほど作成した JaegerTracerProvider 関数を使用するようにコードを変更する
func main() { tp, tpErr: = tracing.JaegerTracerProvider() if tpErr != nil { log.Fatal(tpErr) } otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext {}, propagation.Baggage {})) connectMongo() setupWebServer() }次に、インストールした計装をフックアップします。
connectMongo 関数に以下の行を追加して、Mongo の計装を追加する
opts.Monitor = otelmongo.NewMonitor()関数は以下のようになります。
func connectMongo() { opts: = options.Client() //Mongo OpenTelemetry 計装 opts.Monitor = otelmongo.NewMonitor() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //データベースに To-Do をシードする docs: = [] interface {} { bson.D { { "id", "1" }, { "title", "Buy groceries" } }, bson.D { { "id", "2" }, { "title", "install Aspecto.io" } }, bson.D { { "id", "3" }, { "title", "Buy dogz.io domain" } }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) }次に、Gin の計装を追加します。
startWebServer 関数で、gin インスタンスを作成した直後に以下の行を追加する
r.Use(otelgin.Middleware("todo-service"))関数は以下のようになります。
func startWebServer() { r: = gin.Default() //Gin OpenTelemetry 計装 r.Use(otelgin.Middleware("todo-service")) r.GET("/todo", func(c * gin.Context) { collection: = client.Database("todo").Collection("todos") //c ではなく c.Request.Context() をコンテキストとして渡すこと cur, findErr: = collection.Find(c.Request.Context(), bson.D {}) if findErr != nil { c.AbortWithError(500, findErr) return } results: = make([] interface {}, 0) curErr: = cur.All(c, & results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }完全な
main.goファイルは以下を参照してください。 これで Jaeger にエクスポートする準備が整いました。package main import ( "context" "log" "net/http" "github.com/aspecto-io/opentelemetry-examples/tracing" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" ) var client * mongo.Client func main() { //トレースを Jaeger にエクスポートする tp, tpErr: = tracing.JaegerTracerProvider() if tpErr != nil { log.Fatal(tpErr) } otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext {}, propagation.Baggage {})) connectMongo() startWebServer() } func connectMongo() { opts: = options.Client() //Mongo OpenTelemetry 計装 opts.Monitor = otelmongo.NewMonitor() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //データベースに To-Do をシードする docs: = [] interface {} { bson.D { { "id", "1" }, { "title", "Buy groceries" } }, bson.D { { "id", "2" }, { "title", "install Aspecto.io" } }, bson.D { { "id", "3" }, { "title", "Buy dogz.io domain" } }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) } func startWebServer() { r: = gin.Default() //gin OpenTelemetry 計装 r.Use(otelgin.Middleware("todo-service")) r.GET("/todo", func(c * gin.Context) { collection: = client.Database("todo").Collection("todos") //c ではなく c.Request.Context() をコンテキストとして渡すこと cur, findErr: = collection.Find(c.Request.Context(), bson.D {}) if findErr != nil { c.AbortWithError(500, findErr) return } results: = make([] interface {}, 0) curErr: = cur.All(c, & results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }
トレースを Jaeger にエクスポートする
go run main.goで todo-service を実行する。- トレースを生成するために、http://localhost:8080/todo に HTTP GET リクエストを送る。
- トレースを表示するために、http://localhost:16686/search で Jaeger を開く。
Jaeger UI が表示されます。 todo-service を選択して Find traces をクリックしてください。 右側にトレースが表示されるはずです。

Jaeger UI は todo-service の Go における OpenTelemetry トレースを表示します。 トレースをクリックすると、ドリルダウンしてさらに詳細を確認し、自分で調査を進めることができます。

まとめ
以上です!このガイドが参考になり、わかりやすかったことを願っています。 すべてのファイルは GitHub リポジトリ ですぐに使える状態で公開されています。
この記事の原文は、もともと Aspecto ブログに投稿されました。