MySQLを本番で運用する場合、ヘルスチェック(監視)は欠かせません。ヘルスチェックには大きく分けて死活監視(MySQLのプロセスが稼働しているかどうか)と傾向監視(レスポンスを悪化させるような兆候が表れていないか)の2点に分けられると思います。
今回はこの2点のうち死活監視、中でも「MySQLのプロセスが稼働しているかどうか」について説明します。
mysqldのプロセス確認
まずはシンプルに「プロセスが起動しているかどうか」を確認する例を考えてみましょう。MySQLのプロセスの実体はmysqldプロセスですので、これが存在しない場合当然ながらMySQLから応答は返ってきません。
$ ps auxww | grep mysqld
root 275 0.0 0.1 106192 1536 pts/0 S 09:29 0:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pid-file=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
mysql 471 0.5 17.0 1687664 173516 pts/0 Sl 09:29 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock
root 507 0.0 0.0 103300 872 pts/0 S+ 09:30 0:00 grep mysqld
最もシンプルな方法は、ps
コマンドでmysqldプロセスを探すことです。上記のようにLinux系のOSであれば、mysqldプロセスと一緒にmysqld_safeプロセスが見つかるでしょう。mysqld_safeはmysqldのラッパースクリプトであり、mysqldのプロセスダウンが発生した場合にそれを検知し、自動で再起動する役割を持っています(そのほか、起動時に環境変数を押し込む役割もあります) 。たとえばkill -9
コマンドでmysqldプロセスだけを強制終了させた場合、mysqld_safeプロセスがそれを検知して、すぐにmysqldプロセスを起動させます。
$ kill -9 471
$ ps auxww | grep mysqld
root 275 0.0 0.1 106196 1580 pts/0 S 09:29 0:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pid-file=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
mysql 538 13.5 16.9 1490904 172620 pts/0 Sl 09:34 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock
root 568 0.0 0.0 103300 868 pts/1 S+ 09:34 0:00 grep mysqld
mysqldプロセスのみpidが変更され、起動時間が新しくなったことが確認できます。なお、kill -15
コマンドでmysqldを停止した場合、MySQLの内部で正常終了処理として扱われますので、mysqld_safeはmysqldを再起動せずそのまま一緒に停止します。また、mysqld_safeプロセスそのものにMySQLの機能は無いため、何らかの理由でmysqld_safeプロセスだけが残ってしまったとしても、MySQLの機能は失われています。
この「mysqld_safeがmysqldを再起動する」という動作は良し悪しで、( mysqldのプロセスダウン自体そうそうあるものではありませんが)「 不意のクラッシュに(すぐ再起動してしまうため)気が付けない」 、「 クラッシュ後に起動まではできていても、データが壊れている可能性がある」という点を留意する必要があります。これはまた機会があれば説明したいと思います。
プロセスを確認する方法のもうひとつの手段として、PIDファイルの位置を記録しておき、PIDファイルとpidからチェックする方法があります。これは、PIDファイルの位置を一意に指定できるため、ps
コマンドのみでチェックする方法に比べて、1台のサーバーで複数のmysqldプロセスが稼働しているような場合でも取り回しがしやすいという利点があります。
$ cat /var/run/mysqld/mysqld.pid
538
$ kill -0 538
$ echo $?
0
mysqldプロセスが稼働している間は、上記のような出力が得られます。MySQLが正常に停止している場合は、MySQLのシャットダウンプロセスによりPIDファイルが削除されるため、cat
コマンドがエラーになります。MySQLが異常終了した場合、PIDファイルは残りますがプロセスが存在しなくなるため、kill
コマンドがエラーになります。このように異常終了と正常終了を判定できるのは利点です(ただし、mysqld_safe
により異常終了したmysqld
プロセスが再起動されてしまった場合、PIDファイルも再作成されるため、異常に気付くことはできません)が、「 正常に稼働しているけれど何らかの原因でPIDファイルが消えてしまった(消してしまった) 」ケースもあり得ますので、その点をカバーするためには他の確認方法と合わせて実装する必要があります(参考までに、MySQL 5.6とそれ以前のrpm版のinitスクリプトではPIDファイルからkill -0
コマンドを利用して確認し、失敗した場合にはpidof
コマンドを利用してmysqldのプロセスを探す実装になっていました。yum版では/etc/rc.d/init.d/functionsスクリプトのstatus関数を利用しています。rpm版、yum版の違いについては「第10回 yum, rpmインストールにおけるMySQL 5.6とMySQL 5.7の違い 」も参考にしてください) 。
$ cat /var/run/mysqld/mysqld.pid
cat: /var/run/mysqld/mysqld.pid: No such file or directory
$ kill -0 538
-bash: kill: (538) - No such process
mysqladminコマンドによる確認
MySQLはデフォルトでTCP 3306番ポートを利用し、TCPの上にMySQLプロトコルを利用して通信します。たとえプロセスが起動していても、MySQLプロトコルのレイヤーで通信ができないのであればMySQLは稼働しているとは言えません。
MySQLプロトコルのレイヤーでの死活監視を行うために、MySQLにバンドルされているmysqladmin ping
コマンドを利用することができます。
$ mysqladmin -h127.0.0.1 -P3306 -uroot ping
mysqld is alive
$ echo $?
0
mysqladmin ping
コマンドは接続先のMySQL(この場合、-h127.0.0.1
で接続先IPアドレス、-P3306
で接続先ポート、-uroot
で接続ユーザ名を指定しています)にログイン要求を送信し、ログインに成功または認証拒否(ユーザ名とパスワードの組が不正)の応答を受け取った場合に成功します。ログインの成功はわかりやすいですが、認証拒否の場合、「 ユーザ名とパスワードの組が不正であることを判断できるということはMySQLは稼働していると考えられる」ため、mysqladmin ping
は成功したと見做されます("Error: 1040 Too many connections"などの「サーバーから拒否」のエラー番号もping成功と見なされます) 。
それに対して、失敗した時の出力例は下記のようになります。
$ mysqladmin -h127.0.0.1 -P3306 -uroot ping
mysqladmin: connect to server at '127.0.0.1' failed
error: 'Can't connect to MySQL server on '127.0.0.1' (111)'
Check that mysqld is running on 127.0.0.1 and that the port is 3306.
You can check this by doing 'telnet 127.0.0.1 3306'
$ echo $?
1
$ perror 111
OS error code 111: Connection refused
接続に失敗した旨のメッセージと多少の診断メッセージ、失敗を意味するリターンコード("$?"が0以外)が得られました。注目するべきはmysqladmin ping
コマンドの出力の2行目で、error: 'Can't connect to MySQL server on '127.0.0.1' (111)'
のカッコの中、111というのが接続に失敗した理由です。
MySQLにバンドルされているperror
コマンドを利用すると、エラー番号111の意味を調べることができます。今回は「Connection refused」 、ポートがLISTENされていない(=プロセスが起動していない)場合に出力される典型的なものでした。111の他にも110(「 Connection timed out」 、タイムアウトなのでMySQLが忙しいか通信経路が不安定な場合など) 、113(「 No route to host」 、指定のIPアドレスに対する経路が存在しないためIPアドレス間違いかネットワーク障害など)といったいくつかのパターンがあります。
perror
コマンドの使い方を憶えておけば、これらのエラー番号から少し情報を引き出すことができますので、合わせて使い方を憶えておくと良いと思います(といっても、引数にエラー番号を与えるだけなので特に憶えることもありませんが) 。
mysqladmin ping
コマンドはプロセス監視に比べて、「 mysqldが複数あってもIPアドレスとポート番号(socket接続であればソケットファイル名)で一意に識別できる」 、「 プロセスが存在していても応答を返せないようなケース(たとえばゾンビプロセス)に検知できる」という利点があります。筆者の考える限り、デメリットらしいデメリットは特にない(ほぼMySQLへの接続処理だけで十分高速で影響を与えることはない、接続用のユーザはダミーでも問題ない…ただし、ログレベルによってはエラーログに認証失敗の警告が出力されます)ので、ps
コマンドよりももうちょっと手軽に複数のmysqldを確認したい時に便利です。
ただし、残念ながらmysqladmin ping
コマンドも(ps
コマンドよりは便利に使えたとしても) 、「 mysqldプロセスが起動しており、MySQLプロトコルでしゃべりかけた場合にMySQLプロトコルで応答を返した」以上のことは判定できません。先ほども話に上げましたが、"Error: 1040 Too many connections"は、アプリケーションからすれば処理ができない異常系ですが、mysqladmin ping
コマンドから見れば「mysqldが起動しており、MySQLプロトコルでしゃべりかけた場合にMySQLプロトコルで応答を返した」ので正常系です。テーブル間のデータの不整合、ロックの競合、過負荷などにより「接続はできてもアプリケーションは処理を継続できないケース」であっても、mysqladmin ping
から見れば正常というケースは少なくないのです(これらMySQL内部の動作は、ps
コマンドやkill -0
コマンドでも観測できません) 。
アプリケーションからの確認
今回は「MySQLのプロセスが稼働しているかどうか」について説明しました(ので、この項では多くは説明しません) 。「 MySQLのプロセスが稼働している」は「データベースが問題なく動いている」の必要条件ですが十分条件ではありません。「 データベースが問題なく動いている」かどうかはMySQLの外側から 監視する必要があります。
たとえば「データベースに接続して特定のテーブルを想定の時間内でSELECTすることができるかどうか」 、「 接続しているクライアントの数は想定の範囲内か」 、「 デッドロックの発生回数は想定の範囲内か」など、外側からの監視はいくらかロジカルでルールが複雑です(特に、「 想定の範囲内」をどこに置くかは利用用途ごとに大きく異なります) 。過不足なく全ての情報が手に入る夢のような監視機構があればぜひ利用したいところですが、残念ながら筆者の出会ったものの中に「銀の弾丸」になりそうなものはありませんでした(筆者は結局「自分にとっての過不足なく」を実現するために、プラグインを自作する道を選びました) 。もちろん、既成のプラグインでも「自身にとって過不足なく」情報が取れるものがあれば、積極的に取り入れていけば良いでしょう。
既に死活監視にNagiosかZabbixを利用している場合であれば Percona Monitoring Plugin など、既成のプラグインを利用することもできますので、情報を探して自分に合うものを見つけてください。