大規模な Node.js 本番環境では、複数のアプリケーションを監視することが困難な場合があります。Node.js 用の New Relic APM エージェントは、個々のアプリケーションからログ、トレース、詳細なパフォーマンスメトリクスを取得するのに役立ちますが、すべての Node.js プロセス自体の全般的な健全性やリソース消費量、および CPU やメモリの使用率などの重要なプロセスレベルのメトリクスについてはどうでしょうか?
PM2 は、Node.js アプリケーション用の一般的なプロセスマネージャーであり、デプロイメントを簡素化し、信頼性を確保するように設計されています。自動アプリケーション再起動、負荷分散、監視機能などの堅牢な機能を備えており、本番環境レベルのNode.js環境管理において不可欠なツールです。
PM2 の API 監視を使用すると、アクティブなプロセスやリソース消費量などの詳細なテレメトリーデータに簡単にアクセスできるほか、Git へのコミット、アクティブなブランチ、Node.js のバージョン、エントリスクリプトなどの有用な詳細情報も入手できます。こういったインサイトは、パフォーマンスに関するトラブルシューティングや根本原因の特定を行う際に非常に役立ちます。
PM2 で監視できる情報
PM2 の API を使用すると、豊富な情報を取得できます。理想的には、ホスト上の PM2 の監視 API を使用して視覚化できるすべてのメトリクスを取得し、New Relic にエクスポートすることができます。
このブログ記事では、PM2 の監視において特に重要な各種メトリクスを中心に取り扱います。
アプリケーションIDとホスト情報
PM2 の一部として現在実行されているアプリケーションのホスト ID といったアプリケーションの詳細情報を使用すると、各アプリケーションに関連付けられているインスタンスを簡単に特定できます。これによって特定のプロセスを識別しやすくなり、効果的に管理できるようになるため、複数のサービスが存在する環境で特に便利です。
メトリクス:appName
、hostId
リソースメトリクス
CPU やメモリの使用率といった各プロセスのメトリクスからは、アプリケーションのリソース消費とパフォーマンスに関する重要なインサイトが得られます。axm_monitor を使用すると、HTTP のレイテンシ、アクティブなリクエスト、イベントループのレイテンシを追跡して、応答性をリアルタイムでモニターすることもできます。
メトリクス:monit.cpu
、monit.memory
、axm_monitor
(例:HTTP のレイテンシ、アクティブなリクエスト、イベントループのレイテンシ)
プロセス情報
プロセス ID、ステータス、稼働時間などのプロセスレベルの詳細情報は、各アプリケーションの健全性やライフサイクルをモニターするのに便利です。プロセスが最後に更新または開始された日時を追跡すると、頻繁に再起動する、稼働時間が異常に長いといった問題を特定する際に役立ちます。
メトリクス:pid
、pm2_env.status
、pm2_env.pm_uptime
、pm2_env.created_at
、timestamp
、pm2_env.update_time
デバッグポインター:ログ、エラー、再起動、クラッシュ
ログのパス、エラーコード、再起動回数といったデバッグの詳細情報があると、トラブルシューティングを迅速に行うことができます。標準的な出力とエラーログの両方、および再起動に関する情報が取得できれば、安定性の問題やアプリケーションに潜んでいるバグを正確に特定する際に役立ちます。
メトリクス:pm2_env.error_file
、pm2_env.out_file
、pm2_env.exit_code
、pm2_env.unstable_restarts
、pm2_env.restart_time
、pm2_env.status
バージョン管理とデプロイメントに関するインサイト
Git にコミットした履歴や Node.js のバージョンなどのデプロイメントの詳細情報があると、デプロイされたバージョンを簡単に追跡できます。PM2 はブランチ、リビジョン、コミット情報を保持しています。これによって実行中のバージョンを明確に把握できるので、コード変更後の根本原因分析が容易になります。
メトリクス:pm2_env.node_version
、pm2_env.version
、pm2_env.versioning*
PM2 からインサイトを取得する方法
現在、New Relic エージェントと PM2 は直接統合されてはいませんが、Flexを使用して独自のインテグレーションを構築することができます。Flex は使いやすくてエージェントレスなツールで、データソースと New Relic の間のインテグレーションを構築する際に使用します。
設定
まず、pm2_monit.yml という名前の設定ファイルを新規作成します。次に、以下の設定を追加します。
この設定は PM2 jlist API を呼び出し、JQ を使用して JSON 出力データをサニタイズするという簡単なものです。JQ は、JSON データの処理、クエリ、変換に使用されるコマンドラインユーティリティです。Flex には JQ のサポートが組み込まれているため、データのサニタイズと変換が容易になります。
元の出力には不要な機密の詳細データが含まれている可能性がありますが、Flex の JQ および remove_keys 関数を使用すると簡単に削除いただけます。
基本設定
integrations:
- name: nri-flex
timeout: 60s
interval: 30s
config:
name: PM2status
apis:
- name: PM2Process
event_type: PM2Sample
commands:
# Linux specific command
- run: USER=ubuntu; su - $USER bash -c "pm2 jlist"
# command for windows/mac
# - run: npx pm2 jlist
JQ による JSON 変換
この段階では、PM2 jlist
の出力は JSON 形式の生データです。JQ を使用して JSON 出力のサニタイズと変換を実行し、必要なフィールドのみを保持するようにします。さらに remove_keys
関数とrename_keys
関数を使用してデータを整理します。
# Sanitize and transform the JSON output to required format
jq: >-
[] | {
pid,
name,
pm2_env: {
script: .pm2_env.script?,
out_file: .pm2_env.out_file?,
error_file: .pm2_env.error_file?,
watch: .pm2_env.watch?,
exit_code: .pm2_env.exit_code?,
node_version: .pm2_env.node_version?,
versioning: .pm2_env.versioning?,
version: .pm2_env.version?,
unstable_restarts: .pm2_env.unstable_restarts?,
restart_time: .pm2_env.restart_time?,
created_at: .pm2_env.created_at?,
pm_uptime: .pm2_env.pm_uptime?,
status: .pm2_env.status?,
unique_id: .pm2_env.unique_id?
},
pm_id,
monit
} | del(.pm2_env.versioning.remotes)
remove_keys:
- pm_id
rename_keys:
name: appName
custom_attributes:
hostId: localhost
ユーザーコンテキストの切り替え
デフォルトで Flex コマンドは root (sudo) ユーザーで実行されるため、Flex 設定の command
ブロックにて、PM2 プロセスを実行しているユーザーに切り替える必要があります。Windows または Mac のローカルで PM2 を実行している場合は、Linux 固有のコマンドではなく、npx PM2 jlist
コマンドを使用してください。
設定全体は次のようになります。
integrations:
- name: nri-flex
timeout: 60s
interval: 30s
config:
name: PM2status
apis:
- name: PM2Process
event_type: PM2Sample
commands:
- run: USER=ubuntu; su - $USER bash -c "pm2 jlist"
# - run: npx pm2 jlist
# Sanitize and transform the JSON output to required format
jq: >-
[] | {
pid,
name,
pm2_env: {
script: .pm2_env.script?,
out_file: .pm2_env.out_file?,
error_file: .pm2_env.error_file?,
watch: .pm2_env.watch?,
exit_code: .pm2_env.exit_code?,
node_version: .pm2_env.node_version?,
versioning: .pm2_env.versioning?,
version: .pm2_env.version?,
unstable_restarts: .pm2_env.unstable_restarts?,
restart_time: .pm2_env.restart_time?,
created_at: .pm2_env.created_at?,
pm_uptime: .pm2_env.pm_uptime?,
status: .pm2_env.status?,
unique_id: .pm2_env.unique_id?
},
pm_id,
monit
} | del(.pm2_env.versioning.remotes)
remove_keys:
- pm_id
rename_keys:
name: appName
custom_attributes:
hostId: localhost
検証
設定の確認は Flex のデバッグモードで検証ができます。Flex の独立したバイナリモードを使用している場合は、次のコマンドを使用して設定をテストします。
sudo ./nri-flex -config_file pm2_monit.yml --pretty --verbose
New Relic の infrastructure エージェントの Flex インテグレーションを使用して設定をテストする場合、次のコマンドを実行します。
sudo /var/db/newrelic-infra/newrelic-integrations/bin/nri-flex --verbose --pretty --config_file ./pm2_monit.yml
Flex のテストとデバッグの詳細については、こちらのドキュメントでご確認ください。
正常に実行されると、以下に示すような出力が表示されます (ここでは Flex のデバッグ出力は記載しておりません)。
{
"name": "com.newrelic.nri-flex",
"protocol_version": "3",
"integration_version": "1.15.2",
"data": [
{
"metrics": [
{
"appName": "expressApp-otel",
"event_type": "PM2Sample",
"hostId": "ec2-webserver",
"integration_name": "com.newrelic.nri-flex",
"integration_version": "1.15.2",
"monit.cpu": 0,
"monit.memory": 46641152,
"pid": 1304,
"pm2_env.created_at": 1728589283166,
"pm2_env.error_file": "logs/error.log",
"pm2_env.exit_code": 1,
"pm2_env.node_version": "16.17.0",
"pm2_env.out_file": "logs/app.log",
"pm2_env.pm_uptime": 1729058602005,
"pm2_env.restart_time": 0,
"pm2_env.script": "./server.js",
"pm2_env.status": "online",
"pm2_env.unique_id": "858b1616-bfd7-41c0-8861-de5babcfe106",
"pm2_env.unstable_restarts": 0,
"pm2_env.version": "1.0.0",
"pm2_env.versioning.ahead": "false",
"pm2_env.versioning.branch": "master",
"pm2_env.versioning.branch_exists_on_remote": "true",
"pm2_env.versioning.comment": "update: Added query param to fetch mapped category in response\nfix: fallback envvar value\n",
"pm2_env.versioning.next_rev": "<nil>",
"pm2_env.versioning.prev_rev": "f195db41aec0592142ef478c424ec1722c7318a4",
"pm2_env.versioning.remote": "origin",
"pm2_env.versioning.repo_path": "/home/ubuntu/workspace/node-express-app",
"pm2_env.versioning.revision": "8d31b501b38229171be428db1dc7e2b412694116",
"pm2_env.versioning.type": "git",
"pm2_env.versioning.unstaged": "true",
"pm2_env.versioning.update_time": "2024-10-16T06:03:22.325Z",
"pm2_env.versioning.url": "git@github.com:zmrfzn/node-express-app.git",
"pm2_env.watch": "false"
},
{
"event_type": "flexStatusSample",
"flex.Hostname": "ip-172-31-30-74",
"flex.IntegrationVersion": "1.15.2",
"flex.counter.ConfigsProcessed": 1,
"flex.counter.EventCount": 2,
"flex.counter.EventDropCount": 0,
"flex.counter.PM2Sample": 2,
"flex.time.elapsedMs": 263,
"flex.time.endMs": 1730119876587,
"flex.time.startMs": 1730119876324
}
],
"inventory": {},
"events": []
}
]
}
Flex で PM2 プロセスから取得し JQ で変換した後の単純化されたデータは以下のようになります。
{
"appName": "expressApp-otel",
"hostId": "ec2-webserver",
"integration_name": "com.newrelic.nri-flex",
"integration_version": "1.15.2",
"monit.cpu": 0,
"monit.memory": 80068608,
"pid": 1310,
"pm2_env.created_at": 1728589676754,
"pm2_env.error_file": "logs/error.log",
"pm2_env.exit_code": 1,
"pm2_env.node_version": "16.17.0",
"pm2_env.out_file": "logs/app.log",
"pm2_env.pm_uptime": 1729058602010,
"pm2_env.restart_time": 0,
"pm2_env.script": "./server.js",
"pm2_env.status": "online",
"pm2_env.unique_id": "83e7e3d5-17d7-41b0-87fe-5201148c826a",
"pm2_env.unstable_restarts": 0,
"pm2_env.version": "1.0.0",
"pm2_env.versioning.ahead": "false",
"pm2_env.versioning.branch": "otel",
"pm2_env.versioning.branch_exists_on_remote": "true",
"pm2_env.versioning.comment": "chore: update OTEL SDK packages to latest",
"pm2_env.versioning.next_rev": "<nil>",
"pm2_env.versioning.prev_rev": "f9fb795ec5ec07d24bd858b55287cbffda44b365",
"pm2_env.versioning.remote": "origin",
"pm2_env.versioning.repo_path": "/home/ubuntu/workspace/otel/node-express-app",
"pm2_env.versioning.revision": "aad10ae5445469718cd2da5041d140e39be8ef78",
"pm2_env.versioning.type": "git",
"pm2_env.versioning.unstaged": "true",
"pm2_env.versioning.update_time": "2024-10-16T06:03:22.319Z",
"pm2_env.versioning.url": "git@github.com:zmrfzn/node-express-app.git",
"pm2_env.watch": "false",
"timestamp": 1729513381684
}
検証
Flex は処理されたデータをすべて New Relic のイベントAPI 経由で送信します。これにより、さまざまな種類のイベントデータを効率的に処理できるようになります。この設定では、イベントに PM2Sample という名前を付けます。システム内の他のイベントと明確に識別できるため、混同する心配がありません。
このイベントに関連付けられたすべてのデータは、このテーブル自体で New Relic Query Language (NRQL) を使用して簡単にクエリできます。
FROM PM2Sample SELECT * SINCE 10 MINUTES AGO LIMIT 5
可視化
New Relic プラットフォームでデータにアクセスできると、ニーズに関連する特定のメトリクスを簡単にクエリすることが可能で、またカスタマイズした形で可視化できます。パフォーマンスの傾向分析やプロセスの健全性監視を実行し、現在 PM2 で実行されている個々のアプリケーションの詳細なバージョン情報を収集することもできます。
カスタマイズした形で可視化できるため、目的に最適な方法でデータを表示し、複雑な情報をより深く理解することができます。さらに、これらのメトリクスを使用して、パーソナライズされたアラートを設定することもできます。
PM2Sample
について詳しく見ていきましょう。これで、個々のアプリケーションの平均 CPU 使用率を簡単にクエリできるようになります。
FROM PM2Sample SELECT average(monit.cpu) as 'CPU Usage %' FACET appName
また、時系列データを取得すると、APM メトリクスには含まれない PM2 のすべてのアプリケーションのメモリ消費量と CPU 使用率を時系列的に比較することもできます。
FROM PM2Sample SELECT average(monit.memory)/1048576 as 'Avg Memory M/b', average(monit.cpu) as 'Avg CPU%' EXTRAPOLATE TIMESERIES
NRQL では、ダッシュボード機能を使用して、メトリクスに対するクエリやメトリクスの可視化を簡単に行うことができます。以下は、PM2 監視用に作成したカスタムダッシュボードの設定例です。アプリケーションごとのメモリ使用量、アクティブなアプリケーションの合計の CPU 使用率 vs メモリ使用量、PM2 利用のアクティブなプロセスの最新のアプリケーションリビジョンの詳細など、さまざまなメトリクスを取得します。
PM2_monit_dashboard にある JSON を使用してこのダッシュボードをインポートします。インポートする前に、すべてのプレースホルダーのアカウントID(1234567
)を ご自身の New RelicアカウントID に置き換えてください。
まとめ
PM2 のテレメトリーは CPU、メモリ、アプリケーションログなどの重要なメトリクスを取得するもので、プロセスレベルのインサイトを得ることができます。Flex はカスタマイズして New Relic と統合することができるため、これらのデータポイントを単一の統合ビューにまとめることができます。これらを組み合わせることで、アプリケーションパフォーマンスの可視性が向上するだけでなく、根本原因の分析やプロアクティブな監視が容易になります。
本ブログに掲載されている見解は著者に所属するものであり、必ずしも New Relic 株式会社の公式見解であるわけではありません。また、本ブログには、外部サイトにアクセスするリンクが含まれる場合があります。それらリンク先の内容について、New Relic がいかなる保証も提供することはありません。