管理・運用

July 03, 2010

Parallel::Scoreboard でワーカープロセスをモニタリングする方法

 cho45 さんの Plack::Middleware::ServerStatus (Starman や Starlet で Apache の mod_status 相当の情報を得られるようにする - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 - subtech) に続き、昨日 kazeburo さんが「StarmanやStarletでmod_statusっぽい情報を得る簡易版Plack::Middleware::ServerStatus - blog.nomadscafe.jp」というエントリを書かれていらっしゃいましたが、ウェブアプリケーションサーバに限らず、複数のワーカープロセスが動作するシステムにおいて、それらの状況をモニタリングするためのスコアボードがほしい、というケースはよくあることだと思います。

 また、プロセス名を使う方法は、他の監視ツールとの相性が悪い、プロセス名の取得方法にポータビリティがない、あまり長いステータスを保存できない、バッファオーバーフローの可能性がある、といった問題があります。

 なので昨日、特定のミドルウェアや特定の規格 (Plack) に依存しない、より汎用的で確実な実装があるといいよね、という話をしていたのですが、気が向いたので書いてみました。新幹線ハック!

 使い方は簡単。なんでも保存できちゃうので JSON で構造化された情報を保存したり、あるいは HTTP の全リクエストを書いておいてモニタリングに活用する、といったことも可能です。

use Parallel::Scoreboard;

# 初期化
my $scoreboard = new Parallel::Scoreboard(
    base_dir => '/tmp/my_scoreboard',
);

# ワーカープロセス側からステータスを保存
$scoreboard->update('this is my current status');

# 全ワーカープロセスのステータスを読み込み
my $stats = $scoreboard->read_all();
for my $pid (sort { $a <=> $b } keys %$stats) {
    print "status for pid:$pid is: ", $stats->{$pid}, "\n";
}

 内部的な仕組みとしては、プロセス毎にステータスファイルを作り、flock を使って死活監視を行うようになっています。

 コードは github/kazuho/p5-Parallel-Scoreboard にあります。また CPAN にもあげておいたので、もうじき seach.cpan.org/dist/Parallel-Scoreboard からダウンロードできるようになると思います。それでは have fun!

注: 「革命の日々! setproctitle(3)のカーネルサポートが-mmに入りました」等を参照

February 24, 2010

既製品の管理ツールを使わないことでウェブサービスの TCO を下げる話について hbstudy#8 で話してきた件

 昨日、hbstudy#8 で話をする機会をいただくことができたので、Nagios や Amanda といった既製品の管理ツールやバックアップツールを使わずに内製したことで「パストラック」の運用コストを下げた、という話をしてきました。

 もちろん、「既製品を使わない」というのもひとつの手段にすぎませんから、それを無闇にお勧めするつもりはありません。ただ、小回りの効くツールを組み合わせる手法にも十分な競争力があるという点、あるいはその事例として参考になれば幸いです。

 スライドはこちら。hbstudy 運営の皆様、話を聞いてくださった皆様、ありがとうございました。

January 18, 2010

監視とは継続的なテストである、という話 (もしくは cronlog とテストスクリプトを組み合わせた監視手法について)

 結論から先に。cronlog を使えば、アプリケーションのテストコードと全く同じ形式で、監視用のスクリプトを書くことができます。プログラマが監視ツールの記法を覚える必要はありません。これは、プログラマが運用も行うケースでは特に有効な手法だと思います。

 先週公開した Kazuho@Cybozu Labs: crontab を使って効率的にサービス監視する方法 というエントリで、crontab と拙作の cronlog を用いてサービス監視を書く手法を紹介しました。しかし、挙げた例はいずれも ping や http のテストといった外形監視の手法です。RDBMS とウェブアプリケーションのみから構成されるサービスならそれだけで十分でしょう。

 しかし、外形監視だけでは、メッセージキューのような非同期処理の遅延を観測することはできません。また、http のログを監視して、エラーレスポンスや平均応答時間の監視も行うことが望ましいです。それらの処理をどう書くか。

 自分は、監視とは継続的なテストである、という観点から、キューの処理遅延等の監視を、アプリケーションのテストスートの一部として書いています。具体的には、以下のような感じ (一部改変)。テストはアプリケーションと設定情報を共有しているので、監視ソフトウェアに様々な設定値を入力する必要もありません。

