OpenTelemetryをローカルのDocker環境で動かす

## OpenTelemetryとは
[OpenTelemetry（オープンテレメトリー）](https://opentelemetry.io/docs/what-is-opentelemetry/)は、マイクロサービスにおける性能や健全性を示す「テレメトリーデータ」を生成、管理するためのOSSです。

ちなみに、OpenTelemetryの公式サイトでは、以下のようにオブザーバビリティのフレームワークであると説明されています。
>OpenTelemetry is an Observability framework and toolkit designed to create and manage telemetry data such as traces, metrics, and logs.

### テレメトリーデータとは
テレメトリーデータとは、システムやアプリケーションの動作状態を示すデータのことで、主にログ・メトリクス・トレースのことを指します。

## デモ構成図
今回はExpressでのシンプルなアプリケーションを作成し、メトリクスデータをOpenTelemetry Collectorに渡す構成とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689023/3a5f42ba-c7aa-41da-b6b0-9274a06be86e.png)

## ファイル構成
```
.
├── instrumentation.js
├── main.js
├── package-lock.json
├── package.json
├── docker-compose.yml
└── otel-collector-config.yml
```

## デモ
### 計装するアプリ作成
まず計装に必要なパッケージをインストールします。
```
npm i express @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/sdk-metrics @opentelemetry/exporter-metrics-otlp-grpc @opentelemetry/api
```
次にOpenTelemetry SDKの設定を行います。
```javascript:instrumentation.js
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";

// OpenTelemetryのSDKを初期化する
const sdk = new NodeSDK({
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter(), // OTLPエンドポイントにテレメトリーデータを送信
  }),
  instrumentations: [
    getNodeAutoInstrumentations(), // 自動計装を有効化
  ],
});

// SDKを起動することで自動で計装が開始される
sdk.start();
```
メトリクス生成を含めたシンプルなエンドポイントを作成します。
```javascript:main.js
import express from "express";
await import("./instrumentation.js");
import { metrics } from "@opentelemetry/api";

const app = express();

app.get("/", (req, res) => {
  const meter = metrics.getMeter("test"); // プロバイダーからメーターを取得する。
  const countMetric = meter.createCounter('metric.counter'); // 新しいカウンターメトリクスを作成する
  countMetric.add(1); // カウントする
  res.send("OK")
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

```

### otel-collector-config.ymlの設定
ymlファイルにOpenTelemetry Collectorの動作を定義します。
```yaml:otel-collector-config.yml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: otel-collector:4317 # docker-compose.ymlで設定したポートを指定

processors:
  batch:

exporters:
  debug: # コンソールにテレメトリーデータを出力するためdebugを使用
    verbosity: detailed

extensions:
  health_check:

service:
  extensions: ["health_check"]
  pipelines:
    metrics: # メトリクスのための設定を定義
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]
```
### docker-compose.ymlの設定
```yml:docker-compose.yml
services:
  otel-collector:
    image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.116.1
    container_name: otel-collector
    volumes: # OpenTelemetryの設定ファイルをバインドマウント
      - ./otel-collector-config.yml:/etc/otel-collector-config.yml
    command: ["--config=/etc/otel-collector-config.yml"]
    ports:
      - "13133:13133"
      - "4317:4317" # OTLP gRPC用ポート

```

### docker-composeとアプリケーションを動作
ターミナルを3つ開き、それぞれで以下のコマンドを実行します。
```
$ docker compose up
```

```
$ node main.js
```

```
$ curl http://localhost:3000
```
### Dockerコンテナのログを確認
アプリケーションで生成した「metric.counter」というカウンター名がOpenTelemetryで設定したexporterによって出力されていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689023/3275d84d-c0ac-432e-88a6-c3a15f87b9e3.png)