Mackerel で OpenTelemetry のラベル付きメトリックを使ってみよう

ラベル付きメトリックのチュートリアル

このチュートリアルでは AWS の ECS 上で稼働している Nginx を対象に、ゼロからラベル付きメトリックを収集して Mackerel のダッシュボード上でグラフを表示するところまでをゴールとします。

なお、Mackerel のラベル付きメトリック機能は本記事執筆時点(2023 年 9月)でベータ版となっています。そのため、Mackerel のウェブコンソール上の画面は実際のものと異なる場合があります。

現在、「ラベル付きメトリック機能」ベータ版テストの参加者を募集しています。下記のフォームよりベータ版テストへの参加お申し込みをいただけます。

forms.gle

OpenTelemetry コレクターを設定する

OpenTelemetry コレクターは、テレメトリーデータの受信・処理・エクスポートについてベンダーに依存しない実装を提供します。テレメトリーデータをバックエンドから直接エクスポートするのではなく、コレクターを経由することには、以下のようなメリットがあります。

  • バックエンドからはエクスポート先を意識せずにテレメトリーデータを送信できる
  • データを加工したり、複数のエクスポーター(Mackerel, Jaeger, Prometheus)へ送信したりできる
  • パフォーマンスを最適化できる

OpenTelemetry コレクターは YAML ファイルで設定を記述することで、どこから来たデータをどのように加工して送信するかを定義します。 設定は以下の 4 つのコンポーネントから構成されています。

  • Receivers: テレメトリーデータをコレクターに取り込む方法
  • Processors: テレメトリーデータを加工する
  • Exporters: テレメトリーデータをバックエンドにエクスポートする
  • Connectors: 2 つのパイプラインを接続する(今回のチュートリアルでは使用しません)

これらのコンポーネントは service セクション内の pipelines で定義しない限り、有効となりません。今回は、以下のような設定を記述します。

receivers:
  nginx:
    endpoint: "http://localhost:80/status"
    collection_interval: 10s

processors:
  batch:
    timeout: 1m

exporters:
  otlp/mackerel:
    endpoint: otlp.mackerelio.com:4317
    compression: gzip
    headers:
      Mackerel-Api-Key: ${env:MACKEREL_APIKEY}

service:
  pipelines:
    metrics:
      receivers: [nginx]
      processors: [batch]
      exporters: [otlp/mackerel]

それぞれのセクションについて詳しく見ていきましょう。

receivers

receivers はデータをコレクターに取り込む方法です。多くのレシーバーにはデフォルトの設定が付属しているので、レシーバー名を指定するだけで利用できます。

ここでは Nginx からデータを収集するため、Nginx Receiver をレシーバーとして指定しています。このレシーバーは、Nginx の ngx_http_stub_status_module で提供される status エンドポイントからメトリックを取得します。

receivers:
  nginx:
    endpoint: "http://localhost:80/status"
    collection_interval: 10s

Nginx Receiver は以下のオプションを必ず指定する必要があります。

  • endpoint: Nginx の status エンドポイントの URL。Nginx 側の設定に応じて、適宜変更が必要。

任意で以下の 2 つの設定を行えます。

  • collection_interval: データを収集する間隔。デフォルトは 10 秒。
  • initial_delay:データを収集する前に待機する時間。デフォルトは 1 秒。

Nginx Receiver がメトリックを取得できるよう、Nginx 側で ngx_http_stub_status_module を有効にする必要があります。これには Nginx の設定の server または location ディレクティブに以下のように追加します。location に指定するパス名 /status は、 endpoint で指定した URL と一致していなければなりません。

location /status {
  stub_status;
}

processors

processors はテレメトリーデータを加工するもので、データを受信してからエクスポートされるまでの間に実行されます。デフォルトでは有効となっているプロセッサーはありませんが、データソースによって推奨されるプロセッサーがあります。

今回の例では batch プロセッサーを指定しています。batch プロセッサーは、指定した時間ごとにデータをバッチ処理してエクスポートするプロセッサーです。バッチ処理により、データがより適切に圧縮され、データの送信に必要な発信接続の数が削減されます。batch プロセッサーはすべてのコレクターで有効にすることを強く推奨します。

