1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

EOSのリソースをZabbixで監視

Last updated at Posted at 2020-03-23

はじめに

EOS(EOSIO)では確保しているメモリやネットワーク、CPUなどのリソースが枯渇するとコントラクトの実行ができなくなります。
そこで、これらのリソースをZabbixで監視するように設定しました。

EOSのバージョンは1.8.1です。

作業内容

アカウントのリソースを知るにはcleosのget accountで取得できます。
/usr/bin/cleos --url http://localhost:8011 get account アカウント名
(EOSを自サーバの8011ポートで起動している場合)

$ /usr/bin/cleos --url http://localhost:8011 get account アカウント名
created: YYYY-MM-DDTHH:MM:SS.sss
permissions:
     owner     1:    1 アドレス
        active     1:    1 アドレス
memory:
     quota:     742.8 TiB    used:     94.42 KiB

net bandwidth:
     delegated:1000000000.0000 SYS           (total staked delegated to account from others)
     used:               588 KiB
     available:        47.07 TiB
     limit:            47.07 TiB

cpu bandwidth:
     delegated:100000000.0000 SYS           (total staked delegated to account from others)
     used:             365.5 ms
     available:        228.6 hr
     limit:            228.6 hr


$

※Private環境なのでものすごく大量にリソースを割り振ってます

これをうまく取り出してZabbixに伝えれば監視ができそうです。
取り出すスクリプトを作成してzabbix_agentにたたかせることでzabbix_serverに表示させる。
という方針で作成していきます。

スクリプト

上記で得られた指定されたアカウントのリソースをテキストファイルに出力し、
そこから監視対象項目の値を取り出すという動きにしています。

いったんテキストファイルに出力するのは
監視対象項目ごとにcleosコマンドを発行することを抑えるためです。

zabbix-eos-stats.py

/usr/local/bin/に作成し、Ethereumユーザで実行できるようにしておきます。

cd /usr/local/bin/
vi zabbix-eos-stats.py
chmod 755 zabbix-eos-stats.py

中身は以下の通りです。

zabbix-eos-stats.pyの中身 折り畳み
/usr/local/bin/zabbix-eos-stats.py
# !/bin/python3.6
# -*- coding: utf-8 -*-
# 指定されたアカウントのステータスを表示
# param1 : アカウント名
# param2 : 表示対象のステータス名
#
# 表示対象のステータス名は以下の通り
#   mem_quota_gb        memoryのquota(GiB単位)
#   mem_used_gb         memoryのused(GiB単位)
#   mem_available_gb    mem_quota_gb - mem_used_gb(GiB単位)
#   net_delegated       net bandwithのdelegated
#   net_used_gb         net bandwithのused(GiB単位)
#   net_available_gb    net bandwithのavailable(GiB単位)
#   net_limit_gb        net bandwithのlimit(GiB単位)
#   cpu_delegated       cpu bandwithのdelegated
#   cpu_used_min        cpu bandwithのused(分単位)
#   cpu_available_min   cpu bandwithのavailable(分単位)
#   cpu_limit_min       cpu bandwithのlimit(分単位)

import datetime
import math
import os
import re
import subprocess
import sys
import syslog

# 設定
OUTPUT_DIR = '/tmp'
FILE_PREFIX = 'zabbix-eos-stats-'
CLEOS_CMD = '/usr/bin/cleos --url http://localhost:8011'

