New Relic Flex を用いた Linux プロセス/サービスの監視設定

Flex を利用した Linux プロセス数の監視設定方法をご紹介します。アラート用プラグインスクリプトを New Relic でも使いたい場合など、本記事をご参照ください。

## New Relic Flex によるカスタムモニタリング

New Relic インフラエージェントに付帯されている Flex インテグレーションを利用すると
任意のコマンドやスクリプトの結果を New Relic にモニタリングデータとし送信することができます。

本ポストでは Flex を用いて下記の情報を収集する設定例と

- Linux の ps コマンドを用いて特定のプロセス数を取得する
- systemctl コマンドを用いたサービス一覧とそのステータスを取得する

それぞれのデータを用いたアラートコンディションの設定例を紹介します。

## ProcessSample との対比

インフラエージェントでは enable_process_metrics オプションを true にすることで プロセス情報を収集することができます。
稼働しているプロセスの一覧や指定と合致したプロセス情報を収集することができます。

ProcessSample を利用して、プロセス数のアラート設定を行うことも可能ですが
プロセスが存在しないことを検出する場合、信号損失閾値や filter 関数など一工夫が必要になります。

この 一工夫 に関し、多くの質問をいただくことが多いため、よりシンプルにアラートを設定することができる Flex を用いる方法をご紹介します。

## 事前準備

