MongoDBを監視する

MongoDBのReplica SetはPrimary/Secondary間でfailoverしてくれるため、信頼性を向上させることができる。とはいえ、failoverした事実はZabbix等の監視ソフトで検知できるようにしたい。

port監視

ZabbixでMongoDB関連の検知をしようとしたら、まず「シンプルチェック」でport監視を最低限入れるといい。net.tcp.service[tcp,,27017]がキーとなる。

rs.status()でhealth check

Replica Set用に個別に検知項目を増やすのであれば、rs.status()healthという項目があるので、これを監視すると良い。
MongoDBが動いているサーバに次のスクリプトを配置し、snmpdで値を取得できるように設定する。

$ cat check_mongo_replicaset_health.sh 
#!/bin/bash

USER="XXXXXXXX"
PASS="XXXXXXXX"
HOST="localhost"
PORT="27017"

# echo 1 if health check succeeds. otherwise echo 0.
res=$(
cat << JS | mongo mongodb://${HOST}:${PORT}/admin -u ${USER} -p ${PASS} --quiet 2>/dev/null
rs.status().
 members.
   filter(m => m.name == "`hostname`:$PORT").
   map(m => m.health)[0] || 0
JS
)
test $? -eq 0 && echo $res && exit 0
echo 0

Zabbix側はアイテムのタイプを「SNMPエージェント」にして監視する。
トリガーは値が1以外であれば検知というシンプルな条件でいいだろう。

{mongo-replicaset:mongo[health].last(0)}#1

rs.printReplicationInfo()でoplogの保有期間監視

Replica Setを追加する場合にレプリケーションが追いつくまでoplogが残っているかという観点や、mongodumpしたデータとoplogからmongorestoreする際に直前までPoint-in-time Recoveryできるかという観点などで、oplogの保有期間も監視する必要がある。

oplogの情報はrs.printReplicationInfo()で取得できる。

rs:PRIMARY> rs.printReplicationInfo()
configured oplog size:   51168.3046875MB
log length start to end: 305530secs (84.87hrs)
oplog first event time:  Tue Feb 20 2018 01:18:53 GMT+0900 (JST)
oplog last event time:   Fri Feb 23 2018 14:11:03 GMT+0900 (JST)
now:                     Fri Feb 23 2018 14:11:05 GMT+0900 (JST)

mongodumpをする頻度を考慮して、最低24時間分保持していなければいけないとする。
rs.printReplicationInfo()を監視するスクリプトをMongoDBサーバに配置し、先ほどと同じようにsnmpdで値を取得できるように設定する。

$ cat check_mongo_oplog_length.sh
#!/bin/bash

USER="XXXXXXXX"
PASS="XXXXXXXX"
HOST="localhost"
PORT="27017"

# echo 1 if `oplog length start to end` is longer than 24hours. otherwise echo 0.
res=$(
mongo mongodb://${HOST}:${PORT}/admin -u ${USER} -p ${PASS} --quiet --eval 'rs.printReplicationInfo()' 2>/dev/null |
 awk 'match($0, /([0-9]+)secs/, a){print (a[1] > 60 * 24)}'
)
test $? -eq 0 && echo $res && exit 0
echo 0

Zabbix側はアイテムのタイプを「SNMPエージェント」にして監視する。

{mongo-replicaset:mongo[oplog].last(0)}#1

rs.isMaster()でfailover検知

今回一番実施したかった監視項目。
Primaryであればrs.isMaster().ismasterがtrueを返し、Primaryでなければfalseを返す。failoverしたということは、この値がその時点でtrue -> falseに変化する。

まずはrs.isMaster().ismasterを監視するためのスクリプトをMongoDBサーバに配置し、先ほどと同じようにsnmpdで値を取得できるように設定する。

$ cat check_mongo_replicaset_failover.sh 
#!/bin/bash

USER="XXXXXXXX"
PASS="XXXXXXXX"
HOST="localhost"
PORT="27017"

# echo true if this server is PRIMARY. otherwise echo false.
# if output has changed, failover might have happened.
res=$(mongo mongodb://${HOST}:${PORT}/admin -u ${USER} -p ${PASS} --quiet --eval 'rs.isMaster().ismaster' 2>/dev/null)
test $? -eq 0 && echo $res && exit 0
echo false

Zabbix側はアイテムのタイプを「SNMPエージェント」にして監視する。
トリガーの書き方だけ工夫が必要になる。

単にlast(0)prev()を比べて異なる場合に検知するだけだと、アイテムの更新間隔が短い場合にすぐにZabbixが復旧と判断してしまう。元々trueだったものが、あるタイミングでfalseになって検知しても、次の更新時にまたfalseを取得するだろうから、"false"="false"となり復旧と判断してしまう。
Zabbixの監視間隔は短くしたいものの、一度アラートを検知した場合は一定時間以上検知し続けるための工夫が必要になる。

一定時間以上検知し続けるトリガーの書き方

ドキュメントに載っているcountを使って、直近 n 分間のスパンで条件式を書けるようにするといい。n 分間は検知を続けてくれる。

count

期間内に収集された値の数。最初のパラメータで期間を秒単位で設定するか、値の数(ハッシュマーク#を先頭に付けます)で設定します。 関数は、2番目の選択可能なpatternパラメータ、3番目のoperatorパラメータ、4番目のtime_shiftパラメータを受け付けます。 例: count(600,12,"gt") - 直近10分間の「12」より大きい値の出現回数。

つまり以下のように書く。

{mongo-replicaset:mongo[failover].count(600,"true","eq")}>0 & {mongo-replicaset:mongo[failover].count(600,"false","eq")}>0

直近600秒の間にtrueとfalseがどちらも1回以上表れた場合、true / falseが入れ替わったと判断できるので、failoverとみなす。降格したサーバと昇格したサーバが検知できるようになる。