株式会社クリアコード > ククログ

ククログ


採用活動を再開

昨年の夏くらいから採用活動を中止していましたが、再開することにしました。

応募条件は変わらず「プログラミングが好きなこと」です。フリーソフトウェアに理解があることが望ましいです。

1名のみの募集ですが、興味のある方は採用情報を参照してください。

2010-04-01

Muninプラグインの作り方

モニタリングツールMuninのプラグインの作り方を簡単に紹介します。ざっくり紹介しているので、説明を省いているところもあります。というのも、次の話の準備のための説明だからです。

Muninはネットワークのモニタリングに用いられることが多く、監視対象の各ホストに情報収集デーモン(munin-node)を配置し、そのデーモンがネットワークの情報などを収集します。監視ホストは定期的に情報収集デーモンにアクセスし、監視対象のホストの情報をグラフにします。グラフはHTMLで出力されます。PHPなどを導入しなくてもよいため、導入が楽ですし、リソース消費も少ないです。

監視対象ホスト上でどの情報を収集するかはインストール後に追加・削除できます。情報を収集する部分がプラグイン化されているためです。情報収集デーモンmunin-nodeにプラグインをインストールすればそのプラグインが収集する情報がモニタリング対象となり、自動的に監視ホスト上でグラフ化されます。

Muninはネットワークをモニタリングするために使われることが多いですが、プラグインが収集する情報はネットワーク情報に限定されていないため、仕様に従ったプラグインさえインストールすれば任意の情報をグラフ化することができます。

先日リリースされたmilter manager 1.5.0でもSMTPクライアントの同時接続数や各milterの適用結果をモニタリングするMuninプラグインを提供しています。SMTPクライアントの同時接続数はネットワークっぽい感じがしますが、milterの適用結果はあまりネットワークっぽい感じがしませんね。

それでは、まず、プラグインの仕様です。

Muninプラグインの仕様

Muninプラグインは以下の入力と出力を満たす実行ファイルです。RubyスクリプトでもシェルスクリプトでもC言語で書いてコンパイルしたプログラムでもかまいません。ただ、後述するmunin-node-configureを使いやすいのでスクリプト言語の方がオススメです。

入力
実行ファイルへの引数。以下のいずれかが渡される。
  • config: 収集する情報のメタ情報を要求
  • autoconf: ホスト上でプラグインが利用可能かどうかの判断を要求
  • snmpconf: 収集する情報のSNMP情報を要求
  • suggest: プラグインのパラメータを要求(後述)
  • 引数なし: 収集する値を要求
出力
出力は入力毎に異なります。
  • config: 以下のような1行1パラメータというフォーマットでグラフや収集する情報を出力します。パラメータのキーの一覧はprotocol-config - Munin - Trac(英語)にあります。

    1
    2
    3
    4
    
    echo "graph_title milter manager statistics"
    echo "graph_category milter-manager"
    # ...
    exit 0
    
  • autoconf: プラグインが利用可能なら"yes"を出力し、終了コード0で終了します。利用不可なら"no"を出力し、終了コード1で終了します。以下はシェルスクリプトでの例です。

    1
    2
    3
    4
    5
    6
    7
    
    if [ "$available" = "yes" ]; then
      echo "yes"
      exit 0
    else
      echo "no (daemon isn't running)"
      exit 1
    fi
    
  • snmpconf: ConcisePlugins - Munin - Trac(英語)あたりを参照してください。
  • suggest: 利用可能なプラグインのパラメータを1行に1つずつ出力します。(詳しくは後述)

    1
    2
    3
    
    echo "report"
    echo "status"
    exit 0
    
  • 引数なし: 以下のように収集したデータを出力します。

    1
    2
    3
    
    echo "accept.value 4"
    echo "reject.value 31"
    exit 0
    

と、このくらいのことは検索すると日本語の情報もいろいろでてきます。が、なぜかautoconfとsuggestのことはあまりでてきません。これがMuninプラグインの便利なところな気がしますが、どうしてでしょう。

ということで、そのあたりの紹介もします。

munin-node-configure