################################################################################
class AccountInfo:

    def __init__(self):
        self.name = ''
        self.mem_quota_gb = 0
        self.mem_used_gb = 0
        self.mem_available_gb = 0
        self.net_delegated = 0
        self.net_used_gb = 0
        self.net_available_gb = 0
        self.net_limit_gb = 0
        self.cpu_delegated = 0
        self.cpu_used_min = 0
        self.cpu_available_min = 0
        self.cpu_limit_min = 0

    def parse(self, str):
        lines = str.splitlines()
        line = lines[0];                                    # created: 2020-01-01T00:00:00.000
        line = lines[1];                                    # permissions:
        line = lines[2];                                    #      owner     1:    1 EOSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        line = lines[3];                                    #         active     1:    1 EOSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        line = lines[4];                                    # memory:
        line = lines[5];  self.__parse_mem(line)            #      quota:     742.8 TiB    used:     43.56 KiB
        line = lines[6];                                    #
        line = lines[7];                                    # net bandwidth:
        line = lines[8];  self.__parse_net_delegated(line)  #      delegated:1000000000.0000 SYS           (total staked delegated to account from others)
        line = lines[9];  self.__parse_net_used(line)       #      used:               105 bytes
        line = lines[10]; self.__parse_net_available(line)  #      available:        49.92 TiB
        line = lines[11]; self.__parse_net_limit(line)      #      limit:            49.92 TiB
        line = lines[12];                                   #
        line = lines[13];                                   # cpu bandwidth:
        line = lines[14]; self.__parse_cpu_delegated(line)  #      delegated:100000000.0000 SYS           (total staked delegated to account from others)
        line = lines[15]; self.__parse_cpu_used(line)       #      used:               218 us
        line = lines[16]; self.__parse_cpu_available(line)  #      available:        252.6 hr
        line = lines[17]; self.__parse_cpu_limit(line)      #      limit:            252.6 hr

    def __parse_mem(self, line):
        """ MEMの確保量、使用量、残量をパースして算出 """
        # "     quota:     742.8 TiB    used:     43.56 KiB"
        groups = re.search('^     quota: +(([1-9]\d*|0)(\.\d+))? ([a-zA-Z]+)\s+used: +(([1-9]\d*|0)(\.\d+))? ([a-zA-Z]+) *$', line)
        if groups:
            mem_quota_val  = groups.group(1)  # 742.8
            mem_quota_unit = groups.group(4)  # TiB
            mem_used_val   = groups.group(5)  # 43.56
            mem_used_unit  = groups.group(8)  # KiB
            self.mem_quota_gb = self.__unit_exchange_to_gb(mem_quota_val, mem_quota_unit)
            self.mem_used_gb  = self.__unit_exchange_to_gb(mem_used_val,  mem_used_unit)
            self.mem_available_gb = self.mem_quota_gb - self.mem_used_gb

    def __parse_net_delegated(self, line):
        """ NETの確保量をパースして算出 """
        # "     delegated:1000000000.0000 SYS           (total staked delegated to account from others)"
        groups = re.search('^     delegated: *(([1-9]\d*|0)(\.?\d+)).* *$', line)
        if groups:
            self.net_delegated = groups.group(1)

    def __parse_net_used(self, line):
        """ NETの使用量をパースして算出 """
        # "     used:               105 bytes"
        groups = re.search('^     used: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            net_used_val  = groups.group(1)  # 105
            net_used_unit = groups.group(4)  # bytes
            self.net_used_gb = self.__unit_exchange_to_gb(net_used_val, net_used_unit)

    def __parse_net_available(self, line):
        """ NETの残量をパースして算出 """
        # "     available:        49.92 TiB"
        groups = re.search('^     available: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            net_available_val  = groups.group(1)  # 49.92
            net_available_unit = groups.group(4)  # TiB
            self.net_available_gb = self.__unit_exchange_to_gb(net_available_val, net_available_unit)

    def __parse_net_limit(self, line):
        """ NETの上限をパースして算出 """
        # "     limit:            49.92 TiB"
        groups = re.search('^     limit: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            net_limit_val  = groups.group(1)  # 49.92
            net_limit_unit = groups.group(4)  # TiB
            self.net_limit_gb = self.__unit_exchange_to_gb(net_limit_val, net_limit_unit)

    def __parse_cpu_delegated(self, line):
        """ CPUの確保量をパースして算出 """
        # "     delegated:100000000.0000 SYS           (total staked delegated to account from others)"
        groups = re.search('^     delegated: *(([1-9]\d*|0)(\.?\d+)).* *$', line)
        if groups:
            self.cpu_delegated = groups.group(1) # 100000000.0000

    def __parse_cpu_used(self, line):
        """ CPUの使用量をパースして算出 """
        # "     used:               218 us"
        groups = re.search('^     used: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            cpu_used_val  = groups.group(1)  # 218
            cpu_used_unit = groups.group(4)  # us
            self.cpu_used_min = self.__unit_exchange_to_min(cpu_used_val, cpu_used_unit)

    def __parse_cpu_available(self, line):
        """ CPUの残量をパースして算出 """
        # "     available:        252.6 hr"
        groups = re.search('^     available: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            cpu_available_val  = groups.group(1)  # 252.6
            cpu_available_unit = groups.group(4)  # hr
            self.cpu_available_min = self.__unit_exchange_to_min(cpu_available_val, cpu_available_unit)

    def __parse_cpu_limit(self, line):
        """ CPUの上限をパースして算出 """
        # "     limit:            252.6 hr"
        groups = re.search('^     limit: *(([1-9]\d*|0)(\.?\d+)) ([a-zA-Z]+) *$', line)
        if groups:
            cpu_limit_val  = groups.group(1)  # 252.6
            cpu_limit_unit = groups.group(4)  # hr
            self.cpu_limit_min = self.__unit_exchange_to_min(cpu_limit_val, cpu_limit_unit)

    # 単位の変換
    def __unit_exchange_to_gb(self, val, unit):
        """ GiB単位に変換
        Args:
            val(float):変換対象の値
            unit(string):変換対象の単位(TiB, GiB, MiB, KiB, bytes)
        Returns:
            float: GiB単位に変換された値
        """
        if   unit == 'TiB':   rtn = float(val) * 1024
        elif unit == 'GiB':   rtn = float(val)
        elif unit == 'MiB':   rtn = float(val) / 1024
        elif unit == 'KiB':   rtn = float(val) / 1024 / 1024
        elif unit == 'bytes': rtn = float(val) / 1024 / 1024 / 1024
        else:                 rtn = -1
        return math.ceil(rtn)

    def __unit_exchange_to_min(self, val, unit):
        """ 分単位に変換
        Args:
            val(float):変換対象の値
            unit(string):変換対象の単位(hr, min, sec, ms, us)
        Returns:
            float: 分単位に変換された値
        """
        if   unit == 'hr':  rtn = float(val) * 60
        elif unit == 'min': rtn = float(val)
        elif unit == 'sec': rtn = float(val) / 60
        elif unit == 'ms':  rtn = float(val) / 60 / 1000
        elif unit == 'us':  rtn = float(val) / 60 / 1000 / 1000
        else:               rtn = -1
        return math.ceil(rtn)

    def to_list_string(self):
        lststr = []
        lststr.append("name "               + self.name                     + '\n')
        lststr.append("mem_quota_gb "       + str(self.mem_quota_gb)        + '\n')
        lststr.append("mem_used_gb "        + str(self.mem_used_gb)         + '\n')
        lststr.append("mem_available_gb "   + str(self.mem_available_gb)    + '\n')
        lststr.append("net_delegated "      + str(self.net_delegated)       + '\n')
        lststr.append("net_used_gb "        + str(self.net_used_gb)         + '\n')
        lststr.append("net_available_gb "   + str(self.net_available_gb)    + '\n')
        lststr.append("net_limit_gb "       + str(self.net_limit_gb)        + '\n')
        lststr.append("cpu_delegated "      + str(self.cpu_delegated)       + '\n')
        lststr.append("cpu_used_min "       + str(self.cpu_used_min)        + '\n')
        lststr.append("cpu_available_min "  + str(self.cpu_available_min)   + '\n')
        lststr.append("cpu_limit_min "      + str(self.cpu_limit_min)       + '\n')
        return lststr  # "[key] [value]"という文字列の配列


################################################################################
def get_accoount_info(account_name):
    """指定されたアカウントの情報を取得してファイルに出力"""
    output_file = f'{OUTPUT_DIR}/{FILE_PREFIX}{account_name}.out'
    # 最終実行から1分以内ならば終了
    if os.path.exists(output_file):
        if datetime.datetime.now() < datetime.datetime.fromtimestamp(os.stat(output_file).st_mtime) + datetime.timedelta(minutes=1):
            exit

    # cleos get account invoice
    cmd_result = subprocess.run([f'{CLEOS_CMD} get account {account_name}'], shell=True, encoding='utf-8', stdout=subprocess.PIPE)
    account_info_lines = cmd_result.stdout
    account_info = parse_account_info(account_info_lines)
    account_info.name = account_name
    # 結果をファイルに出力
    with open(output_file, 'wt') as f:
        f.writelines(account_info.to_list_string())

def parse_account_info(account_info_str):
    account_info = AccountInfo()
    account_info.parse(account_info_str)
    return account_info

def return_target_value(account_name, target_param):
    output_file = f'{OUTPUT_DIR}/{FILE_PREFIX}{account_name}.out'
    with open(output_file, 'r') as f:
        lines = f.readlines()
    for line in lines:
        if line.find(target_param) >= 0:
            # "[param] [value]"の形で記載されている
            target_value = line.split()[1]
            return target_value


################################################################################
account_name = sys.argv[1]
target_param = sys.argv[2]

# アカウント情報取得
get_accoount_info(account_name)
# 対象項目の値を出力
print(return_target_value(account_name, target_param))

get_accoount_info()を実行することで
リソース情報を記載した /tmp/zabbix-eos-stats-アカウント名.out が生成されます。

出力する項目の値はGiBもしくは分単位に統一しています。
プライベート環境で大量にリソースを割り振っているので
この単位にしていますが、お使いの環境に応じて適当に改変してください。

また、監視項目の数だけZabbixエージェントから実行されるため、
1分以内に再実行された際は、取得せずに既存のリソース情報を返すようにしています。

return_target_value()でそれをgrepして監視対象の項目を表示します。

テストを兼ねて一度実行してみましょう

./zabbix-eos-stats.py アカウント名 mem_quota_gb

正常に終了すると、以下のファイルが生成されます。

/tmp/zabbix-eos-stats-アカウント名.out
name アカウント名
mem_quota_gb 760628
mem_used_gb 1
mem_available_gb 760627
net_delegated 1000000000.0000
net_used_gb 1
net_available_gb 48200
net_limit_gb 48200
cpu_delegated 100000000.0000
cpu_used_min 1
cpu_available_min 13716
cpu_limit_min 13716

Zabbixエージェントの設定

ユーザパラメータの設定ファイルを作成します。

/etc/zabbix/zabbix_agentd.d/userparameter_eos.conf
# 指定したアカウントの情報を監視する
# $1 : 監視対象のアカウント名
# $2 : 監視対象の項目名
# 監視対象の項目名は以下の通り
#   name                アカウント名
#   mem_quota_gb        memoryのquota(GiB単位)
#   mem_used_gb         memoryのused(GiB単位)
#   mem_available_gb    mem_quota_gb - mem_used_gb(GiB単位)
#   net_delegated       net bandwithのdelegated
#   net_used_gb         net bandwithのused(GiB単位)
#   net_available_gb    net bandwithのavailable(GiB単位)
#   net_limit_gb        net bandwithのlimit(GiB単位)
#   cpu_delegated       cpu bandwithのdelegated
#   cpu_used_min        cpu bandwithのused(分単位)
#   cpu_available_min   cpu bandwithのavailable(分単位)
#   cpu_limit_min       cpu bandwithのlimit(分単位)
#
UserParameter=eos.getinfo[*],/usr/local/bin/zabbix-eos-stats.py $1 $2

作成したら読み込ませるためzabbix-agentを再起動しましょう。

Zabbixサーバの設定

アプリケーション

アプリケーションとしてEOSを作成します。

アイテム

アイテムを作成します。

項目 備考
名前 resource アカウント名 mem_quota_gb 監視対象項目に応じて適当に
タイプ Zabbixエージェント
キー eos.getinfo[アカウント名, mem_quota_gb] 監視対象項目に応じて適当に
ホストインターフェース 監視対象のもの
データ型 数値(浮動小数)
単位 GiB もしくは min
アプリケーション EOS

上記以外の項目はそれぞれの環境に合わせて適当に設定します。
値が取れれば成功です!

テンプレートとして作ったりトリガー設定などは適当に。。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?