- 対象 Linux ホストにインフラエージェントをインストールします
- - ガイドインストールが簡易的です (詳細は [ドキュメント](https://docs.newrelic.com/jp/docs/infrastructure/infrastructure-agent/new-relic-guided-install-overview/) をご参照ください)

## Flex の設定

インフラエージェントでのデータ収集が開始できたら Flex でのカスタムモニタリングの動きを確認しておきましょう。

Flex の設定サンプルが github にあります。
https://github.com/newrelic/nri-flex/tree/master/examples

linux > [linux-filesystem.yml](https://github.com/newrelic/nri-flex/blob/master/examples/linux/linux-filesystem.yml) では df コマンドの結果を収集するようなのでこちらを用いて、収集をしてみます。
(df コマンドでの -x オプション (exclude-type) の指定は不要なので外しておきます)

インフラエージェントのインテグレーション用ディレクトリ (デフォルト /etc/newrelic-infra/integrations.d/) 配下に linux-filesystem.yml の名前でファイルを作成、下記の内容を書き込みます。
保存するとインフラエージェントが yml ファイルの作成を検出し、コマンド実行およびデータ送信が実行されます。

```yaml
---
integrations:
  - name: nri-flex
    # interval: 30s
    config:
      name: linuxFilesystem
      apis:
        - name: linuxFilesystem
          commands:
            - run: df -PT -B1 | grep -v ^Filesystem
              split: horizontal
              set_header:
                [
                  fs,
                  fsType,
                  capacityBytes,
                  usedBytes,
                  availableBytes,
                  usedPerc,
                  mountedOn,
                ]
              regex_match: true
              split_by: (\S+.\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)
          perc_to_decimal: true
```
(yaml ファイルでは行頭のスペースの数が重要なので、スペースは削除されないように注意ください。# はコメントアウトの意味です)

New Relic 側では NRQL で送信されたデータを確認できます。
対象イベントは Flex 設定内の apis > name である linuxFilesystem に Sample をつけた linuxFilesystemSample となります。

```sql
SELECT fs, fsType, capacityBytes, usedBytes, availableBytes, usedPerc, mountedOn, hostname, * FROM linuxFilesystemSample
```
![nrql-linuxFilesystem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3663589/5877f6e0-8249-43a0-a6e3-5a03560cc9ac.png)

set_header で指定した項目がインフラエージェント用データに付随するホスト情報と共に収集されています。また収集間隔は 30秒のようです (interval 項目で調整が可能です)。

うまくいかない場合などは、Flex コマンドの手動などで設定項目や run コマンドを調整します。
ref. [New Relic Flex > トラブルシューティング](https://docs.newrelic.com/jp/docs/infrastructure/host-integrations/host-integrations-list/flex-integration-tool-build-your-own-integration/#troubleshooting)

## Flex でプロセス数を収集

では、次に Flex を利用したプロセス数の収集例です。

特定プロセスのプロセス数を取得する場合、下記のような ps コマンドを利用する場合が多いかと思います。

```
$ ps auxww | grep [h]ttpd | wc -l
```
Flex では そのまま使えます。(※ 一部うまくいかないため、少し修正します)

github の linux サンプルに [linux-processes-filtered.yml](https://github.com/newrelic/nri-flex/blob/master/examples/linux/linux-processes-filtered.yml) があるので、この内容に倣って作成してみます。

先と同様にインテグレーション用ディレクトリ (デフォルト /etc/newrelic-infra/integrations.d/) 配下に linux-process-count.yml の名前でファイルを作成、下記の内容を書き込みます。

```yaml
---
integrations:
  - name: nri-flex
    interval: 30s
    config:
      name: linuxProcessCount
      apis:
        - name: linuxProcessCount
          commands:
            - run: ps auxww | grep "[h]ttpd" | wc
              split: horizontal
              set_header: [number_of_process]
              regex_match: true
              split_by: \s+(\d+)\s+\d+\s+\d+
              custom_attributes:
                processFilter: httpd
        - name: linuxProcessCount
          commands:
            - run: ps auxww | grep "[/]usr/sbin/sshd" | wc
              split: horizontal
              set_header: [number_of_process]
              regex_match: true
              split_by: \s+(\d+)\s+\d+\s+\d+
              custom_attributes:
                processFilter: sshd
        - name: linuxProcessCount
          commands:
            - run: ps auxww | grep "[h]ogehoge" | wc
              split: horizontal
              set_header: [number_of_process]
              regex_match: true
              split_by: \s+(\d+)\s+\d+\s+\d+
              custom_attributes:
                processFilter: hogehoge
```
(※ コマンド出力が数字のみの場合、うまく拾ってくれないので wc -l から wc に変更しました。合わせて split_by も調整しました。)
ここでは収集例として、複数プロセスが存在している httpd プロセス, 1つだけ存在する sshd (/usr/sbin/sshd を含む) および存在しないプロセス hogehoge の 3プロセスの数を取集するようにしました。各環境に合わせて調整ください。


NRQL でデータを確認してみます。対象イベントは linuxProcessCountSample となります。
set_header で指定した number_of_process が ps コマンドで取得したプロセス数、processFilter にプロセス名が記録されていることを確認します。

```sql
SELECT processFilter, number_of_process, hostname, * FROM linuxProcessCountSample
```
![nrql-linuxProcessCount.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3663589/81d3ba74-ab07-4275-8f39-b04c33cd929b.png)

コマンドラインでの結果とも一致して問題ありません。hogehoge も (数値) 0 を収集できています。
ProcessSample と異なり、対象プロセスがない場合にも (数値) 0 が報告されています。

### スクリプトの利用例

先のような yaml 設定の場合、ps コマンドを 3回実行しています。
大した負荷ではありませんが、個人的には無駄なことをしているように思ってしまったので簡単な perl スクリプトを作ってみました (python など馴染みのある言語で作成ください)。

```perl
#!/bin/perl

use strict;
use warnings;

my $input = [
    {
        'name' => 'httpd',
        'regex' => 'httpd',
    },
    {
        'name' => 'sshd',
        'regex' => '/usr/sbin/sshd',
    },
    {
        'name' => 'hogehoge',
        'regex' => 'hogehoge',
    },
    {
        'name' => 'bash (exact match)',
        'regex' => '^-bash$',
    },
];

$_->{'cnt'} = 0 foreach (@{$input});
foreach my $c (`ps -e -o command`) {
    foreach my $h (@{$input}) {
        $h->{'cnt'}++ if ($c =~ /$h->{'regex'}/);
    }
}
print "$_->{'name'}\t$_->{'regex'}\t$_->{'cnt'}\n" foreach (@{$input});
```
上記ではスクリプト内に対象プロセスをハードコーディングしています。監視実装される場合は、コードとインプット用ファイルが別になっている方がよいでしょう。

上記スクリプトをインテグレーション用ディレクトリ (デフォルト /etc/newrelic-infra/integrations.d/) 配下に linux-process-count2.pl などで作成 (実行権限もつけておきましょう)。
Flex 用の yaml (linux-process-count2.yml) も下記のように作成します。
commands > run に作成したスクリプトのフルパスを記載すれば、実行してくれるはずです。

```yaml
---
integrations:
  - name: nri-flex
    interval: 30s
    config:
      name: linuxProcessCount2
      apis:
        - name: linuxProcessCount2
          commands:
            - run: /etc/newrelic-infra/integrations.d/linux-process-count2.pl
              split: horizontal
              set_header: [name, regex , count]
              regex_match: true
              split_by: (.+)\t(.+)\t(\d+)
```

NRQL で linuxProcessCount2Sample イベントの取得状況を確認。
```sql
SELECT name, regex, `count`, error, error_msg, hostname, * FROM linuxProcessCount2Sample
```
![nrql-linuxProcessCount2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3663589/e03d8c31-04f9-4dfa-aac5-d9e2f9f65ffa.png)

確認をしたら、error_msg に スクリプトの実行が Permission denied で失敗していることに気がつきました。実行権限をつけ忘れていることに気がつき、+x し、以降は収集されるようになりました。

Flex では エラーがある場合は error, error_msg, error_exec などに Flex の仕様でエラー出力を記載してくれます。

## Flex で (systemctl) サービスのステータスを収集

github の linux サンプルに [linux-systemctl-cmd-example.yml](https://github.com/newrelic/nri-flex/blob/master/examples/linux/linux-systemctl-cmd-example.yml) があるので、こちらを利用してみます。

```yml
---
integrations:
  - name: nri-flex
    # interval: 30s
    config:
      name: systemctl
      apis:
        - name: systemctlServiceType
          commands:
            - run: systemctl --type=service --no-page --no-legend --all --plain
              split: horizontal
              set_header: [service, load, active, sub, description]
              regex_match: true
              split_by: (\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)
```

実行プロセスとは異なり、サービスは停止しても active -> inactive や failed などにステータスが変わるだけで、サービスの一覧を取得するコマンドで問題ありません。
github のサンプルコマンドのままだと先頭に dot 文字が入ってしまい上手く収集できていなかったので --plain オプションを追加しました (各環境で微調整ください)。
また、name も systemctlServiceType に変更しました。

enable かどうかという情報も併せて取得するとアラート設定時には、WHERE 句で指定できるのでよりよいかもしれません。

プロセス数の場合と同様 NRQL で想定通りのデータ収集ができていることを確認します。
```sql
SELECT service, load, active, sub, description, hostname, * FROM systemctlServiceTypeSample
```
![nrql-systemctlServiceType.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3663589/ef770c69-8b1a-47fe-9a9d-6223543a1326.png)

## アラートコンディションの作成

データの収集ができたので、アラートコンディションの作成を行います。

### プロセス数監視

linuxProcessCountSample を利用して設定してみます。
Flex でのコマンド実行は 30秒ごとに実行されるので、2回連続 0 だったら閾値違反とする、という要件でアラートコンディションを作成します。
また、NRQL 型アラートコンディションは自由度が高いため、下記以外でも類似の設定は可能です。

- アラートクエリ:
```sql
SELECT max(number_of_process) FROM linuxProcessCountSample FACET processFilter, hostname
```
- Window Duration: 1分 (または 30秒)
- Event flow (遅延 5秒)
- 静的閾値
  - critical: result = 0 for at least 1分  (下限値に対する閾値)
  - warning : result > 100 for at least 1分 (上限値に対する閾値 (任意))
- Gap filling: Last known value (任意)
- Title template: (任意)
```
{{tags.processFilter}} process {{operator}} {{threshold}} on {{tags.hostname}} ({{conditionName}})
```
- Description template: (任意)
```
Process: {{tags.processFilter}}
```
Description template や Title template は好みに合わせて調整ください。

FACET processFilter, hostname が指定されていれば、各ホストの各プロセスごとにシグナルが作成されるので、このアラートコンディション 1つで 5000プロセスまでモニターできます。
また、信号損失閾値を設定してしまうと、ホストやエージェントの停止時に対象プロセスの数だけアラートが発生してしまうので、別途 Host Not Reporting のアラートコンディションを作成しておくのがよいと思います。

同様にアラート通知を削減するという観点では
プロセスの場合、依存関係のあるプロセスが一緒に停止するということもあるので
アラートポリシーの Issue creation preference 設定では One issue per condition and signal (シグナル単位で作成) で個別に Issue を作成するよりも One issue per condition (コンディション単位で作成) または One issue per policy (ポリシー単位で作成) の方がよいかもしれません。


### Linux サービス監視

systemctl コマンドで取得している systemctlServiceTypeSample イベントを利用します。
WHERE 条件にサービスを指定し (ここでは httpd, php-fpm, postgresql)、1回でも 非 active ステータスだった場合に閾値違反とする要件で作成してみます。
(データ収集間隔は 30秒)

- アラートクエリ (非 active なサービス):
```sql
SELECT filter(count(*), where active != 'active') FROM systemctlServiceTypeSample FACET service, hostname
WHERE load = 'loaded' AND service IN ('httpd.service', 'php-fpm.service', 'postgresql.service')
```
- Window Duration: 30秒 (または 1分)
- Event flow (遅延 5秒)
- 静的閾値
  - critical: result > 0 at least once in 1分
- Title template: (任意)
```
{{tags.service}} != active on {{tags.hostname}} ({{conditionName}})
```
- Description template: (任意)
```
Service: {{tags.service}}
```
Description template や Title template は好みに合わせて調整ください。

バックアップやログローテーションなどの処理で、意識を意識をしていない再起動 (短時間ステータスが変わるなど) が行われている場合もあります。
情報収集開始後、数日または 1週間程度あとに対象サービスで認識と異なるステータスの変化が発生していないかを確認されたあとで、監視設定を行う方がよいかもしれません。

可能であれば、サービスの通常停止、kill によるプロセス停止などを行い、active や sub がどのように変わるかも確認しておきましょう。

上記の結果に応じて filter 関数内の抽出条件 (where active != 'active') を
(where sub != 'running') または (where active != 'active' or sub != 'running') などに変更調整ください。

また、併せて workflow の Enrichment などで対象ホストのサービス状態を取得するクエリを登録しておくと通知がわかりやすくなるかもしれません。
```sql
SELECT service, load, active, sub, description, hostname FROM systemctlServiceTypeSample WHERE active != 'active' AND service IN ('httpd.service', 'php-fpm.service', 'postgresql.service') AND hostname in {{accumulations.tag.hostname}} SINCE 30 minutes ago
```

### アラートの発報確認

テストのために httpd.service を停止し、発報確認を実施。

アラートポリシーでは、One issue per policy としたため、httpd サービス停止インシデントと httpd プロセス数 (= 0) インシデントが同一 Issue にまとまって報告されました。

![Issue-httpd-service_process.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3663589/16a19f38-7c90-41b8-83d9-ed54d2475cce.png)

# その他
New Relicでは、**新しい機能やその活用方法**について、QiitaやXで発信しています！
**無料でアカウント作成**も可能なのでぜひお試しください！

:::note info
New Relic株式会社の[X(旧Twitter)](https://x.com/NewRelicJapan) や [Qiita OrganizationOrganization](https://qiita.com/organizations/newrelic)では、
新機能を含む活用方法を公開していますので、ぜひフォローをお願いします。
:::

:::note info
無料のアカウントで試してみよう！
[New Relic フリープランで始めるオブザーバビリティ！](https://qiita.com/ktst79/items/5ee74b34e6adb8a7a6fa)