ここでは timeout セクションに 1m を設定して、1 分ごとにデータをバッチ処理しています。

processors:
  batch:
    timeout: 1m

exporters

exporters は複数のバックエンド/デスティネーションにデータを送信する方法を定義します。例えば、Mackerel, Prometheus, Jaeger などのシステムに対して設定を記述し service セクションのパイプラインで指定することで、1 つのコレクターから複数のバックエンドにデータを送信できます。多くの場合、エクスポーターにはエンドポイントと認証情報の設定を記述することになります。

今回は Mackerel にデータを送信するために、otlp/mackerel を指定します。ここで指定するキー名は、後ほど service セクションの pipeline で指定するために使用するものです。/ 以降の文字は、ユニークな名前であれば任意の名前を指定できます。

endpoint はデータの送信先です。この設定は Mackerel を利用する際は固定の値で otlp.mackerelio.com:4317 となります。Mackerel では認証方式として API キーを使用するため、headers.Mackerel-Api-Key に API キーを設定しています。ここでは API キーは環境変数から取得するようにしています。

exporters:
  otlp/mackerel:
    endpoint: otlp.mackerelio.com:4317
    compression: gzip
    headers:
      Mackerel-Api-Key: ${env:MACKEREL_APIKEY}

ここでは、MACKEREL_APIKEY という名前で環境変数を設定することを想定しています(環境変数は後ほど設定します)。

service

service セクションは定義されたコンポーネントの中でどれを有効にするかを定義します。service セクション内で使用されていないコンポーネントは有効となりません。

service セクションには pipelines セクションがあり、ここでレシーバー、プロセッサー、エクスポーターのセットを構成します。pipelines セクションには、トレース(trace)、メトリック(mtrics)、ログ(logs)のデータを処理するパイプラインのセクションをそれぞれ定義します。

今回はメトリックのみを処理するので metrics のみを使い、service セクションの外側でこれまで定義してきたレシーバー、プロセッサー、エクスポーターのセットのセットを指定します。

service:
  pipelines:
    metrics:
      receivers: [nginx]
      processors: [batch]
      exporters: [otlp/mackerel]

この設定では、Nginx Receiver で収集したデータを batch プロセッサーでバッチ処理し、otlp/mackerel エクスポーターで Mackerel にデータを送信する、というパイプラインを定義しています。

OpenTelemetry コレクターを起動する

OpenTelemetry コレクターを実行するには、テレメトリーデータを収集する対象のアプリケーションと同じホストにインストールする必要があります。今回は、Docker を使用して先ほどの設定ファイルを読み込んだイメージを作成し、そのイメージをもとにコンテナを起動します。

Docker イメージを作成する

まずはイメージを作成するために、以下の Dockerfile を作成します。Docker Hub で OpenTelemetry より公開されている otel/opentelemetry-collector-contrib イメージをベースとして、先ほどの設定ファイルをコンテナ内にコピーします。

opentelemetry-collector-contrib は、多くのコンポーネントがあらかじめ含まれているイメージです。さまざまな構成を試すために、このイメージをベースにするのは妥当な選択です。しかし、本番環境では、環境に必要なコンポーネントのみを含めたイメージを作成することが以下の理由から推奨されます。

  • コレクターのサイズを縮小し、コレクターのデプロイメント時間を短縮する
  • 利用可能な攻撃対象領域を減らすことで、コレクターのセキュリティを向上させる

OpenTelemetry Collector Contrib Distro - Recommendation

本番環境では otel/opentelemetry-collector をベースイメージとして必要なプラグインのみを含めてビルドすることをお勧めします。

設定ファイルの名前は otel-collector-config.yaml とし、Dockerfile と同じディレクトリに配置します。設定ファイルは起動時の引数で指定するため、CMD で設定ファイルのパスを指定しています。

FROM otel/opentelemetry-collector-contrib:latest
COPY otel-collector-config.yaml /etc/otel-collector-config.yaml
CMD ["--config=/etc/otel-collector-config.yaml"]