use strict;
use warnings;

use Test::More;
use Pathtraq;

my $app = Pathtraq->new;

cmp_ok
    do {
        my $row = $app->dbh->selectrow_arrayref(
            'select XXXX', # Q4M 使ってない昔のキュー監視
        ) or die $app->dbh->errstr;
        $row->[0];
    },
    '<',
    1000,
    'delay of updater';

for my $tbl (qw(analyze_page set_page_info analyze_keyword)) {
    cmp_ok
        do {
            my $row = $app->dbh_queue->selectrow_arrayref(
                "select count(*) from $tbl",
            ) or die $app->dbh_queue->errstr;
            $row->[0];
        },
        '<',
        1000,
        "delay of $tbl",
}

done_testing;

 あとは、これらのテストコードを、crontab 上で cronlog を介してテストの実行ツール (perl なら prove) に食わせるだけ。

*/5 * * * * cd /var/webapp && exec setlock -nX /tmp/monitor.lock cronlog -- prove -v monitor.t/*.t 2>&1

 これで、監視対象項目 (=テスト項目) になんらかの問題が見つかった場合には、以下のような感じのメールが届くようになります。

Subject: Cron <user@host> cd /var/webapp && exec setlock -nX /tmp/monitor.lock cronlog -- prove -v monitor.t/*.t 2>&1

host.example.com starting: prove -v monitor.t/queue.t

#   Failed test 'delay of analyze_page'
#   at monitor.t/queue.t line 23.
#     '1396'
#         <
#     '1000'
# Looks like you failed 1 test of 4.
monitor.t/queue.t ..
ok 1 - delay of updater
not ok 2 - delay of analyze_page
ok 3 - delay of set_page_info
ok 4 - delay of analyze_keyword
1..4
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/4 subtests

Test Summary Report
-------------------
monitor.t/queue.t (Wstat: 256 Tests: 4 Failed: 1)
Failed test:  2
Non-zero exit status: 1
Files=1, Tests=4,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.40 cusr  0.08 csys =  0.50 CPU)
Result: FAIL
command exited with code:1

 perl に限らず、テストの実行ツールは一般に、成功した場合はゼロ、失敗した場合は非ゼロを返します (そうしないと make test && make install とか自動化できない)。この特性と cronlog の、非ゼロが返った場合だけ cron を経由してログをメールで送信する、という機能を組み合わせることで、テストコードが監視系に早変わりするのです。さらに外側で setlock -nX しているのは、5分間でテストが終了しなかった場合に、やはりエラーメールを送信するため。

 以上のように、cronlog を使えば、プログラマが使い慣れたテストスートの一部として監視系を書くことができます。パストラックでは更に、リバースプロキシのログ監視を touch_if コマンドとテストコードを連動する形で実施するようにしています。どうぞご覧ください。

blockdiff を使ったお手軽ホットバックアップ環境の構築 (Linux, MySQL, etc.)

 一昨日に開催された hbstudy #7 にバックアップの話を聞きに行ってきました。Amanda を中心にした話で、とても勉強になりました。が、設定がめんどくさそうだなぁ、とも。自分の需要にはあわない感じでした。

 勉強会が終わったあとで、自作のバックアップスクリプト blockdiff に関する話を何人かの方とさせていただいたのですが、思いのほか反応が良かったので、あらためて紹介したいと思います。

 blockdiff は、一言でいうと、パーティションやデータベースのデータファイルの差分バックアップツールです。rsnapshot に似ていますが、rsnapshot ではデータベースのホットバックアップ不可能です。逆に blockdiff はディレクトリ単位でのバックアップには対応していないかわり、ファイルシステムやデータベースを、一貫性を保ちつつ実質無停止で差分バックアップすることができます

 blockdiff の具体的な特徴は以下のとおりです。

  • 設定ファイルが不要
  • 複雑な設定がありません。コマンドライン (あるいは crontab) で、適当な引数をつけてバックアップコマンドを呼び出すだけの簡単インターフェイスです。コマンドラインで動作を制御するため、(ホットバックアップを含む) 様々なバックアップロジックを組むことも可能です。

  • バックアップを取るサーバにソフトウェアのインストールが不要
  • ssh 経由でリモートサーバのバックアップを取ることができます。サーバにバックアップソフトウェアをインストールする必要はありません (バックアップを取るために必要なプログラムは自動的に送り込まれます) 注1

  • フルバックアップと増分バックアップに対応
  • フルバックアップと増分バックアップに対応しています。また、下位レイヤのスクリプト (blockdiff_dump) を直接実行すれば、差分バックアップも可能です。

  • ファイル単位、あるいは LVM スナップショットを使ったバックアップが可能
  • ファイル単位のバックアップと、LVM スナップショットを使ったホットバックアップに対応しています。

  • MySQL のホットバックアップが可能
  • 同梱の mysqllock コマンドと組み合わせることで、LVM スナップショットを使った MySQL データベースのホットバックアップが可能です。PostgreSQL については確認はしていませんが、ロックコマンドを使わなくてもバックアップが取れるんじゃないかと思います。

  • LVM ベースの VM のホットバックアップが可能
  • LVM ベースの仮想ディスクを用いた Xen の DomU のホットバックアップが可能です注2。私は使っていないので試していませんが、KVM の仮想マシンについても同等のことが可能だと思われます。LVM ベースではない VM についても、スナップショット機構によってはホットバックアップが可能だと思われます。

 blockdiff を使った LVM ボリュームのバックアップ方法については Kazuho@Cybozu Labs: リモートからXenのDomUとかLVMやファイルを差分バックアップするスクリプトを書いた で触れたので、ここでは MySQL データベースをホットバックアップする場合の設定を紹介したいと思います。前提として、MySQL のデータが LVM ボリューム上に保存されている必要があります。

 バックアップスクリプトは、以下のような感じになります。

#! /bin/bash

export BLOCKSIZE=16834
export LVCREATE_PREFIX="mysqllock --host=db-host --user=root --password=XXX"
export YEARMONTH=`date '+%Y%m'`

blockdiff_backup /var/backup/db-backup-$YEARMONTH bin/ssh_lvm_dump --gzip \
  root@db-host /dev/mapper/logical_volume_of_mysql

このバックアップスクリプトは以下のことを行っています。

  • バックアップのブロックサイズを InnoDB のブロックサイズである 16KB に設定
  • スナップショットを取る瞬間に mysqllock コマンドを使って FLUSH TABLES WITH READ LOCK を使うことで、MyISAM テーブル等の一貫性のあるバックアップを実現
  • 1ヶ月毎にフルバックアップ。それ以内は増分バックアップ

 バックアップスクリプトを実行すると、最初に db-backup-YYYYMM.1.gz, db-backup-YYYYMM.1.md5, db_backup-YYYYMM.ver という3つのファイルが作成されます。YYYYMM.1.gz がバックアップデータ、YYYYMM1.md5 がブロック単位のチェックサム情報です。次に実行すると YYYYMM.2.* が、その次は YYYYMM.3.* が、という形で増分バックアップが増えていきます。最新のバックアップの番号は .ver ファイルが記憶しているので、バックアップが途中で失敗しても問題ありません。次回バックアップ時に壊れたバックアップファイルが上書きされます。

% ls -l db-backup-201001*
-rw-r--r-- 1 backup backup 50289166539 2010-01-01 05:35 db-backup-201001.1.gz
-rw-r--r-- 1 backup backup   131072004 2010-01-01 05:35 db-backup-201001.1.md5
-rw-r--r-- 1 backup backup 10914423057 2010-01-02 04:32 db-backup-201001.2.gz
-rw-r--r-- 1 backup backup   131072004 2010-01-02 04:32 db-backup-201001.2.md5
-rw-r--r-- 1 backup backup 13648250036 2010-01-03 04:33 db-backup-201001.3.gz
-rw-r--r-- 1 backup backup   131072004 2010-01-03 04:34 db-backup-201001.3.md5
...
-rw-r--r-- 1 backup backup           3 2010-01-18 04:34 db-backup-201001.ver

 バックアップスクリプトが正しく動作することを確認したら、crontab を設定して毎日バックアップを取るようにします。

0 4 * * * setlock -nX /var/backup/backup.lock cronlog -l /var/backup/backup.log -t -- /var/backup/backup.sh 2>&1

 ここでは、daemontools の setlock コマンドを使って万が一の重複起動を抑止しています。また、kaztools の cronlog コマンドを使うことで、バックアップの進捗をログに保存しつつ、バックアップが失敗した場合は cron 経由でアラートメールが送信されるようになっています。

 ちなみに、バックアップのログ (backup.log) は、毎日次のような感じで追記されていきます。データベースの停止期間 (アプリケーションからのクエリがロックされる期間) は 03:35:56 から 03:36:00 までの4秒間であり、実質無停止でのバックアップができていることがわかります。

------------------------------------------------------------------------------
[Sat Jan  9 03:05:02 2010] backup-srv starting: /var/backup/backup.sh
[Sat Jan  9 03:05:02 2010] creating snapshot...
[Sat Jan  9 03:05:07 2010]   Logical volume "lvm_dump" created
[Sat Jan  9 03:05:07 2010] running: bin/ssh_blockdiff_dump --gzip "root@db-host" "/dev/db/lvm_dump"...
[Sat Jan  9 03:19:22 2010] removing snapshot /dev/db/lvm_dump...
[Sat Jan  9 03:19:23 2010]   Logical volume "lvm_dump" successfully removed
[Sat Jan  9 03:19:23 2010] backup completed successfully
[Sat Jan  9 03:19:23 2010] creating snapshot...
[Sat Jan  9 03:35:56 2010] issuing lock statement: FLUSH TABLES WITH READ LOCK
[Sat Jan  9 03:36:00 2010]   Logical volume "lvm_dump" created
[Sat Jan  9 03:36:00 2010] issuing unlock statement: UNLOCK TABLES
[Sat Jan  9 03:36:00 2010] running: bin/ssh_blockdiff_dump --gzip "root@db-host" "/dev/x25m.1/lvm_dump"...
[Sat Jan  9 04:18:44 2010] removing snapshot /dev/x25m.1/lvm_dump...
[Sat Jan  9 04:18:46 2010]   Logical volume "lvm_dump" successfully removed
[Sat Jan  9 04:18:46 2010] backup completed successfully
[Sat Jan  9 04:18:46 2010] command exited with code:0

 このように、blockdiff を使うことで VM 単位のバックアップだけでなく、データベースのホットバックアップも簡単に取ることができます。パストラックでは、これらバックアップスクリプトに処理を追加し、バックアップ中は統計処理を抑止するといったことも行っています。

 blockdiff や kaztools は、いずれも私の github リポジトリ (github.com/kazuho) からダウンロードして perl Makefile.PL && make all test && make install でインストール可能です。あるいは nopan がインストールされていれば、nopan http://github.com/kazuho/blockdiff http://github.com/kazuho/kaztools と1コマンドでインストールすることができます。いずれもパストラックで実際に運用しているツール群ですが、興味のある方は (at your own risk で) お試しあれ。

注1: バックアップを取るサーバに Perl 5.8 以降がインストールされている必要があります。また、公開鍵認証を用いて SSH ログインできる必要があります
注2: XenServer のスナップショット機能とは併用できません

January 14, 2010

crontab を使って効率的にサービス監視する方法

監視とは継続的なテストである、という話 (もしくは cronlog とテストスクリプトを組み合わせた監視手法について)に続きます

 今日ようやく、積ん読状態だった「Software Design 2010年1月号」を手に取ったのですが、特集が「今日から使えるスクリプト満載! [プロ直伝]お手軽サーバ監視術」。興味深く拝読したのですが、もっと楽ができるのにと思うところも。ちょうど、昨年末に運用しているサービス「パストラック」のサーバを移転し、crontab と perl で書かれたスクリプト群を使った監視環境を構築したところなので、そこで使っているスクリプト cronlog を紹介したいと思います。

 特集の前書きにも書かれていることですが、サーバやネットワーク機器が多数ある環境なら、Nagios を始めとする、専ら監視のために作られたソフトウェアを使って、監視システムを構築すべきです。逆に小規模な環境で crontab やスクリプトを監視に使うメリットがあるとしたら、それは、極力覚えることや必要な設定を減らせる、という点でしょう。

 監視項目毎にスクリプトを書くなんて面倒でやってられません。そこで cronlog! cronlog を使うことで、既存のコマンドを crontab を使った監視系に早変わりさせることができます。

 cronlog は、引数として与えられたコマンドを起動し、その終了コードが非ゼロであった場合に、コマンドの出力結果 (stdout と stderr) を標準出力にプリントするコマンドです。何を言っているか良くわからないかもしれませんが、cronlog を使うと、たとえば ping によるサーバ監視は crontab に以下のように書くことで可能となります。

MAILTO=alert@example.com
5 * * * * cronlog -- ping -n 5 my-server 2>&1

 この crontab を解説すると、次のようになります。ping コマンドの終了コードは、応答が返ってきた場合 0、返ってこない場合に 1 です。cronlog は終了コードが非ゼロの場合のみ、実行されたコマンド (ここでは ping) の出力結果を出力、つまり ping がエラーを返した場合にのみ、その内容を cron に受け渡すことになります。cron は何か出力を受け取ると MAILTO に指定されたアドレスに、その内容をメールとして送信します。以上をまとめると、ping 応答が返ってこなかった場合にエラーメッセージを送信するという処理が、上の1行で実現できていることが分かると思います。

 これなら、監視項目ごとにスクリプトを書かなくてもいいので簡単です。

 ping のかわりに HTTP の監視をしたいのなら、

5 * * * * cronlog -- wget -O - http://my-server 2>&1

と書けば OK です。あるいは、ping 監視に成功した場合のみ HTTP 監視を行う、というようなマニアックな処理も、

5 * * * * cronlog -- ping -n 5 my-server 2>&1 && cronlog -- wget -O - http://my-server 2>&1

とすることで実現できます。監視項目が大量にあったり、ワンライナーで書きたくない場合は、スクリプトを書いて、それを cronlog で呼び出せばいいのです。注意すべきは唯一、障害を検知した場合は、非ゼロの値で exit すること。このへんも小技があるのですが、それはさておき。

 cronlog には、ログをファイルに保存したり、ログの各行にタイムスタンプを挿入するオプションもあります (というか、cronで実行するタスクのログを保存しつつ、タスクがエラーを返したらメールを送信する、というのが本来の機能です。監視=副作用のないタスク、なだけ)。詳しくは man cronlog をご覧ください。

PS. cronlog はオレオレ監視スクリプト群 kaztools の一部です。インストール方法は github.com/kazuho/kaztools からダウンロードしてきて perl Makefile.PL && make && make test && make install。興味のある方はどうぞ。

November 17, 2009

リモートからXenのDomUとかLVMやファイルを差分バックアップするスクリプトを書いた

kazuho's blockdiff at master - GitHub

 月曜から XenServer の運用を始めたんですが、以下のような要件のバックアップツールがほしくなりまして。ちょっと調べた範囲で見つからなかったので、書いてみました。

  • スナップショットによる無停止でのバックアップ
  • 差分/多世代バックアップが可能
  • ネットワーク越しにプルベースでバックアップが可能

 元々は去年書いたバイナリファイルの差分バックアップスクリプト (データベースの差分バックアップとウェブサービスのお引っ越し)。これを拡張して、LVMを操作したり、バージョン番号を自動採番するようにしたり、ssh経由での転送機能をつけたりした感じです (ディスクは遅いからギガイーサなら over ssh でも問題ない)。

 詳しいことを知りたい人にはソースコードを読んでいただくとして (といっても300行ちょい)、たとえば XenServer 上の DomU のバックアップを取るなら、サーバ上で xe vm-disk-list コマンドを実行して論理ボリューム名を取得したあと、バックアップ用マシンの crontab にこんな感じで設定します。Xen以外の一般的なLVMのバックアップを取ることもできます。

0 3 * * * blockdiff_backup /var/xenbackup/vm01-`date '+%Y%m'` ssh_lvm_dump -z root@xenserver /dev/VG_XenStorage-... 2>&1

 これで、毎月1日にフルバックアップ、他の日は差分バックアップがとれる。リストアは、こんな感じ。

# for i in vm-01-200911.*.gz ; do \
>   gunzip < $i | blockdiff_merge /dev/VG_XenStorage-...
> done

 あとは、差分バックアップ時にディスクを全スキャンしなくてもよくなれば、もっとバックアップ頻度を上げられていいのにな、と思っています。

11/19追記: XenServerで使う場合の制限と詳細については、下記URLをご参照ください。

August 06, 2009

Deployment of MySQL using daemontools, XtraBackup

I am sure many people have already done similar things, but to ease my pain of setting up mysqld on a large-scale environment (I am trying to create a set of database nodes, each node consists of a MySQL failover cluster using semi-sync replication, that can be administered easily), I have just finished writing a deployment script called mysqld_jumpstart.  The caveats are:

  • integration with daemontools (mysqld is automatically started)
  • setup of masters and slaves
  • can setup slaves from backup data generated by XtraBackup

The last feature was the one I especially needed, since thanks to the people at Percona, things have become much easier with XtraBackup (or with InnoDB Hot Backup) since there is no more need to detach a mysqld before creating a backups.

Setting up and starting a master database:

# ssh root@10.0.1.1 mysqld_jumpstart \
--mysql-install-db=/usr/local/mysql/scripts/mysql_install_db \
--mysqld=/usr/local/mysql/bin/mysqld --base-dir=/var/my_webapp/mysql \
--data-dir=/var/datadrive/my_webapp --server-id=10 \
--replication-user=repl --replication-password=replpass \
--replication-network='10.0.0.0/255.0.0.0'

To create and a start a slave from a backup by XtraBackup (specified by --data-dir):

# ssh root@10.0.1.2 mysqld_jumpstart \
--mysql-install-db=/usr/local/mysql/scripts/mysql_install_db \
--mysqld=/usr/local/mysql/bin/mysqld --base-dir=/var/my_webapp/mysql \
--data-dir=/var/datadrive/my_webapp --server-id=10 \
--replication-user=repl --replication-password=replpass \
--replication-network='10.0.0.0/255.0.0.0' \
--master-host=10.0.1.1 --from-innobackupex

Mysqld_jumpstart can be downloaded from coderepos.org/share/browser/platform/mysql/mysqld_jumpstart.  Use --help to find out how to use the script.  FYI to run the tests, you need to apply a patch to XtraBackup.