Muninにはmunin-node-configureというプラグインの追加・削除を自動的に行ってくれるツールがついています。ブログなどに書かれているプラグインの追加方法は以下のように手動でやっていることが多いです。

% sudo ln -s /usr/share/munin/plugins/cpu /etc/munin/plugins/

しかし、手動で追加・削除は面倒です。システム管理をしている人ならなるべく自動化したいと思うことでしょう。

munin-node-configureを使うと、プラグインの引数に"autoconf"を渡して、プラグイン自身に利用可能かを判断させ、その結果に応じて追加・削除することができます。自作プラグインでも"autoconf"に対応することで管理がだいぶ楽になります。ブログなどに載っている自作Muninプラグインは一回限りの利用だけを想定しているからなのか、"autoconf"に対応しているものが少ないように見えます。

"autoconf"に対応していることをmunin-node-configureに伝えるためには以下の行をプラグイン内に埋め込んでおく必要があります。バイナリの実行ファイルよりスクリプトの方がよいと書いたのはこのためです。

1
2
#%# family=auto
#%# capabilities=autoconf

このような行があるとmunin-node-configureは"autoconf"引数付きで呼び出してくれます。このときに"yes"と出力すれば自動で追加対象にしてくれます。ConcisePlugins - Munin - Trac(英語)あたりも参照してください。

munin-node-configureを利用した自動追加・削除は以下のコマンドでできます。

% sudo -H /usr/sbin/munin-node-configure --shell --remove-also | sudo -H sh

munin-nodeを再起動すれば反映されます。

% sudo /etc/init.d/munin-node restart

最近だと、/etc/init.d/以下のスクリプトを直接実行するよりもservice(8)を使う方がよいでしょう。

ワイルドカードプラグイン

ワイルドカードプラグインとは1つのプラグインで複数のグラフの情報を収集できるプラグインのことです。(1.4からは同じようなことができるマルチグラフプラグインという仕組みも増えています。)

ワイルドカードプラグインはプラグインファイルのファイル名の最後が「_」で終わっています。使うときは、ファイル名の最後にパラメータをつけたシンボリックリンクを張ります。

例えば、「milter_manager_」というワイルドカードプラグインに「status」というパラメータを渡したいときは以下のようにします。

% sudo ln -s /usr/share/munin/plugins/milter_manager_ /etc/munin/plugins/milter_manager_status

プラグイン内では自分のファイル名(シェルやRubyでいえば$0)を見てパラメータを受けとります。これにより、1つのプラグインファイルで複数の情報を収集することができるようになります。

そして、どのパラメータを指定すればよいかをプラグインに判定させて自動でワイルドカードプラグインを追加・削除することもできます。このときに使われる入力が"suggest"です。この機能に対応していることをmunin-node-configureに教えるためにはプラグイン内に以下のような行を書いておきます。

1
2
#%# family=auto
#%# capabilities=autoconf suggest

これを書いておけばmunin-node-configureが"suggest"引数付きでワイルドカードプラグインを実行してくれます。このとき、"status"と"report"というパラメータが利用できるのであれば、以下のように1行につき1パラメータで出力します。

status
report

こうすると、1つのワイルドカードプラグインを2種類のプラグインとしてインストールしてくれます。このとき、インストールされるプラグインは「milter_manager_status」、「milter_manager_report」というような名前になります。

追加・削除方法は変わりません。

% sudo -H /usr/sbin/munin-node-configure --shell --remove-also | sudo -H sh

munin-nodeの再起動も忘れないようにしましょう。

% sudo /etc/init.d/munin-node restart

まとめ

Muninプラグインの作り方を(実例は示さずに)紹介しました。実例を示さない代わりに本家のドキュメントの概要箇所をリンクしています。具体例はドキュメントやインストール済みのプラグインを見てください。プラグインは小さいシェルスクリプトかPerlスクリプトのことが多いので、読むとすぐにわかるでしょう。

また、管理がとても楽になり便利なのに、なぜかあまり触れられていない"autoconf"と"suggest"についても説明しました。自分でMuninプラグインを作るときは"autoconf"に対応することをオススメします。もちろん、ワイルドカードプラグインにする場合は"suggest"にも対応することをオススメします。

