⛏️

OpenTelemetry Operator による自動計装の注入(Injection)を試す

に公開

前の記事で「もうちょっと定期的にアウトプットしていきたい」と宣言したので、早速、次のネタを書き始めます。

OpenTelemetry Operator

Kubernetes Operatorの実装の一つとして、OpenTelemetry Operatorというものが提供されています。
このOperatorは、OpenTelemetry Collectorの管理や、Kubernetes上で実行されるアプリケーションに対するゼロコード計装(自動計装)を行ってくれます。
特に後者、自動計装に関しては、Init Containerを通じて計装ライブラリを注入(Injection)します。
言語別のOpenTelemetryエージェント等をアプリケーションコンテナに組み込んで再作成するような必要がなくなるわけです。

https://opentelemetry.io/ja/docs/platforms/kubernetes/operator/

最近、検証用の環境で Operator を試してみたので、その際のログをベースにして紹介してみます。
なお、Splunk Distro of OpenTelemetryを利用しており、計装対象のアプリケーションは Java を用いています。

OpenTelemetry Operatorのデプロイ

OpenTelemetry公式のOperator導入については、以下のコマンドでの導入となります。

$ kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

Splunk Distro of OpenTelemetryを導入する際には、Kubernetes環境への導入に用いるHelmチャートを使用します。以下のようなオプションを追加して導入する形です。

# Helmリポジトリの取得
$ helm repo add splunk-otel-collector-chart https://signalfx.github.io/splunk-otel-collector-chart && helm repo update

# HelmによるOperator導入
$ helm install splunk-otel-collector \
--set="operatorcrds.install=true", \        ## CRD導入
--set="operator.enabled=true", \            ## Operator有効化
--set="splunkObservability.realm=$REALM" \
--set="splunkObservability.accessToken=$ACCESS_TOKEN" \
--set="clusterName=k8s-cluster-01" \
##
## その他いくつかのオプション...
##
splunk-otel-collector-chart/splunk-otel-collector

そうすると以下のような感じで、OpenTelemetry CollectorとOperatorが導入されます。

$ kubectl get pods
NAME                                                         READY   STATUS    RESTARTS   AGE
...
splunk-otel-collector-agent-znplh                            1/1     Running   0          47s
...
splunk-otel-collector-k8s-cluster-receiver-6fc897595-hzn8t   1/1     Running   0          47s
splunk-otel-collector-operator-86c996fcb5-6rt9s              2/2     Running   0          47s

その他にも、ゼロコード計装を行う上で必要となるリソースがさまざまに設定されています。
いくつかを抜粋しながら見てみましょう。

## Mutationを行うadmission webhookの定義を確認
$ kubectl get mutatingwebhookconfiguration.admissionregistration.k8s.io
NAME                                      WEBHOOKS   AGE
splunk-otel-collector-operator-mutation   3          2m4s

## 自動計装を司るInstrumentationリソースを確認
$ kubectl get otelinst
NAME                    AGE    ENDPOINT                                                            SAMPLER   SAMPLER ARG
splunk-otel-collector   6d2h   http://splunk-otel-collector-agent.default.svc.cluster.local:4317

ゼロコード計装の注入

マイクロサービス化されたSpring Petclinicのアプリケーションがこちらに公開されています。フレームワーク名から分かる通りJavaでの実装です。
https://github.com/spring-petclinic/spring-petclinic-microservices/

Kubernetes環境にデプロイするにあたっての構成ファイルを以下からダウンロードして今回は利用します。
https://github.com/signalfx/splunk-otel-collector-chart/blob/main/examples/enable-operator-and-auto-instrumentation/spring-petclinic/spring-petclinic.yaml

$ wget https://raw.githubusercontent.com/signalfx/splunk-otel-collector-chart/main/examples/enable-operator-and-auto-instrumentation/spring-petclinic/spring-petclinic.yaml
$ kubectl apply -f spring-petclinic.yaml

※コンテナ単位のメモリ上限が512MiBと設定されており、OOMKilledが発生したので、1GiBに増やしたりしました。

さて、注入を行う前に、まずは現在の設定を確認してみましょう。
先ほどのYAMLを開いてもいいし、実際に deployment を describe してみてもいいでしょう。
試しに、spring-petclinic-admin-server を開いてみます。
Pod TemplateにAnnotationは入っていなさそうです

