🦑

docker-composeでOpenTelemetryに入門してみる for Laravel

に公開

Social Databank Advent Calendar 2025 の10日目です。

こんにちは、zinです🦑

APMの機運が高まってきたので、まずは開発環境でログ・トレース・メトリクスを確認できる環境を作ってみました。
まだ動作イメージが持てる程度の理解度ですが、これから育てていこうという気持ちと共に記事にしてみました。

サンプルコードはこちらに置いてあります。

https://github.com/zinkosuke/local-otel-laravel

構成するコンポーネント

今回の環境では、以下のコンポーネントをdocker-composeで起動します。

OpenTelemetry Collector

テレメトリデータ(ログ、トレース、メトリクス)を受信し、適切なバックエンドに振り分ける中継役です。アプリケーションからはこのCollectorにデータを送信するだけで済むため、バックエンドの変更がアプリケーションに影響しません。

https://opentelemetry.io/docs/collector/

Grafana

可視化のためのダッシュボードです。Loki、Prometheus、Tempoからデータを取得して、統合的に確認できます。

https://grafana.com/docs/grafana/latest/

Prometheus

メトリクス(CPU使用率、メモリ使用量、HTTPリクエスト数など)を時系列で保存するデータベースです。

https://prometheus.io/docs/introduction/overview/

Loki

ログを保存するデータベースです。Grafana Labsが開発しており、Grafanaとの連携が非常にスムーズです。

https://grafana.com/docs/loki/latest/

Tempo

分散トレーシングのデータを保存するデータベースです。HTTPリクエストの流れを追跡できます。

https://grafana.com/docs/tempo/latest/

データの流れ

今回の構成では、以下のような流れでデータが処理されます。

Laravelアプリケーション
  ↓
OpenTelemetry Collector
  ├→ Prometheus (メトリクス)
  ├→ Loki (ログ)
  └→ Tempo (トレース)
  ↓
Grafana (可視化)

Laravelアプリケーションは、すべてのテレメトリデータをOpenTelemetry Collectorに送信します。Collectorは受け取ったデータの種類に応じて、適切なバックエンド(Prometheus、Loki、Tempo)に振り分けます。

OpenTelemetry Collectorの設定

otel/config.yamlで、Collectorの動作を定義します。

otel/config.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
  otlp:
    endpoint: tempo:4317
    tls:
      insecure: true
  otlphttp/loki:
    endpoint: http://loki:3100/otlp
    tls:
      insecure: true

service:
  pipelines:
    metrics:
      receivers: [ otlp ]
      exporters: [ prometheus ]
    traces:
      receivers: [ otlp ]
      exporters: [ otlp ]
    logs:
      receivers: [ otlp ]
      exporters: [ otlphttp/loki ]

Laravel側の設定

1. PHP拡張のインストール

DockerfileでOpenTelemetry PHP拡張をインストールします。

Dockerfile
RUN pecl install opentelemetry \
 && docker-php-ext-enable opentelemetry

この拡張により、PHPアプリケーションからOpenTelemetryの機能を利用できるようになります。

2. Composerパッケージのインストール

composer.jsonに以下のパッケージを追加します。

composer.json
{
  "require": {
    "open-telemetry/exporter-otlp": "^1.3",
    "open-telemetry/opentelemetry-auto-guzzle": "^1.2",
    "open-telemetry/opentelemetry-auto-laravel": "^1.4",
    "open-telemetry/opentelemetry-auto-psr18": "^1.1",
    "open-telemetry/sdk": "^1.10"
  }
}

3. 環境変数の設定

docker-compose.ymlで、LaravelコンテナにOpenTelemetry関連の環境変数を設定します。

docker-compose.yml
# SDK有効化
OTEL_SDK_DISABLED: "false"
OTEL_PHP_AUTOLOAD_ENABLED: "true"

# サービス情報
OTEL_SERVICE_NAME: laravel-app
OTEL_RESOURCE_ATTRIBUTES: deployment.environment=dev,service.version=dummy

# エンドポイント設定
OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: http://otel-collector:4318/v1/logs
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: http://otel-collector:4318/v1/metrics
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://otel-collector:4318/v1/traces

# 各種シグナルのエクスポーター指定
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_TRACES_EXPORTER: otlp

4. カスタムメトリクスの送信(オプション)

自動計装だけでなく、アプリケーション固有のメトリクスを送信したい場合は、独自のメトリクスサービスを実装できます。

MetricsServiceの実装

app/Telemetry/MetricsService.phpを作成し、OpenTelemetryのMeterProviderをラップします。
ServiceProviderに登録する方法はここでは割愛します。

app/Telemetry/MetricsService.php
<?php

namespace App\Telemetry;

use Closure;
use OpenTelemetry\API\Metrics\CounterInterface;
use OpenTelemetry\API\Metrics\MeterInterface;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MeterProviderFactory;

class MetricsService
{
    private string $prefix;
    private MeterProvider $meterProvider;
    private MeterInterface $meter;
    private array $counters = [];

    public function __construct(string $prefix)
    {
        $this->prefix = $prefix;
        $this->meterProvider = (new MeterProviderFactory)->create();
        $this->meter = $this->meterProvider->getMeter($prefix);
    }

    public function forceFlush(): void
    {
        $this->meterProvider->forceFlush();
    }

    public function increment(
        string $name,
        float|int $value = 1,
        array $attributes = []
    ): void {
        $metricName = "{$this->prefix}.{$name}";
        if (!isset($this->counters[$metricName])) {
            $this->counters[$metricName] = $this->meter->createCounter($metricName);
        }
        $this->counters[$metricName]->add($value, $attributes);
    }
}

使用例

ミドルウェアやコントローラーで簡単に使えます。

// HTTPリクエスト数をカウント(ルート別)
\Metrics::increment('http_request_count', attributes: [
    'uri' => \Route::getCurrentRoute()->uri,
]);

この実装により、自動計装では取得できないビジネス固有のメトリクスを送信できるようになります。

動かしてみる

1. コンテナの起動

初回はLaravelコンテナのビルドとComposerの依存関係インストールに時間がかかります(5〜10分程度)。

docker compose up -d

2. Laravelアプリケーションにアクセス

サンプルで以下3つのエンドポイントを用意しています。いずれもLaravelのwelcomeページが表示されます。
Grafanaで確認するためにたくさんアクセスしておきましょう。

3. Grafanaでデータを確認

ある程度データが溜まったらブラウザで http://localhost:3000/dashboards にアクセスします。

  • ユーザー名: admin
  • パスワード: password

Default APMというダッシュボードが作られているので、開きます。
まだGrafana力がヨワヨワなため見た目はご愛嬌ですが、ログ・トレース・メトリクスが届いていることは確認できました。

おわりに

ひとまずdocker-composeを使って自動計装や独自のメトリクス定義を動かす方法がわかりました。
OpenTelemetryでできることはもっとたくさんあるので道半ばといったところですが、今後理解が深まってきたら続編記事を出したいと思います。

ソーシャルデータバンク テックブログ

Discussion