いつか、ソフトウェア開発にもMuninを使う例を紹介したいものです。例えば、自動化された単体テストのテスト数やカバレッジ率などもMuninを使ってグラフ化することができます。

2010-04-08

Cutter 1.1.3リリース

C・C++言語用の単体テストフレームワークCutterのバージョン1.1.3をリリースしました。

ハイライト

今回のリリースではデータ駆動テストのサポートを強化しています。サポートしている型が増えたので、これまで以上にテストデータを作りやすくなっています。

例えば、このようにテストデータを作ることができます。

1
2
3
4
5
gcut_add_datum("normal case",
               "expected", G_TYPE_CHAR, 'e',
               "input", G_TYPE_STRING, "Cutter",
               "n", G_TYPE_UINT, 4,
               NULL);

詳しくはリファレンスマニュアルの便利なテストデータ用API - gcut_add_datum()あたりを見てください。

Cutterユーザ(徐々に)増加中

Cutterが今のようなGLibベースの作りになってから2年半くらい経ちますが、徐々にユーザが増えてきました。最近、nfc-toolsという近距離無線通信(NFC)用のツールを開発しているオープンソースプロジェクトでも使われているのを見つけました。

groongaもCutterを採用しているプロジェクトで、Cutterのカバレッジ支援機能なども使っています。groongaプロジェクトではCutterが提供している支援機能だけではなく、コミットした人毎のカバレッジ率などもグラフ化して公開しています。今後のリリースで、Cutter本体がこのような見せ方を支援する機能を提供したいですね。

まとめ

Cutter 1.1.3の目玉機能とCutter採用プロジェクトについて紹介しました。

C・C++のプロジェクトで使うテスティングフレームワークを探しているならCutterも検討してみてはいかがでしょうか。

タグ: Cutter
2010-04-14

Ruby 1.8.7/1.9.1どちらでも使えるWindows用バイナリ入りgemをDebian GNU/Linux上で作る方法

groongaのRubyバインディングrroonga 0.9.3がリリースされました。rroonga 0.9.3に関することはメーリングリストでのアナウンスを見てください。

rroonga 0.9.3ではWindows用のgemも提供するようにしました。このgemにはgroonga/rroongaのビルド済みのバイナリが含まれているのでビルド環境がないことが多いWindowsでも簡単に使えるようになっています。

さて、このgemですが、1つのgemでRuby 1.8.7にもRuby 1.9.1にも対応しています。そもそも、gemにはWindowsや32bit環境などのプラットフォームを指定することはできますが、Rubyのバージョンは指定することができません。そのため、Rubyのバージョン毎にgemを用意することはできません。用意する場合はgemのパッケージ名を"rroonga187"や"rroonga191"などと変えなければいけません。これはカッコワルイですね。

解決法は、1つのgemの中に1.8用のバイナリと1.9用のバイナリを両方いれ、実行時にどちらを使うかを切り替える、です。

まず、以下のようにバイナリを配置します*1

lib/1.8/groonga.so # <- Ruby 1.8.7のWindows用バイナリ
lib/1.9/groonga.so # <- Ruby 1.9.1のWindows用バイナリ

そして、groonga.soを読み込む部分を以下のようにします。

major, minor, micro, = RUBY_VERSION.split(/\./)
require "#{major}.#{minor}/groonga.so"

これで、適切なバイナリを読み込むことができます。

この他に、rroongaのように依存しているDLL(libgroonga.dll)がある場合はそのDLLがあるフォルダをPATHに入れなければいけない、などといった注意点がありますが、それはまたいつか機会があったら触れるかもしれません。

それでは、1.8.7でも1.9.1でも使えるWindows用バイナリが入ったgem*2をDebian GNU/Linux上のMinGWで作る方法を紹介します。

rake-compiler: Ruby 1.8.7と1.9.1をクロスコンパイル

まず、Windows用のRuby 1.8.7と1.9.1をMinGWでクロスコンパイルします。これにはrake-compilerが便利です。

% sudo gem install rake-compiler