$ kubectl describe deployment spring-petclinic-admin-server
...
Pod Template:
  Labels:  app.kubernetes.io/part-of=spring-petclinic
           io.kompose.network/spring-petclinic-default=true
           io.kompose.service=admin-server
  Containers:
   admin-server:
    Image:      springcommunity/spring-petclinic-admin-server
...

さて、では、先ほどのInstrumentationリソースを用いて、ゼロコード計装を注入してみましょう。
注入に際しては、以下のようにannotationを追加する必要があります。
今回はOpenTelemetry Collectorを default namespaceに導入しているので、以下のように指定します。

spec:
  template:
    metadata:
      annotations:
        instrumentation.opentelemetry.io/inject-java: "default/splunk-otel-collector"

今回は kubectl patch deployment で直接編集してしまいました。

$ kubectl get deployments -l app.kubernetes.io/part-of=spring-petclinic -o name | xargs -I % kubectl patch % -p "{\"spec\": {\"template\":{\"metadata\":{\"annotations\":{\"instrumentation.opentelemetry.io/inject-java\":\"true\"}}}}}"

これだけで自動計装が追加されます。
改めて、Pod Templateの設定を確認してみましょう

$ kubectl describe deployment spring-petclinic-admin-server
...
Pod Template:
  Labels:       app.kubernetes.io/part-of=spring-petclinic
                io.kompose.network/spring-petclinic-default=true
                io.kompose.service=admin-server
  Annotations:  instrumentation.opentelemetry.io/inject-java: true
  Containers:
   admin-server:
    Image:      springcommunity/spring-petclinic-admin-server
...

instrumentation.opentelemetry.io/inject-java: true が追加されました。

実行されているPodについても確認してみましょう。patch後に新しいPodが立ち上がっているはずです。describeしてみます。

$ kubectl get pod | grep spring-petclinic
spring-petclinic-admin-server-5689f68d7b-d9fzl                1/1     Running   1 (2m ago)      2m39s
spring-petclinic-api-gateway-5b776cf664-pbd8v                 1/1     Running   1 (119s ago)    2m39s
spring-petclinic-config-server-6c76999979-qj5zd               1/1     Running   0               2m38s
spring-petclinic-customers-service-6b794cd7d7-mlqtt           1/1     Running   1 (119s ago)    2m38s
spring-petclinic-discovery-server-df456b764-x7mxt             1/1     Running   2 (96s ago)     2m37s
spring-petclinic-vets-service-9fdb765d8-v4lq6                 1/1     Running   1 (2m1s ago)    2m37s
spring-petclinic-visits-service-6b9bcf7bcf-zw5kd              1/1     Running   1 (2m1s ago)    2m36s

