Ganglia の mysqld プラグインを導入してみた

前回に引き続き Ganglia のお話です。
元々 MySQL のチューニングの効果検証のために導入したので、MySQL 関係のメトリックを追加します。

手軽にメトリックを追加しようと思うと、Ganglia 3.1 以降では ganglia/gmond_python_modules のものを使うのが良いみたいです。
MySQL 関係だと mysql と mysqld というのがあるんですが、graph.d があるというのと比較的最近に更新されているという理由で mysqld を使ってみることにしました。元々 Ganglia を導入して効果検証しようと思ったのは id:hirose31 さんによる「実録MySQLのチューニング 春の陣 - (ひ)メモ」がきっかけなので、mysql を使うべきかもしれませんが・・・。

mysqld プラグインのインストール

監視対象クライアントでのインストール

更新の度にアーカイブをダウンロードするのも何なんで、リポジトリをクローンします。

# mkdir /var/git
# cd /var/git/
# git clone https://github.com/ganglia/gmond_python_modules.git

次の内容で /etc/ganglia/conf.d/modpython.conf を作成します。/etc/ganglia/conf.d がない場合は作成します。

modules {
  module {
    name = "python_module"
    path = "/usr/lib/ganglia/modpython.so"
    params = "/usr/lib/ganglia/python_modules"
  }
}

include('/etc/ganglia/conf.d/*.pyconf')

必要なファイルを移動します。

# mkdir /usr/lib/ganglia/python_modules
# cp /var/git/gmond_python_modules/mysqld/python_modules/* /usr/lib/ganglia/python_modules/
# cp /var/git/gmond_python_modules/mysqld/conf.d/mysql.pyconf /etc/ganglia/conf.d/

mysql.pyconf に書いてあるように gmond 用の MySQL ユーザを作成します。

# mysql -uroot -p -e 'GRANT SUPER, PROCESS ON *.* TO health@localhost IDENTIFIED BY "hogehoge";'

/etc/ganglia/conf.d/mysql.pyconf の host と passwd をセットします。

