こんにちは、@ymtdzzzです。
この記事はOpenTelemetry Advent Calendar 2022の4日目の記事です。3日目は@munisystemさんの「bridge を使って OpenCensus / OpenTracing から OpenTelemetry に段階的に移行する」でした。
違うネタでもう少し先に書かせていただく予定でしたが、ネタがあったのでせっかくなので滑り込みで書かせていただきます。
はじめに
2022年10月24日に、opentelemetry-demoがGAを迎えました。
https://opentelemetry.io/blog/2022/announcing-opentelemetry-demo-release/
このデモアプリはECサイトを模した作りになっており、それぞれ異なる言語で実装されたマイクロサービスで構成されています。そして、それぞれがOpenTelemetryの機能を最大限活かして実装されており、OpenTelemetryの機能や実装を確認するのに非常に参考になるリポジトリになっています。
リポジトリのREADMEによると、デモアプリの目的は下記の3点みたいです。
- Provide a realistic example of a distributed system that can be used to demonstrate OpenTelemetry instrumentation and observability.
- Build a base for vendors, tooling authors, and others to extend and demonstrate their OpenTelemetry integrations.
- Create a living example for OpenTelemetry contributors to use for testing new versions of the API, SDK, and other components or enhancements.
勝手に翻訳)
- OpenTelemetryの計装と可観測性を実証するのに使用できる、分散システムの現実的な例を提供する
- ベンダーやツール作成者、その他のユーザーがOpenTelemetry統合を拡張したり実証したりするための基盤を構築する
- OpenTelemetryのコントリビューターがAPI, SDKやその他のコンポーネントや新機能をテストするために使える生きた例を作成する
実際のプロダクトに導入するためのリファレンス実装を提供するのに加え、OpenTelemetry関連のリポジトリへの貢献する際の検証環境としても使えそうです。
私もちょこちょこPR送っているので、今回は後者に着目して色々試してみたいと思います。
Demoリポジトリ概要
初めにどこにどんな情報があるかをまとめておきます。
サービス一覧と実装言語
サービスと実装言語については下記のドキュメントに記載があります。
https://github.com/open-telemetry/opentelemetry-demo/blob/main/docs/service_table.md
実装ステータス
DemoでどこまでOpenTelemetryの機能をカバーできているかについてはこちら。
- Tracing
- Metrics
- Logging - APIとSDKはまだspecでdraftのステータスなのでまだっぽい。現状LogRecordはLoggingではなくTrace Eventで記録しているようです。
Manual Span Attributes
サービス固有の、手動でSpanに追加しているAttributesの一覧。
https://github.com/open-telemetry/opentelemetry-demo/blob/main/docs/manual_span_attributes.md
デプロイ方法
デプロイ方法はDockerとKubernetesそれぞれ下記のドキュメントにまとまっています。
OpenTelemetryの検証環境として使う
OpenTelemetryそれ自体の検証環境として使うための条件はこんな感じでしょうか。
- 個別のリポジトリ(言語別のinstrumentation libraryやcollector、contribなど)について、ローカルで変更してビルドしたバージョンをdemoに組み込めること
- exporterやバックエンドなどを自由に切り替えできること
それぞれ機能追加やバグ修正で動作確認するためには必要で、また、バグの再現確認でも必要になってきそうです。
いくつかリポジトリを取り上げてdemoへの組み込み方法を確認してみます。
opentelemetry-collector-contrib
例えばこのissue
https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/16538
prometheus receiverの設定が空の場合にpanicするというissueですが、configに設定してあげるだけで再現確認ができそうです。
せっかくなので手元のソースコードでビルドしたotelcolを組み込んで動作確認してみます。
まずはdemo側の設定にprometheus receiverを追加し、metrics pipelineに追加します。
receivers:
prometheus: # add prometheus
otlp:
protocols:
grpc:
...
service:
pipelines:
traces:
receivers: [otlp]
processors: [spanmetrics, batch]
exporters: [logging, otlp]
metrics:
receivers: [otlp, prometheus] # add prometheus
processors: [batch]
exporters: [prometheus, logging]
続いて、opentelemetry-collector-conribのソースをチェックアウトし、手元でビルドします。
# cloneしたディレクトリ
$ make docker-otelcontribcol
# ビルドしたimageを確認
$ docker images | grep otelcontribcol
otelcontribcol latest 12e57e115ec1 5 minutes ago 270MB
demoのリポジトリに戻り、docker-compose.yaml
のotelcol serviceのイメージを手元のimageに変更します。
otelcol:
image: otelcontribcol:latest
この状態で起動(docker compose up —no-build -d & docker compose logs otelcol
)すると、確かに再現しました。
otel-col | 2022-11-30T13:38:45.205Z info pipelines/pipelines.go:102 Receiver is starting... {"kind": "receiver", "name": "prometheus", "pipeline": "metrics"}
otel-col | panic: runtime error: invalid memory address or nil pointer dereference
otel-col | [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4766d4d]
otel-col |
otel-col | goroutine 1 [running]:
otel-col | github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver.gcInterval(...)
エラー内容的に、receiverのstart時に通るgcInterval()
のcfgがnilの状態を考慮できていない可能性が高そうなので、試しにnilチェックを入れてみます
func gcInterval(cfg *config.Config) time.Duration {
gcInterval := defaultGCInterval
if cfg == nil { // 追加
return gcInterval
}
if time.Duration(cfg.GlobalConfig.ScrapeInterval)+gcIntervalDelta > gcInterval {
gcInterval = time.Duration(cfg.GlobalConfig.ScrapeInterval) + gcIntervalDelta
この対応を入れて再度imageをビルドしてdemoを再起動すると、該当箇所のエラーは解消されましたが、今度は似たような別の場所でエラーが発生しました。
対応方針についてはissueやPRで議論する必要がありそうですが、こんな調子でローカルで開発できそうです。
ということでPR上げておきました(default configを使用する方針に変更)
https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/16584
今回のissueは再現方法が非常にシンプルなので元のリポジトリでシングルバイナリ+configで確かめられそうですが、色々なバックエンドやpipelineを組み合わせた実践的な構成で発生したエラーについては、demoの環境を使った方が検証しやすいと思います。
opentelemetry-go
続いて、instrumentation libraryを見てみます。今回はgolangで。よさげなissueを探す時間がなかったので組み込み方法だけ確認してみます。
基本的にinstrumentation libraryについては言語毎のパッケージ管理方法に拠ります。
demoの中でgolangで実装されているサービスはproductcatalogservice
とcheckoutservice
なので、今回はcheckoutservice
で使っているSDKをローカルの実装と置き換えてみようと思います。
opentelemetry-demoでの作業
demoのgo.mod
で、該当パッケージをローカルにreplaceする設定を追加します。
cd src/checkoutservice
go mod edit -replace go.opentelemetry.io/otel/[email protected]=./opentelemetry-go/sdk
demoのdocker imageをビルドする際には、外部ディレクトリにcloneしたopentelemetry-goを参照できない(build contextの親ディレクトリを参照できない)のでopentelemetry-go側でbase imageを作ってあげる必要があります。
opentelemetry-goでの作業
opentelemetry-goのリポジトリ直下に下記のようなDockerfile
を作成します(imageやworkdirはdemoの該当サービスのDockerfileからコピペでOK)。
FROM golang:1.19.2-alpine AS builder
WORKDIR /usr/src/app/
COPY ./ ./opentelemetry-go
これでベースイメージのworkdir直下にローカルのopentelemetry-goのソースが配置されるので、まずはこれをビルドします。
docker build -t opentelemetry-go-base:latest .
opentelemetry-demoでの作業
checkoutservice
のDockerfile
を一部変更し、先程作成したベースイメージからビルドするように変更します。また、go mod download
をgo mod tidy
に変更して、依存パッケージの解決をするようにしておきます。
https://github.com/open-telemetry/opentelemetry-demo/blob/main/src/checkoutservice/Dockerfile
#...
# FROM golang:1.19.2-alpine AS builder
# 先程作成したベースイメージを指定
FROM opentelemetry-go-base:latest AS builder
RUN apk add build-base protobuf-dev protoc
WORKDIR /usr/src/app/
#...
# RUN go mod download
RUN go mod tidy
最後に、imageをビルドしてdemoを起動します。
docker compose build checkoutservice
docker compose up --no-build -d
# ログを確認して、ローカルの実装が動いていることを確認
docker ps | grep checkout
# e3a78a7adcb9 ghcr.io/open-telemetry/demo:v1.1.0-checkoutservice ...
docker logs e3a78a7adcb9 | grep hello
# hello from local!
手元のopentelemetry-goのソースでdemoが動いています。これで検証や開発に使うことができるようになりました。
終わりに
今回は、私のように気軽にOpenTelemetryにコントリビュートしてみたい人向けにopentelemetry-demoを活用してみる方法について見てみました。
次回は@Hidekazu-Karinoさんです!