はじめに
前回、Node.js(JavaScript)でOpenTelemetryによるトレースデータ収集を行なったので、今度はカウンタ値のようなメトリックデータ収集についてまとめてみました。
OpenTelemetryの概要やトレースデータ収集の記事はこちらを参照。
やってみること
Node.jsアプリケーション上で収集したメトリックデータ(今回は3種類のカウンタの数値)を、Prometheusでグラフ化してみます。
実行環境
- MacOS X
- node: v13.8.0
- npm: v7.5.3
- express: v4.17.1
- OpenTelemetry: v0.18
1. サンプル構成の作成
まずはベースとなるサンプルアプリケーションを作成します。
サンプルの内容は、URLにアクセスすると3種類の色(red, blue, yellow)のいずれかを返す単純なロジックとなります。
1.1 サンプル作成
プロジェクトにExpressをインストールします。
$ npm install express
アプリケーション(app.js)を作成します。
'use strict';
const express = require('express');
const app = express();
const PORT = process.env.PORT || '8080';
app.use(function(req, res, next) {
// ランダムで色を選ぶ
const color = ['red', 'blue', 'blue', 'yellow', 'yellow', 'yellow'];
res.locals.color = color[Math.floor(Math.random() * color.length)];
next();
});
app.get('/', (req, res, next) => {
res.send(res.locals.color);
next();
});
app.listen(parseInt(PORT, 10), () => {
console.log(`Listening for requests on http://localhost:${PORT}`);
});
1.2 サンプルの動作確認
サンプルを実行すると、localhost:8080でクライアントからのリクエストを待ち受けるようになります。
$ node app.js
Listening for requests on http://localhost:8080
別のターミナルからサンプルのURLにアクセスします。
$ curl localhost:8080
red
上記のようにred, blue, yellowのいずれかが返却されます。
2. Prometheusの起動
メトリック収集の準備として、メトリックデータのグラフ化を行うPrometheusを構築します。
今回はDockerコンテナ版を使用します。
コンテナ起動前に、まずprometheus.ymlを作成します。
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'opentelemetry'
static_configs:
- targets:
- 'host.docker.internal:9464'
targetsはPrometheusにおけるメトリックデータの収集先となります。
今回はホストとなりますが、コンテナから見たホストはlocalhostではなくhost.docker.internalとなります(localhostはコンテナを指す)。
なおhost.docker.internalはLinuxでは使えないため(MacやWindowsなら可)、Linux環境の場合はホストのIPアドレスに置き換えてください。
docker run \
-d \
-p 9090:9090 \
-v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
localhost:9090にアクセスし、PrometheusのGUI画面が表示されたら起動OKです。
3. OpenTelemetryの組み込み
それでは先ほど作成したサンプルに、メトリックデータ収集を行うOpenTelemetryを組み込んでいきます。
3.1 パッケージのインストール
$ npm install @opentelemetry/metrics @opentelemetry/exporter-prometheus
3.2 monitoring.jsの作成
サンプルに組み込むカウンタのロジックを作成します。
'use strict';
const { MeterProvider } = require('@opentelemetry/metrics');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const prometheusPort = PrometheusExporter.DEFAULT_OPTIONS.port
const prometheusEndpoint = PrometheusExporter.DEFAULT_OPTIONS.endpoint
const exporter = new PrometheusExporter(
{
startServer: true,
},
() => {
console.log(
`prometheus scrape endpoint: http://localhost:${prometheusPort}${prometheusEndpoint}`,
);
},
);
const meter = new MeterProvider({
exporter,
interval: 1000,
}).getMeter('color-meter');
const colorCount = meter.createCounter('colors', {
description: 'Count each color'
});
// 色ごとにCounterを生成する
const redCounter = colorCount.bind({ color: 'red'});
const blueCounter = colorCount.bind({ color: 'blue'});
const yellowCounter = colorCount.bind({ color: 'yellow'});
module.exports.countColorRequests = () => {
return (req, res, next) => {
switch (res.locals.color) {
case 'red':
redCounter.add(1);
break;
case 'blue':
blueCounter.add(1);
break;
case 'yellow':
yellowCounter.add(1);
break;
}
next();
};
};
カウンタはmeter.createCounter()で作成します。
引数で指定した文字列が、メトリックデータのテーブル名に相当します。
また今回は返却した色ごとのカウンタ値をグラフ化したいので、以下のようにmeter.createCounter()で作成したカウンタにラベルをbindし、3色分のカウンタを作成します。
ここで指定する color: 'red'はグラフの系列名に相当します。
const redCounter = colorCount.bind({ color: 'red'});
3.3 サンプルへの組み込み
先ほど作成したmonitoring.jsをサンプルに組み込みます。
'use strict';
const express = require('express');
const app = express();
const PORT = process.env.PORT || '8080';
+ const { countColorRequests } = require("./monitoring");
app.use(function(req, res, next) {
// ランダムで色を選ぶ
const color = ['red', 'blue', 'blue', 'yellow', 'yellow', 'yellow'];
res.locals.color = color[Math.floor(Math.random() * color.length)];
next();
});
+ app.use(countColorRequests());
app.get('/', (req, res, next) => {
res.send(res.locals.color);
next();
});
app.listen(parseInt(PORT, 10), () => {
console.log(`Listening for requests on http://localhost:${PORT}`);
});
Math.random()で色を選択した後、countColorRequests()を呼び出し、選択した色のカウンタをインクリメントしています。
3.4 動作確認
1章と同様に、サンプルを起動します。
起動すると、localhost:8080のリクエスト待ち受けに加え、localhost:9464/metricsでもPrometheusからのメトリックデータ収集(Pull)を待ち受けるようになります。
$ node app.js
Listening for requests on http://localhost:8080
prometheus scrape endpoint: http://localhost:9464/metrics
サンプルへのリクエスト送信を100回連続で行い、カウンタ値を増やしてみます。
$ for var in `seq 100`; do curl localhost:8080; done
カウンタ値は、localhost:9464/metricsにアクセスすると参照できます。
(Prometheusはこの内容を定期的に収集します)
$ curl http://localhost:9464/metrics
# HELP colors Count each color
# TYPE colors counter
colors{color="red"} 14 1615214799584
colors{color="blue"} 32 1615214799558
colors{color="yellow"} 54 1615214799597
それではPrometheusでグラフ表示してみます。
Graphタブを選択し、テキストボックスにcolorsと入力、Executeボタンを実行します。
すると、下図のようなグラフが出力され、カウンタ値のインクリメントの推移を確認することができます。
おわりに
メトリック収集はOpenTelemetryを使わなくてもさまざまな方法で実現できますが、ユーザアプリケーション独自のメトリックを収集する場合にOpenTelemetryは強みを発揮しそうです。
OpenTelemetryについて、他にも面白そうな機能があれば試していきたいと思います。