modules {
  module {
    name = "mysql"
    language = "python"
    param host {
      value = 'localhost'
    }
    param user {
      value = 'health'
    }
    param passwd {
      value = 'hogehoge'
    }

必要な Python モジュールをインストールします。特にこだわらないので virtualenv + pip でインストールするとかじゃなくて APT でインストールします。

# aptitude install python-mysqldb

試しに起動してみます。

# /etc/init.d/ganglia-monitor stop
# gmond --debug=2

Python のバージョンの関係か、ここで次のようなエラーが出るかもしれません。

# gmond --debug=2
loaded module: core_metrics
loaded module: cpu_module
loaded module: disk_module
loaded module: load_module
loaded module: mem_module
loaded module: net_module
loaded module: proc_module
loaded module: sys_module
loaded module: python_module
udp_recv_channel mcast_join=NULL mcast_if=NULL port=8649 bind=NULL
tcp_accept_channel bind=NULL port=8649
udp_send_channel mcast_join=NULL mcast_if=NULL host=172.16.199.5 port=8649

Unable to find the metric information for 'mysql_innodb_pending_normal_aio_reads'. Possible that the module has not been loaded.

Unable to find the metric information for 'mysql_innodb_pending_log_writes'. Possible that the module has not been loaded.
(後略)

mysql_innodb_pending_normal_aio_reads とか定義されているけど、そんな情報取得できなかったよって意味です。
その場合は /usr/lib/ganglia/python_modules/mysql.py を次のように変更するとうまくいくかもしれません。変更したら忘れずに mysql.pyc を削除しましょう。

--- mysql.py.orig       2012-10-08 19:18:43.000000000 +0900
+++ mysql.py    2012-10-08 18:53:22.000000000 +0900
@@ -124,7 +124,7 @@
                if get_innodb:
                        cursor = conn.cursor(MySQLdb.cursors.Cursor)
                        cursor.execute("SHOW /*!50000 ENGINE*/ INNODB STATUS")
-                       innodb_status = parse_innodb_status(cursor.fetchone()[0].split('\n'))
+                       innodb_status = parse_innodb_status(cursor.fetchone()[2].split('\n'))
                        cursor.close()
                        logging.debug('innodb_status: ' + str(innodb_status))

ちなみに使用している Python はシステムに組み込みのもので古く、次のようになっています。

# python --version
Python 2.6.6
# python -c 'import MySQLdb; print MySQLdb.version_info'
(1, 2, 2, 'final', 0)

問題なく起動したら再起動します。

# /etc/init.d/ganglia-monitor restart

これで監視対象クライアントへのインストールは完了です。

管理サーバでのインストール

こちらでもクローンします。

# mkdir /var/git
# cd /var/git/
# git clone https://github.com/ganglia/gmond_python_modules.git

グラフを表示するためのファイルをコピーします。

# cp /var/git/gmond_python_modules/mysqld/graph.d/mysql_* /usr/share/ganglia-webfrontend/graph.d/

管理サーバは以上です!

あとは Web Frontend にアクセスして MySQL 関係のメトリックも追加されていることを確認してみてください。
以上、MySQL 関係のメトリックの追加方法でした!

追記

SHOW INNODB STATUS の Buffer pool hit rate とかも取りたかったので次のように変更しました。

/etc/ganglia/conf.d/mysql.pyconf

--- mysql.pyconf.orig	2012-10-08 19:48:22.000000000 +0900
+++ mysql.pyconf	2012-10-08 22:43:14.000000000 +0900
@@ -460,5 +460,22 @@
   metric {
     name = "mysql_aborted_clients"
   }
+
+  metric {
+    name = "mysql_innodb_pages_reads_per_sec"
+  }
+
+  metric {
+    name = "mysql_innodb_pages_creates_per_sec"
+  }
+
+  metric {
+    name = "mysql_innodb_pages_writes_per_sec"
+  }
+
+  metric {
+    name = "mysql_innodb_buffer_pool_hit_rate"
+  }
+
 }

/usr/lib/ganglia/python_modules/DBUtil.py

--- DBUtil.py.orig	2012-10-08 19:46:19.000000000 +0900
+++ DBUtil.py	2012-10-08 23:14:34.000000000 +0900
@@ -65,6 +65,7 @@
                                             dict.__repr__(self))
 
 import MySQLdb
+import re
 
 def longish(x):
 	if len(x):
@@ -178,6 +179,14 @@
 			innodb_status['pages_created'] = longish(istatus[4])
 			innodb_status['pages_written'] = longish(istatus[6])
 
+		elif re.match('.*? reads/s, .*? creates/s, .*? writes/s', line):
+			innodb_status['pages_reads_per_sec'] = float(istatus[0])
+			innodb_status['pages_creates_per_sec'] = float(istatus[2])
+			innodb_status['pages_writes_per_sec'] = float(istatus[4])
+
+		elif "Buffer pool hit rate" in line:
+			innodb_status['buffer_pool_hit_rate'] = float(istatus[4]) / 1000
+
 		# ROW OPERATIONS
 		elif 'Number of rows inserted' in line:
 			innodb_status['rows_inserted'] = longish(istatus[4])

/usr/lib/ganglia/python_modules/mysql.py

--- mysql.py.orig	2012-10-08 19:50:56.000000000 +0900
+++ mysql.py	2012-10-08 22:45:56.000000000 +0900
@@ -1068,6 +1068,30 @@
 				'value_type':'uint',
 				'units': 'txns',
 			},
+
+			innodb_pages_reads_per_sec = {
+				'description': "",
+				'value_type':'float',
+				'units': 'reads/s',
+			},
+
+			innodb_pages_creates_per_sec = {
+				'description': "",
+				'value_type':'float',
+				'units': 'creates/s',
+			},
+
+			innodb_pages_writes_per_sec = {
+				'description': "",
+				'value_type':'float',
+				'units': 'writes/s',
+			},
+
+			innodb_buffer_pool_hit_rate = {
+				'description': "Buffer pool hit rate",
+				'value_type':'float',
+				'units': '',
+			},
 		)
 
 	update_stats(REPORT_INNODB, REPORT_MASTER, REPORT_SLAVE) 

参考