$ kubectl describe pod spring-petclinic-admin-server-5689f68d7b-d9fzl
...
(中略)
...
Init Containers:
  opentelemetry-auto-instrumentation-java:
    Container ID:  containerd://2ba23f54d9026c83ea16e9c5e547d5642621f86eda74a53d1dd157ba0e4247c1
    Image:         ghcr.io/signalfx/splunk-otel-java/splunk-otel-java:v2.19.0
    Image ID:      ghcr.io/signalfx/splunk-otel-java/splunk-otel-java@sha256:a54fdfb32d66b662343b75a652274711ce90034a0fdd25f64e11c2fa06e4254c
    Port:          <none>
    Host Port:     <none>
    Command:
      cp
      /javaagent.jar
      /otel-auto-instrumentation-java/javaagent.jar
    ...
    (中略)
    ...
    Mounts:
      /otel-auto-instrumentation-java from opentelemetry-auto-instrumentation-java (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-r7zbc (ro)
Containers:
  admin-server:
    Container ID:   containerd://be65d8877c2de17cca550154eed25fe7eb03929cccfb0cd3daad559393ea1553
    Image:          springcommunity/spring-petclinic-admin-server
    Image ID:       docker.io/springcommunity/spring-petclinic-admin-server@sha256:4290089fd95e88c4201b6e60b14348a3a616bd1212d5d7572ef594fa28dcb082
    Port:           9090/TCP
    Host Port:      0/TCP
    ...
    (中略)
    ...    
    Environment:
      OTEL_NODE_IP:                           (v1:status.hostIP)
      OTEL_POD_IP:                            (v1:status.podIP)
      OTEL_RESOURCE_DISABLED_KEYS:           process.executable.path,process.command_args
      OTEL_JAVA_ENABLED_RESOURCE_PROVIDERS:  io.opentelemetry.instrumentation.resources.ContainerResourceProvider,io.opentelemetry.sdk.autoconfigure.EnvironmentResourceProvider,io.opentelemetry.instrumentation.resources.ProcessResourceProvider
      OTEL_EXPORTER_OTLP_ENDPOINT:           http://splunk-otel-collector-agent.default.svc.cluster.local:4318
      JAVA_TOOL_OPTIONS:                      -javaagent:/otel-auto-instrumentation-java-admin-server/javaagent.jar
      SPLUNK_PROFILER_ENABLED:               true
      SPLUNK_PROFILER_MEMORY_ENABLED:        true
      OTEL_SERVICE_NAME:                     spring-petclinic-admin-server
      OTEL_RESOURCE_ATTRIBUTES_POD_NAME:     spring-petclinic-admin-server-5689f68d7b-d9fzl (v1:metadata.name)
      OTEL_RESOURCE_ATTRIBUTES_NODE_NAME:     (v1:spec.nodeName)
      OTEL_PROPAGATORS:                      tracecontext,baggage,b3
      OTEL_RESOURCE_ATTRIBUTES:              splunk.zc.method=splunk-otel-java:v2.19.0,k8s.container.name=admin-server,k8s.deployment.name=spring-petclinic-admin-server,k8s.namespace.name=default,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.replicaset.name=spring-petclinic-admin-server-5689f68d7b,service.instance.id=default.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).admin-server,service.namespace=default
    Mounts:
      /otel-auto-instrumentation-java-admin-server from opentelemetry-auto-instrumentation-java (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-r7zbc (ro)

PodのInit Containerとして opentelemetry-auto-instrumentation-java というコンテナが追加されていることが確認できます。
このコンテナイメージに含まれている javaagent.jar/otel-auto-instrumentation-java ディレクトリにコピーしています。
このディレクトリは共有ボリュームになっていますね。

アプリケーションコンテナの方を見てみると、共有ボリュームをマウントしています。
そして、その共有ボリュームに含まれる自動計装用のjarファイルをアプリケーション起動時に読み込むように、JAVA_TOOL_OPTIONS 環境変数が設定されています。
その他にもいくつかの環境変数が追加設定されています(OTEL_から始まる環境変数もそうですし、今回はSplunk Distroを使用しているため、例えば SPLUNK_PROFILER_ENABLED などのSplunk固有の環境変数が確認できます)。

この状態でアプリケーションを動かしてみると、トレースデータが生成され、OpenTelemetry Collectorを経由してバックエンドサービスに送信されていきます。
今回はSplunk Observability Cloudをバックエンドサービスとして利用していますので、例えばこんな感じでサービスマップが生成されます。

この状態でアプリケーションを動かしてみると、トレースデータが生成され、OpenTelemetry Collectorを経由してバックエンドサービスに送信されていきます。
今回はSplunk Observability Cloudをバックエンドサービスとして利用していますので、例えばこんな感じでサービスマップが生成されます。

Splunk Observability Cloud - APM Service Map

このように、OpenTelemetry Operatorを使用することで、わずかなAnnotationの追加だけで自動計装を実現できました。

まとめ

OpenTelemetry Operatorを用いたKubernetes上でのゼロコード計装の注入について紹介しました。
トレースデータ生成のためにアプリケーションコンテナを再構築する必要がなく、Annotationを追加するだけで計装が始められるのは非常に便利です。
計装を始めていくハードルを下げることができますし、大規模な環境に対して展開していくのもきっと楽になるでしょう。

Happy Observability!

宣伝:Observability Conference Tokyo 2025

前回に引き続き、宣伝です。
2025年10月27日に Observability Conference Tokyo 2025 が開催されます。
オブザーバビリティについて語りつくす1日として、多くの実践やナレッジがシェアされ、議論されることになると思います。
私も運営メンバーの一人として、当日参加される皆さんに楽しんでもらえるようなイベントにするべく企画に携わらせていただいております。
徐々に参加登録者が増えつつあるようです。ご都合のつく方はお早めに参加登録のお申し込みをお願いします。

オフライン・オンラインのハイブリッド開催で、チケット好評発売中です。
詳細は公式サイトをご覧ください。
https://o11ycon.jp/

Discussion