次に、Dockerfile と同じディレクトリで以下のコマンドを実行してイメージを作成します。このチュートリアルでは ECS を amd64 アーキテクチャで使用するため、--platform linux/amd64 を明示しています。

docker build -t otel-collector --platform linux/amd64 .

作成したイメージを ECS から利用できるようにするために、ECR にリポジトリを用意し、イメージをプッシュします。まずは ECR のリポジトリを作りましょう。AWS コンソールから直接作成してもよいのですが、ここでは Terraform を使用して作成することにします。以下のような main.tf を作成します。

resource "aws_ecr_repository" "repo" {
  name = "nginx-opentelemetry-collector"

  image_tag_mutability = "MUTABLE"
}

terraform init で初期化し、terraform apply でリポジトリを作成します。

terraform init
terraform apply

これで、nginx-opentelemetry-collector という名前の ECR リポジトリが作成されました。次に、イメージをプッシュします。ECR にイメージをプッシュするためのコマンドは、AWS コンソールの「AmazonECR」>「リポジトリ」>「nginx-opentelemetry-collector(作成したリポジトリ名)」>「プッシュコマンドの表示」から確認できます。基本的には、表示されるコマンドに沿って実行すれば問題ありません。

AWS コンソールのスクリーンショット。AmazonECR > リポジトリ > nginx-opentelemetry-collector の画面が表示されていて、プッシュコマンドの表示ボタンが赤い枠で囲まれている

正常にコマンドの実行が完了していれば、ECR にイメージがプッシュされているはずです。ECR のコンソールから確認してみましょう。

ECS に OpenTelemetry コレクターをデプロイ

ECR にイメージをプッシュできたので、このイメージを ECS にデプロイして OpenTelemetry コレクターを実行できるようにしましょう。

ECS タスク定義 に、先ほど ECR にプッシュした OpenTelemetry コレクターのイメージをタスクの情報として JSON 形式で記述します。

{
  "containerDefinitions": [
    {
      "cpu": 0,
      "essential": false,
      "image": "{ECR の URL}/nginx-opentelmetry-collector:latest",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/aws/ecs/nginx",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "otel-collector"
        }
      },
      "memory": 64,
      "name": "otel-collector",
      "secrets": [
        {
          "name": "MACKEREL_APIKEY",
          "valueFrom": "{AWS Parameter Store のパラメータ名}"
        }
      ]
    }
  ],
}

コンテナの定義は containerDefinitions の配列の要素として記述します。image には ECR リポジトリの名前とイメージ名を指定します。logConfiguration にはコンテナのログの設定を記述します。ここでは CloudWatch Logs にログを送信するように設定しています("logDriver": "awslogs")。

secrets にはコンテナ内で使用する環境変数を指定します。ここでは Mackerel の API キーを環境変数として指定しています。この環境変数は otel-collector-config.yaml ファイル内で ${env:MACKEREL_APIKEY} と指定した箇所で使用されます。MACKEREL_APIKEY の値は valueFrom から Parameter Store のパラメータ名を指定するように設定しています。

Parameter Store にはあらかじめ Mackerel の API キーを保存しておく必要があります。これは、Mackerel のウェブコンソールの「オーガニゼーション詳細」>「API キー」から確認できます。

Mackerel のウェブコンソールのスクリーンショットショット。オーガニゼーション詳細画面で API キーのタブが選択されていて、default の API キーがマスクされた状態で表示されている。

上記のタスク定義をもとに、ECS にデプロイしましょう。これで、OpenTelemetry コレクターの起動が完了しました!

Mackerel のカスタムダッシュボードからグラフを作成する

OpenTelemetry コレクターの起動が完了してしばらくたつと、Mackerel のカスタムダッシュボード上でメトリックの一覧を表示できるようになります。API キーを取得したオーガニゼーションの Mackerel のウェブコンソールから「ダッシュボード」>「カスタムダッシュボードを追加」を選択してください。

https://mackerel.io/my/dashboards

Mackerel のウェブコンソールのスクリーンショット。ダッシュボード一覧の画面でカスタムダッシュボードを追加ボタンが赤い枠で囲まれている。

