Goで書いたAPIにOpenTelemetryを入れたい
まだログ基盤どころかそもそもエラーログさえ吐いていない モニタリングのモすらない Go製のAPIに OpenTelemetryを導入して、ログ/メトリクス/トレースの基盤を用意したいというスクラップです。
OpenTelemetryとは
運用のシステム内部の状態を理解/測定可能にする、オブザービリティ(可観測性)を実現するためのプロトコル。オブザービリティ3本柱、メトリクス、トレース、ログの3種を実現するためのプロトコル群。このプロトコルを採用すればサーバーベンダーにより異なるログの規格を統一にして、ベンダーロックインを避けることができるらしい。(要出典)元々は前身として、OpenTracingとOpenCensusというプロトコルがあったが、バラバラに定義するよりまとめて定義したらいいんじゃね?ってことでここ4-5年ぐらいでOpenTelemetryに一体化したらしい。(要出典)昨今のコンテナ基盤、分散したシステムを想定されたプロトコルだが、別に1サーバーに対して導入しても何も問題ない。(要出典)
メトリクスとは
- いわゆる統計情報。レイテンシーとか、エンドポイントのコール数とかを記録して、優先的に対応すべき箇所とかの兆候を知るのに使えそう。
トレースとは
- いわゆるデバッガーのスタックトレースからかかった処理時間だけを測ったものっぽい? システム内の各種関数の呼び出し/その区間を費やした時間を追跡できる。
- トレースの開始地点をNginx等のリクエスト受信時点にすることもでき、単に1サーバーだけでなくリクエストをさばくのに通った全サービスを追跡できるかもしれない。(大変そうなので今回は設定しない)
ログとは
- いわゆるログそのもの。通常のログと同じだけれど、トレースと紐づけができるので、関数の途中で何があったかとかをトレースと一緒に確認できる。
参考
- https://opentelemetry.io/
- https://www.splunk.com/ja_jp/data-insider/what-is-opentelemetry.html
- https://qiita.com/atsu_kg/items/c3ee8141e4638957a947
- https://zenn.dev/ww24/articles/beae98be198c94
- https://christina04.hatenablog.com/entry/opentelemetry-in-go
- https://note.com/asahi_ictrad/n/neb007069d1f2
OpenTelemetryに対応させる方法 (アプリにOpenTelemetryプロトコルを話させる方法)
公式ライブラリを使う
このプロトコルを策定しているのはかなり強力なコミュニティらしく、誰でも知ってるようなメジャー言語には、ほぼ公式に対応ライブラリを開発している。
23/10/14時点では下記のように記載されていた。
Language | Traces | Metrics | Logs |
---|---|---|---|
C++ | Stable | Stable | Stable |
C#/.NET | Stable | Stable | Stable |
Erlang/Elixir | Stable | Experimental | Experimental |
Go | Stable | Stable | Not yet implemented |
Java | Stable | Stable | Stable |
JavaScript | Stable | Stable | Development |
PHP | Release candidate | Release candidate | Release candidate |
Python | Stable | Stable | Experimental |
Ruby | Stable | Not yet implemented | Not yet implemented |
Rust | Beta | Alpha | Alpha |
Swift | Stable | Experimental | In development |
コミュニティライブラリを使う
上に載っていないものとかExperimental / Not yet implementedは全く使えないのかというと、そんなことはない。サイト内に詳しく記載されていないが、3rdパーティライブラリ用に作られた固有実装がかなりある。上の表は言語の公式実装に対応しているかの表であり、GoのLogsは Not yet implemented
と書いてあるが、ZapやLogrus用の実装ならある。
具体的にどれに対応しているかはRegistryで検索するとわかる。
対応していない場合はプロトコルのドキュメントがあるので、自前で気合で実装することもできる。(zapについてはuptraceの実装したものもある)
ログ周りは仕様が確定したのが割りと最近のようなので、ライブラリがない可能性があるかもしれない。(要出典)
参考
OpenTelemetryに対応させる方法 (OpenTelemetryプロトコルを受け取り可視化する方法)
OpenTelemetry自体はプロトコルなので、このプロトコルのデータを集約し、表示する方法が必要。OpenTelemetryのプロトコルを受け付けることができるものは近年増えているようで、かなり色々選択肢がある。筆者のAPIは個人開発のしょぼAPIかつ、VPSを借りて定額運用とかするのが好きなので、できればセルフホスト可能な方法を探してみた。
大手クラウド系
AWSならAWS Distro、GCPならGoogle Cloud Traceを使ったりできそう。
- https://recruit.gmo.jp/engineer/jisedai/blog/gcp-cloud-trace/
- https://zenn.dev/satohjohn/articles/e37e8575966204
Sentry
よく聞く有名なログ可視化サービス。OpenTelemetryも対応しているらしい。
OSS版もある。
Grafana
コンテナ界隈だとよく聞くログ可視化基盤OSS。OpenTelemetryも対応しているらしい。
OSS版もある。
- https://grafana.com/
- https://github.com/grafana/grafana
- https://grafana.com/docs/opentelemetry/
- https://qiita.com/hir00/items/1339f81ffba155195e17
SigNoz
Gigazineで紹介されていたOSS。DatadogやNewRelicの代替環境を自称している。見た目は ダークテーマ風。見た感じExceptionだけの表示やエラーアラートも設定できてかなり多機能そう。Goアプリとの連携方法を丁寧に記事とリポジトリで紹介してくれているので連携はしやすそう。OSS版もある。
- https://signoz.io/
- https://github.com/SigNoz/signoz
- https://github.com/SigNoz/sample-golang-app
- https://signoz.io/opentelemetry/go/
- https://signoz.io/docs/install/docker/
- https://gigazine.net/news/20230723-signoz/
UpTrace
Go言語周りのライブラリ向けのOpenTelemetry実装を大量に出してくれている開発元のサービス。OSS版もある。
結論セルフホストするには結構重そうなので Sentryにエクスポートすることにする。
ここまで調べたところで、ようやく使うライブラリをまとめる。
今利用しているライブラリにはありがたいことに コミュニティライブラリがすべて揃っていたので対応するライブラリを使う。対応ライブラリは下記表にまとめた。
利用ライブラリ | 対応するメトリクスライブラリ |
---|---|
net/http | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp |
mux/router | go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux |
zap | github.com/uptrace/opentelemetry-go-extra/otelzap |
gorm v2 | get github.com/uptrace/opentelemetry-go-extra/otelgorm |
参考
- https://christina04.hatenablog.com/entry/opentelemetry-in-go
- https://blog.cybozu.io/entry/2023/04/12/170000
- https://uptrace.dev/get/instrument/opentelemetry-net-http.html
- https://uptrace.dev/get/instrument/opentelemetry-gorilla-mux.html
- https://uptrace.dev/get/instrument/opentelemetry-gorm.html
- https://uptrace.dev/get/instrument/opentelemetry-zap.html
公式チュートリアルを参考にSentryつないでみて、一応Web画面上にちゃんと出るようになった。
が、Sentryのフロントエンド向けのテレメトリ機能を今回使わないため、左のメニューのほとんどが機能せず、使えないメニューだらけになってしまう? のがイマイチに感じた。とりあえずSigNoz←セルフホストがしんどそうなのでUptraceにしてみる。
UpTraceもSentryのようにかなりしっかりしたクイックスタートがあるので、これを参考にする。
UpTraceに上げてみた例はこちら。(実装をかなりガリガリいじってSpanを実装レイヤー毎に出したりできるようにしている)
Sentryと違い、OpenTelemetry前提で作られているので動くメニューしかなく割りとシンプル。
フィルタ絞り込み方法と、Traceが見れる場所がややわかりづらいが慣れたら良さげ。
とりあえずotelhttp、otelmuxとtracerを使ったトレースだけなら簡単にできたが
otelzapは、そもそも今のプロジェクトがロガー/zapをきれいに使えていなかったため、大規模にほぼ全エンドポイントの変更が発生した。(後で記事にする)
補足: ローカル または シンプルに1サーバーで動かす場合のおすすめ
簡単に動くセルフホストTracingPlatformがほしい場合はJaegerがとてもおすすめ。必要十分な機能を揃えてシンプル、かつ1コンテナで動く最小パッケージが配布されていて一瞬で起動できる。元々は前身であるOpenTracing用だったが、2022年辺りからOpenTelemetryのネイティブサポートをしている。
(ただ、少しでもカスタマイズをしようとすると、元々OpenTelemetry専用なソフトウェアではないことから色々と混乱させられるので、最小限で使うのがいいと思う...)
筆者環境で動いた最小構成
docker run -d --name jaeger2 \
-e COLLECTOR_OTLP_ENABLED=true \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:1.53
docker run -d --name jaeger2 -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one:1.53
ポート内訳
4317: OpenTelemetry Protocol (OTLP) over gRPC のログ受付ポート
4318: OpenTelemetry Protocol (OTLP) over HTTP のログ受付ポート
16686: ログ閲覧用フロントエンド
参考