まず、MinGWをインストールします。

% sudo aptitude install -y mingw32

それでは、Ruby 1.8.7-p249をビルドします。

% rake-compiler cross-ruby VERSION=1.8.7-p249 EXTS=--without-extensions

クロスコンパイルしたrubyはextconf.rbを実行してMakefileを作れればいいだけなので、拡張ライブラリなどはいりません。環境変数として「EXTS=--without-extensions」を指定すると拡張ライブラリはビルドされないのですが、もっとカッコイイ方法がありそうな気がします。

同様にRuby 1.9.1-p378もビルドします。

% rake-compiler cross-ruby VERSION=1.9.1-p378 EXTS=--without-extensions

ただ、これは失敗します。失敗したら以下のパッチを当てます*3

1
2
3
4
5
6
7
8
9
10
11
12
diff -ru ruby-1.9.1-p378.orig/win32/win32.c ruby-1.9.1-p378/win32/win32.c
--- ruby-1.9.1-p378.orig/win32/win32.c        2009-12-05 18:40:53.000000000 +0900
+++ ruby-1.9.1-p378/win32/win32.c        2010-04-20 23:10:13.000000000 +0900
@@ -4604,7 +4604,7 @@

     ret += written;
     if (written == len) {
-        (const char *)buf += len;
+        buf = (const char *)buf + len;
         if (size > 0)
             goto retry;
     }

このパッチは以下のように適用できます。

% cd ~/.rake-compiler/sources
% patch -p0 < /tmp/ruby-1.9.1-build-fix.diff

もう一度、同じコマンドでビルドすると成功します。

% rake-compiler cross-ruby VERSION=1.9.1-p378 EXTS=--without-extensions

Rake::ExtensionTask: gem用バイナリをクロスコンパイル

Windows用のRuby 1.8.7とRuby 1.9.1ができたので、これを利用してgem用のバイナリをクロスコンパイルします。これには、rake-compilerが提供するRake::ExtensionTaskが便利です。

Rake::ExtensionTaskの使い方を紹介しますが、ここでは、もうすでにRakefileがあり、その中でGem::Specificationを作っているものとします。

specがGem::Specificationだとすると、Rakefileに以下を追加することでcrossタスクが定義されます。

1
2
3
4
5
require 'rake/extensiontask'
Rake::ExtensionTask.new("groonga", spec) do |ext|
  ext.cross_compile = true
  ext.cross_platform = 'x86-mingw32'
end

Rake::ExtensionTask.newに"groonga"を指定していますが、このようなRakefileを使うときは、以下のようなファイル構成になっている必要があります。

./
+-- ext/
|    +-- groonga/
|        +-- extconf.rb
|        +-- rb-grn.c
|        +-- ...
+ Rakefile
...

ext/の下にRake::ExtensionTask.newで指定した名前と同じディレクトリを作り、その下にextconf.rbを置きます。

crossタスクを使って1.8.7用のバイナリと1.9.1用のバイナリをクロスコンパイルするには以下のようにします。

% rake cross compile RUBY_CC_VERSION=1.8.7:1.9.1

うまくいくとlib/1.8/groonga.soとlib/1.9/groonga.soができます。

これらを両方含んだgemを作るには以下のようにします。

% rake cross native gem RUBY_CC_VERSION=1.8.7:1.9.1

これでpkg/rroonga-0.9.3-x86-mingw32.gemができます。あとは、このgemをrubygems.orgにアップロードすれば完了です。

% gem push pkg/rroonga-0.9.3-x86-mingw32.gem

まとめ

Ruby 1.8.7/1.9.1のどちらでも使えるWindows用のgemをDebian GNU/Linux上で作成する方法を紹介しました。もし、拡張ライブラリをWindows上でも簡単に使えるようにしたいのであれば、Ruby 1.8.xと1.9.xの両方をサポートしてみてはいかがでしょうか。