カスタムダッシュボードを追加する画面が表示されたら、グラフウィジェットを選択してグリッド上にドラッグ&ドロップしてください。

Mackerel のウェブコンソールのスクリーンショット。カスタムダッシュボードを追加する画面でグラフウィジェットが赤い枠で囲まれている

グラフウィジェットを追加するダイアログが表示されたら、「グラフのタイプ」で「クエリグラフ」を選択しましょう(「クエリグラフ」はベータ版利用者のみに表示されます)。PromQL を記述するエディタが開き、上部のメニューからメトリック一覧を表示できます。

Mackerel のウェブコンソールのスクリーンショット。クエリグラフの設定画面でメトリック一覧が表示されている。

正しくメトリックを収集できていれば、以下のメトリックが表示されているはずです。

  • nginx.requests
  • nginx.connections_current
  • nginx.connections_accepted
  • nginx.connections_handled

例として nginx.connections_current を選択してみると、以下のようにグラフが表示されます!(もし 0 しか表示されない場合には、グラフの表示期間を 30m など短い期間に変更してみてください)

Mackerel のウェブコンソールのスクリーンショット。クエリグラフの設定画面で nginx.connections_current が選択されていて、グラフが表示されている。

デフォルトでは、凡例はクエリとラベルを結合したものが設定されます。nginx.connections_current のクエリで表示されるグラフの凡例は、以下のように表示されていることがわかります。

  • nginx.connections_current{state="active"}
  • nginx.connections_current{state="waiting"}
  • nginx.connections_current{state="writing"}
  • nginx.connections_current{state="reading"}

Mackerel のウェブコンソールのスクリーンショット。クエリグラフの設定画面で nginx.connections_current が選択されていて、グラフのラインにマウスカーソルがホバーされている。マウスカーソルの上にはグラフの凡例が表示されている。nginx.connections_current{state="active"} - 13.00、nginx.connections_current{state="waiting"} - 10.00、nginx.connections_current{state="writing"} - 3.00、nginx.connections_current{state="reading"} - 0.00

ラベルの数が多くなると凡例の長さも非常に長くなり、グラフのメモリがどのラベルに対応するものなのかわからなくなってしまうかもしれません。そこで、凡例の表示方法を変更してみましょう。

凡例の表示方法は設定から変更できます。設定画面の「凡例」から「値」のセレクトボックスを選択すると、現在のクエリに対応するラベルの一覧が表示されます。ここでは state を選択してみましょう。

Mackerel のウェブコンソールのスクリーンショット。クエリグラフの設定画面でグラフの凡例の設定画面が表示されている。値のセレクトボックスには state が表示されていて、state が選択されている

セレクトボックスで選択された値は「凡例」の「表示」に {{state}} と表示されます。これは凡例のテンプレート構文であり、{{}} で囲った価がラベルのキーとして存在する場合、その値に置き換えられます。今回は state がラベルのキーとして存在するため、{{state}}active といった値に置き換えられます。

Mackerel のウェブコンソールのスクリーンショット。クエリグラフの設定画面で nginx.connections_current が選択されていて、グラフのラインにマウスカーソルがホバーされている。マウスカーソルの上にはグラフの凡例が表示されている。active - 13.00、waiting - 10.00、writing - 3.00、reading - 0.00

まとめ

今回は、Mackerel でラベル付きメトリックを収集する方法についてご紹介しました。ラベル付きメトリック機能は、業界標準の OpenTelemetry へ対応して、クラウド環境の進化に合わせて Mackerel の対応環境を増やすことを目的としています。

Mackerel ではラベル付きメトリック機能のベータ版テストの参加者を募集しています。ラベル付きメトリックでは従来の Mackerel の「手軽に監視を導入できる」という特徴を引き継げていないなど、まだまだ課題を抱えています。ベータ版テストの参加者の皆様のフォードバックにより、より良い製品にしていきたいと考えています。

ラベル付きメトリック機能のベータ版テストに参加してみたいという方は、ぜひ以下のフォームからご連絡ください!

forms.gle