amegonの雑なブログ

日常的なものから技術的なものまでメモの雑記

温湿度情報をZabbixで監視

目的

Raspberry Pi に装着した温湿度センサーで情報を取得できるようになったので Zabbix で監視してみる。

方法

過去の記事で書いたが、Raspberry Pi では現在 Zabbix Proxy と Zabbix Agent が動作している。
想定としては以下の流れで温湿度データを監視するようにしたい。

  • Raspberry Pi で温湿度情報を取得
  • 取得した情報を Zabbix Sender で Raspberry Pi 自身の Zabbix Proxy に送信
  • 温湿度情報が Zabbix サーバーに送信され監視される

温湿度情報を監視するために Zabbix にそれぞれの監視アイテムを作成する必要がある。
監視アイテムはホストに作成する必要があるが、今回は Raspberry Pi のエージェント監視を行っているホストに温湿度用の監視アイテムを作成してみる。

設定

Raspberry Pi

ZabbixSender 0.2.7 のインストール

温度センサーの情報は Pythonスクリプトで取得していることから、Zabbix にデータを送信する部分も Python で実施したい。
そこでぐぐって見つけたのが以下の ZabbixSender 0.2.7 である。

https://pypi.org/project/ZabbixSender/

pip を使ってインストールして利用できる状態にしてみる。

コマンド

pip install ZabbixSender

ログ

amegon@raspberrypi:~ $ pip install ZabbixSender
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting ZabbixSender
  Downloading https://www.piwheels.org/simple/zabbixsender/ZabbixSender-0.2.7-py3-none-any.whl (5.2 kB)
Installing collected packages: ZabbixSender
Successfully installed ZabbixSender-0.2.7
amegon@raspberrypi:~ $

インストールの確認

amegon@raspberrypi:~ $ pip list |grep ZabbixSender
ZabbixSender      0.2.7
amegon@raspberrypi:~ $

無事にインストールできた。

Python スクリプトの修正

次に、監視データを Zabbix Sender で送信する部分をスクリプトに実装する。
前回作成したコードに追加する。以下のような感じ。

コード

import Adafruit_DHT as DHT
import time
from ZabbixSender import ZabbixSender, ZabbixPacket

# センサーの定義
SENSOR_TYPE = DHT.DHT22
# GPIO 番号の定義
DHT_GPIO = 4

# 温湿度の取得
exec_time = int(time.time())
hum,temp = DHT.read_retry(SENSOR_TYPE, DHT_GPIO)

# データは小数点第2位を四捨五入して第1位まで表示。
print('Temp={0:0.1f}*C  Humidity={1:0.1f}%'.format(temp, hum))
temp = '{0:0.1f}'.format(temp)
hum = '{0:0.1f}'.format(hum)
print(temp)
print(hum)

# Zabbix Sender で送信
# 送信先 Zabbix Server or Zabix Proxy の IP アドレス
ZBX_SVR = '127.0.0.1'
# 送信先 Zabbix Server or Zabix Proxy の ポート番号(デフォルト 10051)
ZBX_SVR_PORT = 10051
# 監視データのアイテムを持つ Zabbix 上のホスト名
ZBX_HOST_NAME = 'RasPi_01'
# 監視データ用アイテムのキー文字列:温度
ZBX_ITEM_KEY_TEMP = 'raspberrypi.temperature'
# 監視データ用アイテムのキー文字列:湿度
ZBX_ITEM_KEY_HUM = 'raspberrypi.hummidity'

# ZabbixSender オブジェクトと packet オブジェクトを作成
server = ZabbixSender(ZBX_SVR, ZBX_SVR_PORT)
packet = ZabbixPacket()

# packet オブジェクトに送信するデータの組みあわせをセット
packet.add(ZBX_HOST_NAME, ZBX_ITEM_KEY_TEMP, temp, exec_time)
packet.add(ZBX_HOST_NAME, ZBX_ITEM_KEY_HUM, hum, exec_time)

# データを送信
server.send(packet)

# 送信したデータを表示
print(server.status)

実行したらエラー。

amegon@raspberrypi:~/work/scripts/sensor/samples $ python3 test5.py
Temp=28.8*C  Humidity=60.9%
28.8
60.9
Traceback (most recent call last):
  File "/home/amegon/work/scripts/sensor/samples/test5.py", line 42, in <module>
    server.send(packet)
  File "/home/amegon/.local/lib/python3.9/site-packages/ZabbixSender/ZabbixSender.py", line 36, in send
    status = re_status.search(status).groups()[0]
AttributeError: 'NoneType' object has no attribute 'groups'
amegon@raspberrypi:~/work/scripts/sensor/samples $

zabbix_proxy.log にも以下のログが書かれていることを発見。

Message from 127.0.0.1 is missing header. Message ignored.

ググってみると Zabbix サーバーと Zabbix エージェントのバージョンが不一致の場合に missing header のログが記録されるらしい。
ということは python ライブラリの方で Header に設定情報が足りていないのかな?と思い申すコスググる

そうしましたら以下の記事を発見。

https://www.happylifecreators.com/blog/20220323/

す、すごい。。。
コード ZabbixSender.py の保存場所を find で探して以下のように修正。
自分の環境では上記URLの修正のままだと動作しなかったので、struct の import 部分と pack の仕様部部分を書き換えています。

import json
import re
import socket
import time
from struct import *

class ZabbixSender:
    def __init__(self, server='127.0.0.1', port='10051', config=None):
        if config is not None:
            conf_file = open(config, 'r')
            re_server = re.compile('\\nServer=(\S*)\\n\\n')
            temp_server = re_server.search(conf_file.read())
            conf_file.close()
            self.server = temp_server.groups()[0]
        else:
            self.server = server
        self.port = port
        self.status = ''

    def __str__(self):
        return json.dumps({'server': self.server,
                           'port': self.port},
                          indent=4)

    def send(self, packet):
        packet = str(packet).encode('utf-8')
        s = socket.socket()
        try:
            s.connect((self.server, int(self.port)))
        except Exception as e:  # TODO: Horrible! Rewrite immediately.
            print(e)

        # Add Header (2023-09-07)
        header = pack(b'<4sBQ', b'ZBXD', 1, len(packet))
        packet = header + packet

        s.send(packet)
        time.sleep(0.5)
        status = s.recv(1024).decode('utf-8')
        re_status = re.compile('(\{.*\})')
        status = re_status.search(status).groups()[0]
        self.status = json.loads(status)
        s.close()

ログ

amegon@raspberrypi:~/work/scripts/sensor/samples $ python3 test5.py
Temp=24.2*C  Humidity=58.1%
24.2
58.1
{'response': 'success', 'info': 'processed: 2; failed: 0; total: 2; seconds spent: 0.000316'}
amegon@raspberrypi:~/work/scripts/sensor/samples $

動きました。

Zabbix 側

以下のようなアイテムを作成。
キーは先述のスクリプトに設定した文字列をあわせる。

温度

湿度

動作確認

データの受信も確認できた。

運用設定

作成したスクリプトの名前を変更して、cron で定期実行するようにして Zabbix での監視を行う。

cron の設定内容

# every 1 minute
* * * * * python3 /home/amegon/work/scripts/sensor/sendSensorData.py

Zabbix でも1分ごとにデータが受信できていることが確認できた。