AzureのLinux仮想マシンでLoad averageを監視する方法の検討と実装

船井総研デジタルのよもぎたです。

Linux仮想マシンのLoad averageを監視してしきい値を超えたらアラートを発報したいと考えて仕組みづくりをしたので共有します。環境はAzureの仮想マシンでディストリビューションはRocky Linuxの8系を使いました。

# 大まかな仕組み
``uptime``コマンドを一定間隔で実行してそこの出力からLoad averageを取得し、しきい値を越えたらメッセージを出力するスクリプトを作成します。そのスクリプトをSystemdのサービスにして、出力がSyslogに出力されるようにするとともに、再起動時の自動実行とサービス終了時のメッセージ出力も実現します。

この仕組みとともにSyslogを監視することで、Load averageを監視しようという目論見です。

# Load averageの監視スクリプト

下記の通りです。

```bash
#!/bin/bash

while /bin/true
do
  /bin/uptime | /bin/grep -o 'load average:.*' | /usr/bin/tr -d ',' |\
    /bin/awk -v th=${LOAD_AVERAGE_THRESHOLD} '{if($NF>th || $(NF-1)>th || $(NF-2)>th){print $0}}'
  /bin/sleep ${UPTIME_INTERVAL}
done
```

``${LOAD_AVERAGE_THRESHOLD}``と``${UPTIME_INTERVAL}``は後述の別ファイルで定義します。
上記の内容だと``uptime``コマンドで取得できる3つのLoad averageの値のうちいずれかがしきい値を超えていたらログを出力するようになっています。特定の値だけ監視したい場合、値ごとにしきい値を変えたい場合などは適宜スクリプトを調整してください。

私はスクリプトは下記のように保存しました。

```
$ ls -l /usr/local/bin/loadaveragemonitor.sh
-rwxr-xr-x. 1 root root 218 Sep 15 00:37 /usr/local/bin/loadaveragemonitor.sh
```

# スクリプトをSystemdのサービスにする

## 変数の定義ファイルの作成

上記のスクリプトで使用する変数を定義するファイルを作成します。

```bash
UPTIME_INTERVAL=300
LOAD_AVERAGE_THRESHOLD=5
```

右辺の値は適宜設定してください。
ファイルは``/etc/system/default/``ディレクトリに保存します。

```
$ ls -l /etc/default/loadaveragemonitor
-rw-r--r--. 1 root root 45 Sep 15 11:59 /etc/default/loadaveragemonitor
```

## サービスの定義ファイルの作成

下記のとおりです。

```
$ ls -l /etc/systemd/system/loadaveragemonitor.service
-rw-r--r--. 1 root root 470 Sep 22 13:26 /etc/systemd/system/loadaveragemonitor.service
```

```bash
[Unit]
Description=Run the /bin/uptime command in an infinite loop to monitor loadaverage.
After=local-fs.target

[Service]
EnvironmentFile=/etc/default/loadaveragemonitor
User=nobody
Group=nobody
ExecStart=/usr/local/bin/loadaveragemonitor.sh
StandardOutput=journal
SyslogIdentifier=LoadAverageMonitor
SyslogFacility=local0
SyslogLevel=warning
ExecStop=/usr/bin/logger -p local0.warning 'My load average monitor service stopped.'

[Install]
WantedBy=multi-user.target
```

簡単ですがポイントを説明します。
* スクリプトは``nobody``権限で動作します。
* SyslogにはFacility local0、ログレベル warning でログが出力されます。
* スクリプトが停止したときは``My load average monitor service stopped.``とログが出力されます。

## サービスを起動して動作確認する

``systemctl``コマンドでサービスを起動します。

