はじめに
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の中身 折り畳み
# !/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
正常に終了すると、以下のファイルが生成されます。
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エージェントの設定
ユーザパラメータの設定ファイルを作成します。
# 指定したアカウントの情報を監視する
# $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 |
上記以外の項目はそれぞれの環境に合わせて適当に設定します。
値が取れれば成功です!
テンプレートとして作ったりトリガー設定などは適当に。。。