📑

k8s(Kind)上で、Node.jsアプリケーションのTraceをOtel Collector経由でJaegerに送ってみる

2024/02/12に公開

概要

kubernetes上で、Node.jsアプリケーションのTraceをOpenTelemetry Collector経由でJaegerに送ってみました。

手順

  1. Kind 構築
  2. Jaeger インストール
  3. Node.js アプリケーションデプロイ
  4. OpenTelemetry Collector デプロイ

1. Kind 構築

k8s環境構築には、Kindを利用しています。

kind create cluster --config=kind.yaml

https://qiita.com/tomoyafujita/items/5a3c06705f62c5732bc5
を参考にさせていただきました。

kind.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  # Node.js アプリケーション用
  - hostPort: 30010
    containerPort: 30010
  # Jaeger 用
  - hostPort: 30020
    containerPort: 30020

2. Jaeger インストール

# https://www.jaegertracing.io/docs/1.52/operator/#prerequisite に記載されている前提条件を満たすため
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml
kubectl apply -f namespace.yaml

(しばらく待つ)

# https://www.jaegertracing.io/docs/1.52/operator/#installing-the-operator-on-kubernetes の通り
kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.52.0/jaeger-operator.yaml -n observability

(しばらく待つ)

kubectl apply -f jaeger.yaml

namespace.yaml

kind: Namespace
apiVersion: v1
metadata:
  name: observability
  labels:
    name: observability

---

kind: Namespace
apiVersion: v1
metadata:
  name: apps
  labels:
    name: apps

jaeger.yaml

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: jaeger
  namespace: observability

---

apiVersion: v1
kind: Service
metadata:
  name: jaeger-query-nodeport
  namespace: observability
spec:
  type: NodePort
  ports:
    - port: 16686
      targetPort: 16686
      nodePort: 30020
      protocol: TCP
  selector:
    app: jaeger

3. Node.js アプリケーションデプロイ

実装

https://opentelemetry.io/docs/languages/js/getting-started/nodejs/
を使います。
instrumentation.tsを下記のように変えます。

instrumentation.ts

import { NodeSDK } from "@opentelemetry/sdk-node";
import { Resource } from "@opentelemetry/resources";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import {
  PeriodicExportingMetricReader,
  ConsoleMetricExporter,
} from "@opentelemetry/sdk-metrics";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";

const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]: "backend-service",
});

const sdk = new NodeSDK({
  resource,
  traceExporter: new OTLPTraceExporter({
    url: `http://${process.env.HOST_IP}:4318/v1/traces`,
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new ConsoleMetricExporter(),
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

https://zenn.dev/chot/articles/a37937b5dca703
を参考にさせていただきました。

ts-otel-sandboxという名前で(名前はなんでもいいですが) docker image build します。

デプロイ

# localのイメージを利用するため
kind load docker-image ts-otel-sandbox:latest
kubectl apply -f ts-otel-sandbox.yaml

https://egashira.dev/blog/kind-loads-local-image
を参考にさせていただきました。

ts-otel-sandbox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ts-otel-sandbox
  namespace: apps
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ts-otel-sandbox

  template:
    metadata:
      labels:
        app: ts-otel-sandbox
    spec:
      containers:
        - image: ts-otel-sandbox
          imagePullPolicy: Never
          name: ts-otel-sandbox
          ports:
            - containerPort: 3000
              protocol: TCP
          env:
            - name: HOST_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
      dnsPolicy: ClusterFirst
      restartPolicy: Always

---

apiVersion: v1
kind: Service
metadata:
  name: ts-otel-sandbox
  namespace: apps
spec:
  type: NodePort
  ports:
    - port: 3000
      targetPort: 3000
      nodePort: 30010
      protocol: TCP
  selector:
    app: ts-otel-sandbox

4. OpenTelemetry Collector デプロイ

# https://opentelemetry.io/docs/kubernetes/getting-started/#preparation
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

# https://opentelemetry.io/docs/kubernetes/getting-started/#otlp-receiver
helm install otel-collector open-telemetry/opentelemetry-collector --values otel-collector/values.yaml

otel-collector/values.yaml

mode: daemonset

config:
  exporters:
    otlp:
      endpoint: "jaeger-collector.observability.svc.cluster.local:4317"
      tls:
        insecure: true
  service:
    pipelines:
      traces:
        receivers: [ otlp ]
        processors: [ batch ]
        exporters: [ otlp ]

最初、portを 4317 ではなく 14250 にしていて、全然送れなくてハマりました。
14250は、Jaeger固有のトレースデータを受け取るために利用します。
4317は、OTLP(OpenTelemetry Protocol)を使用して、トレース・メトリクス・ログを受け取るために利用します。
portのリストは下記にあります。
https://www.jaegertracing.io/docs/next-release/getting-started/

確認

  1. http://localhost:30010/rolldice を開く
  2. http://localhost:30020/ を開き、Serviceで backend-service を選択し、Find Tracesする
  3. Traceの一覧が表示されるのでクリックするとTraceが表示される

Discussion