```bash
$ sudo systemctl start loadaveragemonitor
$ systemctl status loadaveragemonitor
● loadaveragemonitor.service - Run the /bin/uptime command in an infinite loop to monitor loadaverage.
   Loaded: loaded (/etc/systemd/system/loadaveragemonitor.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2023-09-22 14:09:45 UTC; 21s ago
  Process: 8985 ExecStop=/usr/bin/logger -p local0.warning My load average monitor service stopped. (code=exited, statu>
 Main PID: 8992 (loadaveragemoni)
    Tasks: 2 (limit: 4262)
   Memory: 564.0K
   CGroup: /system.slice/loadaveragemonitor.service
           ├─8992 /bin/bash /usr/local/bin/loadaveragemonitor.sh
           └─8997 /bin/sleep 300

Sep 22 14:09:45 rocky1 systemd[1]: Started Run the /bin/uptime command in an infinite loop to monitor loadaverage..
```

### ログ出力を確認する

適当に負荷をかけてLoad averageを上げます。私は下記の``dd``コマンドを10回弱実行しました。

```bash
$ /usr/bin/dd if=/dev/random of=/dev/null bs=1024 count=9999999999 &
$ uptime
 14:15:26 up 41 min,  1 user,  load average: 4.87, 1.29, 0.48
```

ログ出力は下記のようになります。

```
$ sudo tail /var/log/messages | grep 'load average'
Sep 22 14:19:45 rocky1 LoadAverageMonitor[10735]: load average: 12.84, 7.95, 3.48
```

### サービス停止時のログ出力を確認する

サービスを止めます。

```
$ sudo systemctl stop loadaveragemonitor
$ systemctl status loadaveragemonitor
● loadaveragemonitor.service - Run the /bin/uptime command in an infinite loop to monitor loadaverage.
   Loaded: loaded (/etc/systemd/system/loadaveragemonitor.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Fri 2023-09-22 14:31:23 UTC; 10s ago
  Process: 12727 ExecStop=/usr/bin/logger -p local0.warning My load average monitor service stopped. (code=exited, stat>
  Process: 8992 ExecStart=/usr/local/bin/loadaveragemonitor.sh (code=killed, signal=TERM)
 Main PID: 8992 (code=killed, signal=TERM)

Sep 22 14:09:45 rocky1 systemd[1]: Started Run the /bin/uptime command in an infinite loop to monitor loadaverage..
Sep 22 14:31:23 rocky1 systemd[1]: Stopping Run the /bin/uptime command in an infinite loop to monitor loadaverage....
Sep 22 14:31:23 rocky1 systemd[1]: loadaveragemonitor.service: Succeeded.
Sep 22 14:31:23 rocky1 systemd[1]: Stopped Run the /bin/uptime command in an infinite loop to monitor loadaverage..
```

ログ出力を確認します。

```
$ sudo tail /var/log/messages | grep 'My load average monitor service stopped.'
Sep 22 14:31:23 rocky1 nobody[12727]: My load average monitor service stopped.
```

``systemctl stop``で終了させた場合はもちろん、スクリプトを``kill <PID>``で終了された場合もログが出力されます。

## サービスが自動起動するように設定する

ひととおり動作確認できたので、起動時にサービスが自動実行されるようにします。

```bash
$ sudo systemctl enable loadaveragemonitor
```

再起動後、サービスが自動起動することを確認します。

```bash
$ sudo shutdown -r now
---snip---
$ systemctl status loadaveragemonitor
● loadaveragemonitor.service - Run the /bin/uptime command in an infinite loop to monitor loadaverage.
   Loaded: loaded (/etc/systemd/system/loadaveragemonitor.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2023-09-23 06:06:43 UTC; 4min 6s ago
 Main PID: 793 (loadaveragemoni)
    Tasks: 2 (limit: 4262)
   Memory: 704.0K
   CGroup: /system.slice/loadaveragemonitor.service
           ├─793 /bin/bash /usr/local/bin/loadaveragemonitor.sh
           └─816 /bin/sleep 300

Sep 23 06:06:43 rocky1 systemd[1]: Started Run the /bin/uptime command in an infinite loop to monitor loadaverage..
```

以上です、最後までお読みいただきありがとうございました。