この話とは関係ありませんが、Ruby Summer of Codeの学生の応募の締切りは今週の土曜日だそうです。(参考: [ruby-list:47029] [ANN] Ruby Summer of Code

Rubyとオープンソースに興味のある学生の方は応募してみてはいかがでしょうか。本家のSummer of CodeやRuby Summer of Codeはフリーソフトウェアの開発に関わるよい機会といえます。Rubyベストプラクティスの著者が開発しているPDF生成ライブラリのPrawnなど、いくつものRuby関連のフリーソフトウェアがSummer of Codeのおかげで開発が進んできました。(参考: Summer of Codeと須藤さんとSubversionのRuby bindings - 角谷HTML化計画(2005-10-26)

ぜひ、このような機会を活かして、フリーソフトウェアの開発に積極的に参加してみてください。

Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
Gregory Brown/高橋 征義/笹井 崇司
オライリージャパン
¥ 3,456

*1 Windowsでも拡張子は.soでいいのです。

*2 複数のバージョン向けのバイナリが入ったgemをfat gemというらしいです。

*3 この問題はtrunkではすでに修正されています。

タグ: Ruby
2010-04-21

るりまサーチ: Rubyでgroonga使ってリファレンスマニュアルを全文検索

先日、るりまの成果物であるRubyのリファレンスマニュアルを検索するWebアプリケーションるりまサーチを公開しました。

るりまサーチ

OpenSearchにも対応しているため、Firefoxの右上の検索窓から検索することもできます。

これまでも、るりまの成果物はBitClustを使ってWebブラウザから見ることができました*1。しかし、BitClustのWebインターフェイスは検索機能が弱く、目的の情報にたどり着くのが難しいと感じたことがあったのではないでしょうか。例えば、全文検索ができなかったり、そもそも検索がとても遅かったりしました。

るりまサーチでは全文検索エンジンとしてgroongaを利用することにより、高速な全文検索機能と使いやすい絞り込み機能を実現しています。それでは、るりまサーチの機能とその実装について簡単に紹介します。

機能

るりまサーチは多くの情報を絞り込んでいきながら目的の情報に到達することを意識したインターフェイスになっています。そのため、できるだけ簡単に絞り込んでいけるような機能を組み込んであります。

ここでは、絞り込みに関する機能を2点だけ紹介します。

ドリルダウン

groongaの得意な機能の1つはドリルダウンと呼ばれる、検索結果の中から特定の値をグループ化し、それぞれのグループのレコード処理を数える処理です。るりまサーチでもこの機能を利用して絞り込みやすいインターフェイスを提供しています。

ドリルダウン

るりまサーチではページ上部にそのときに絞り込める条件を表示します。例えば、トップページではマニュアルの種類によって絞り込めるリンクを表示しています。

このとき、事前に絞り込んだ後のレコード数も表示しています。この時点で絞り込み後のレコード数を数えているので、絞り込んだ後にレコード数がないリンクを表示しないことができます。つまり、「リンクを辿ったけど絞り込んだらマッチするレコードがない!」という状況を防ぐことができます。

便利に絞り込めるリンクを提供し、その一方で、無駄な絞り込みを行わずに済むようになっています。

条件解除

簡単に条件を絞り込めるようにするだけではなく、簡単に条件を解除することもできます。これは様々な絞り込みを行いながら目的の情報に辿りつけるようにするためです。

条件解除

ページ上部にはどのような条件で絞り込んでいったかが表示されるようになっています。それぞれの絞り込み条件は条件の横にあるリンク*2を辿るだけで簡単に解除することができます。

絞り込みすぎてしまったときは、これで条件を解除して違う条件で絞り込んでいくことができます。

実装

るりまサーチはRuby 1.9.1とRackとrroongaを用いて実装されています。rroongaはgroongaをRubyから利用するためのRubyバインディングです。るりまサーチではgroongaをサーバとしてではなく、ライブラリとして利用しています。

rroongaはgroongaの高速な機能を活かしたまま、より使いやすいRubyらしいAPIを提供しています。るりまサーチはそんなrroongaを使って、すっきりとした記述で実現されています。ここでは、rroongaを使ったコードを2つ紹介します。

スキーマ定義

groongaはRDBと同じようにデータの格納場所毎に型を持っています。groongaにデータを格納する前に格納場所を用意する必要があります。

rroongaでは格納場所の定義(スキーマ)をより宣言的に記述するためのAPIを用意しています。以下は検索対象の情報を保存する「Entries」テーブルの定義です。RDBなどのスキーマを見たことがあるなら、この定義からgroongaがどのようなデータを格納できるようになるかを想像できるのではないでしょうか。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Groonga::Schema.define do |schema|
  schema.create_table("Entries",
                      :type => :hash,
                      :key_type => "ShortText") do |table|
    table.short_text("name")
    table.short_text("local_name")
    table.short_text("label")
    table.text("document")
    table.text("signature")
    table.text("description")
    table.reference("type", "Types")
    table.reference("class", "Classes")
    table.reference("module", "Modules")
    table.reference("object", "Objects")
    table.reference("version", "Versions")
    table.reference("visibility", "Visibilities")
  end
end

このように、Rubyでは宣言的に処理を記述することがわりとよく行われます。これは、内部DSLとも呼ばれ、やりすぎる人も出るほどです。例えば、上記のような記述を以下のようにすることもできますが、これは少しやりすぎではないかと感じます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Groonga::Schema.define do
  create_table("Entries",
               :type => :hash,
               :key_type => "ShortText") do
    short_text :name
    short_text :local_name
    short_text :label
    text       :document
    text       :signature
    text       :description
    reference  :type, :Types
    reference  :class, :Classes
    reference  :module, :Modules
    reference  :object, :Objects
    reference  :version, :Versions
    reference  :visibility, :Visibilities
  end
end
検索条件

rroongaでは検索条件をクエリ文字列ではなく、Rubyの式として記述することができます。

例えば、「name」カラムの値が「Regexp」であるレコードを検索するときは以下のようになります。

1
2
3
entries.select do |record|
  record.name == "Regexp"
end

「name」カラムの値が「Regexp」あるいは「description」カラムに「正規表現」が含まれているレコードを検索するときは以下のようになります。

1
2
3
entries.select do |record|
  (record.name == "Regexp") | (record.description =~ "正規表現")
end

「name」カラムか「description」カラムに「encoding」を含むレコードを検索するときは以下のようになります。ただし、「name」カラムにマッチした場合はスコアをあげて、より上位に表示するようにします。

1
2
3
4
5
6
7
entries.select do |record|
  target = record.match_target do |match_record|
    (match_record.name * 100) |
    (match_record.description)
  end
  target =~ "encoding"
end

DataMapperSequelなど文字列ではなくRubyの式で条件を指定できるようにするORマッパーはいくつかありますが、最終的にそれらはSQLになります。しかし、rroongaの場合はRubyで書いた式がそのままgroongaのネイティブな条件式になります。カッコいいですね。

また、ORマッパーも少しやりすぎてしまう傾向がある分野ですが、rroongaはやりすぎることなく、Rubyらしさを保ったまま条件式を指定できているのではないでしょうか。少しやりすぎてしまうと、Symbolにメソッドを追加してしまったりします。

まとめ

Rubyのリファレンスマニュアルを検索するWebアプリケーション「るりまサーチ」の機能と実装を簡単に紹介しました。

るりまサーチを使うことでるりまプロジェクトの成果物であるRubyのリファレンスマニュアルをより便利に活用することができます。

また、るりまサーチの実装はgroongaのRubyバインディングであるrroongaのよいサンプルでもあります。groongaをRubyから利用しようと考えていた方はGitHub上にある、るりまサーチのソースコードを読んでみるとよいでしょう。ライセンスはLGPLv3+です。

るりまサーチはSinatraなどのフレームワークを使わずに、直接Rackを使っています。そのような場合にどのようにテスト環境を構築するか、というのもいつか紹介できるとよいですね。今、興味のある人はソースコードを見てください。

このように、るりまサーチにはまだおもしろいところが色々あるのですが、今回はこのへんにしておきます。

*1 自分で設定するのが面倒な場合はokkezさんが公開しているBitClustを利用することもできます。

*2 画像にしたいですね。

タグ: Ruby

この記事の続き

2010-04-27

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|