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

ククログ


Debianでパッケージをリリースできるようにしたい - mentors.debian.netの使いかた

はじめに

以前、オープンソースのカラムストア機能付き全文検索エンジンであるGroongaをDebianに入れるために必要な作業について、最初のとっかかりであるWNPPへのバグ登録やDebianらしいパッケージかどうかチェックするためのLintianについて紹介記事を書きました。

今回は、現在進行中であるGroongaのDebianリポジトリ入りを目指す作業の中から、Debianパッケージのアップロード先となるmentors.debian.netの使いかたについて紹介します。

mentors.debian.netとは

WNPPにバグ登録をしたのち、debパッケージを用意するところまでできたら、次はパッケージを公開します。これはスポンサーに作ったパッケージをレビューしてもらう必要があるためです。

Groongaの場合には、Debian開発者であるやまねさんにスポンサーしてもらえることになっていたので、スポンサー探しに苦労することはありませんでした。

パッケージのレビューをしてもらうときに、パッケージのアップロード先として利用するのがmentors.debian.netです。パッケージをアップロードするには次のような手順を踏みます。

  • mentors.debian.netにアカウントを作成する(初回のみ)
  • アップロードに必要な設定をする(初回のみ)
  • パッケージをmentors.debian.netにアップロードする

mentors.debian.netにアカウントを作成する(初回のみ)

まずはmentors.debian.netにアカウントを作成します。そして、パッケージの署名に使うGPGの鍵を登録します。登録する際は次のコマンドを実行した結果をアカウント詳細ページにてアップロードします。

$ gpg --export --export-options export-minimal --armor (鍵のID)

アカウントの登録が済んだら、次はアップロードに必要な初期設定をします。

アップロードに必要な設定をする(初回のみ)

パッケージをmentors.debian.netへとアップロードするにはdputを使います。アップロード先の設定は、~/.dput.cfに行います。

[mentors]
fqdn = mentors.debian.net
incoming = /upload
method = http
allow_unsigned_uploads = 0
progress_indicator = 2
# Allow uploads for UNRELEASED packages
allowed_distributions = .*

これで、パッケージをアップロードするための準備が整いました。次はパッケージをアップロードします。

パッケージをmentors.debian.netにアップロードする

.dput.cfの設定ができたらパッケージをアップロードします。その際には、引数としてアップロード先であるmentorsと.changesファイルを指定します。

$ dput mentors ../groonga_4.0.0-1_amd64.changes
Checking signature on .changes
gpg: Signature made Mon 10 Feb 2014 08:59:20 AM UTC using RSA key ID 3455D448
gpg: Good signature from "HAYASHI Kentaro <hayashi@clear-code.com>"
Good signature on ../groonga_4.0.0-1_amd64.changes.
Checking signature on .dsc
gpg: Signature made Mon 10 Feb 2014 08:59:13 AM UTC using RSA key ID 3455D448
gpg: Good signature from "HAYASHI Kentaro <hayashi@clear-code.com>"
Good signature on ../groonga_4.0.0-1.dsc.
Uploading to mentors (via ftp to mentors.debian.net):
  Uploading groonga_4.0.0-1.dsc: done.
  Uploading groonga_4.0.0.orig.tar.gz: done.
  Uploading groonga_4.0.0-1.debian.tar.xz: done.
  Uploading groonga_4.0.0-1_amd64.deb: done.
  Uploading groonga-server-common_4.0.0-1_amd64.deb: done.
  Uploading groonga-server-http_4.0.0-1_amd64.deb: done.
  Uploading groonga-server-gqtp_4.0.0-1_amd64.deb: done.
  Uploading libgroonga-dev_4.0.0-1_amd64.deb: done.
  Uploading libgroonga0_4.0.0-1_amd64.deb: done.
  Uploading groonga-tokenizer-mecab_4.0.0-1_amd64.deb: done.
  Uploading groonga-plugin-suggest_4.0.0-1_amd64.deb: done.
  Uploading groonga-bin_4.0.0-1_amd64.deb: done.
  Uploading groonga-httpd_4.0.0-1_amd64.deb: done.
  Uploading groonga-doc_4.0.0-1_all.deb: done.
  Uploading groonga-examples_4.0.0-1_all.deb: done.
  Uploading groonga-munin-plugins_4.0.0-1_amd64.deb: done.
  Uploading groonga_4.0.0-1_amd64.changes: done.
Successfully uploaded packages.

アップロードに成功してしばらくすると、パッケージごとの個別ページが参照できます。パッケージのページではLintianによるチェック結果を参照できます。 具体的な結果については Groongaの例 が参考になるでしょう。

間違ったときの対処について

もしdputしたときに、アップロード先としてmentorsの指定を忘れるとどうなるでしょうか。そのときはdputのデフォルトのアップロード先にアップロードされてしまいます。/etc/dput.cfの[DEFAULT]欄が何も指定していなかったときのアップロード先です。

その場合には、dcutコマンドを使って取り消します。dcutコマンドを使うと削除に必要なコマンドファイルをアップロードします。アップロード先では、定期的にコマンドファイルを処理しているので、しばらくするとコマンドファイルに従ってファイルを削除してくれます。

例えば、間違ってデフォルトの設定でアップロードしてしまった場合、次のようにしてdcutで取り消します*1

$ dcut -m hayashi@clear-code.com -i groonga_4.0.0-1_amd64.changes
 signfile /tmp/dcut.jNuDdW/dcut.hayashi_clear_code_com.1392865192.3377.commands hayashi@clear-code.com

You need a passphrase to unlock the secret key for
user: "HAYASHI Kentaro <hayashi@clear-code.com>"
4096-bit RSA key, ID 3455D448, created 2012-04-24

gpg: gpg-agent is not available in this session

Successfully signed commands file
  Uploading dcut.hayashi_clear_code_com.1392865192.3377.commands: done.

ただし、上記ができるのは、間違ってアップロードしてしまったときの.changesが残っている場合です。うっかり.changesを削除してしまった場合には使えません。デフォルトのアップロード先がDebian公式のUploadQueueだったため、このミスをしたときはftp-masterに削除を依頼する必要がありました。

まとめ

今回はdebパッケージをスポンサーにレビューしてもらうときに利用するmentors.debian.netの使いかたを紹介しました。パッケージをmentors.debian.netにアップロードしたら、スポンサーにレビューしてもらう必要があります。スポンサーによる指摘事項があれば都度修正して、きちんとしたパッケージに仕上げていく作業が必要です。それらについては、またの機会に記事にします。

*1 -mオプションにはGPGの鍵にひもづいたメールアドレスを指定します。

2014-07-02

SEゼミ - リーダブルコード勉強会(2回目)を開催

2014/07/06にプログラミングが好きな学生のためのリーダブルコード勉強会の2回目を開催しました。内容を知りたい方は資料やこれまでの記事を読んでください。

今回も9割以上の参加者が満足してくれたようです*1。よかったです。

今後の予定

これで今年のSEゼミでのリーダブルコード勉強会は終了です。今後もあるかどうかはわかりません*2。しかし、今回の勉強会の資料はCC BY-SA 4.0*3のライセンスで自由に利用できる*4ので、内容に興味がある人は自由に資料を使って自分たちで開催してみてください。

自分たちで開催するときのヒントになるように、実際に開催してわかったことや工夫したことを説明します。参考にしてください。

わかったこと

わかったことは次の通りです。

  • コードチェンジは「コードを読まざるを得ない状況」を作るには有効
  • 進み具合が大きく離れている人同士でコードチェンジしなければいけない時*5はフォローが必要
  • 課題の内容は今回の勉強会に向いていた
    • 課題で工夫した点は後述

工夫したこと

工夫したことは次の通りです。

  • ランチの時間はメンター・スポンサーと学生さんがゆっくり話せるようにする
    • せっかく現役のエンジニア・人事の人と話せる機会なので活用して欲しかった
  • グループでよいコードをまとめる方法はやり方を提示する
    • グループ・ディスカッションで必要になるようなスキルは今回の勉強会では重要ではないので手順からグループで考えてもらうことはやめた
  • 課題の技術的な難易度を下げる

課題について補足します。

課題は徐々に改良していく課題になることを重視しました。リーダブルコードの必要性を感じることが多いのは既存のコードを改良していくフェーズだからです。既存のコードを改良していくためには既存のコードを読まなければいけません。このときにリーダブルコードかどうかが効いてきます。

そのため、技術的な難易度は下げました。具体的にいうと、言語の基本的な機能だけを使えば十分な課題にしました。例えば、データは文字列だけにして、さらに文字列操作はなるべくしなくても済むようにしました。C言語で動的にメモリーを割り当てないで済むように、文字列長の最大サイズなども決めました。引数がとても多い実行例になっているのも同じ理由からです*6

技術的な難易度は下げたのですが、「プログラムの抽象度」を上げていかないと読みにくいプログラムになるような内容になっています。もう少し言うと、「1個だけ扱っていたものをn個扱うようになる」というのを繰り返すようになっています。例えば次の通りです。

  • レシピ名を1つだけ扱えばよかったものがレシピ名を3つ扱わなければいけなくなる。
  • レシピの属性は名前だけだったのが、IDとURLも必要になる。
  • 1ユーザーだけ扱えばよかったものが4ユーザー扱わなければいけなくなる。

プログラムの抽象度を上げていくには既存のコードを改良しないといけません。そうするとコードを読む必要がでてきて、リーダブルコードを意識せざるを得ない、ということを狙っています。

課題をやってみた人は気づいていましたか?

まとめ

  • リーダブルコード勉強会を2回実施した
  • 2回とも好評だった
  • 勉強会用の資料は公開している
  • 資料はCC BY-SA 4.0のライセンスで自由に利用できる
  • コードチェンジは「コードを読まざるを得ない状況」を作るには有効
  • 課題の技術的な難易度は低めにした
  • 課題は、既存のコードを読まないとリーダブルなコードを書くのが難しくなるようにした

主催のSEプラスさん、スポンサーの楽天さん、クックパッドさん、DeNAさん、メンターの川原さん、たなべさん、参加した学生のみなさん、ありがとうございました。いい経験になりました。参加者のみなさんがこれからリーダブルコード力を上げる助けになれたならやった甲斐があるというものです。

勉強会の内容を考え、実際にやってみた側としては、メンターの川原さんとたなべさんが「楽しかった」と言ってくれて非常にうれしかったです。

おまけ:関連URL紹介

勉強会中で出てきた話題に関連するURLを紹介します。

*1 アンケート結果より。「大変不満だった」「不満だった」「ふつう」「満足した」「大変満足した」の5段階評価で回答してもらいました。前回は「満足した」の人数が一番多かったのが、今回は「大変満足した」の人数が一番多かったことに驚きました。

*2 企業研修などでやってみたいという方はお問い合わせください。

*3 著作者は「株式会社クリアコード」。

*4 著作者を表示すればコピーしたり再配布したりできる。商用利用もできる。変更後も同じライセンスにするなら変更してもよい。

*5 たとえば、参加者数が少なくてチェンジできる選択肢が少ない時。

*6 「例」なので、実行例通りに実装しなくてもよいのですが、多くの人は実行例通り実装していたようです。

*7 横山さんはメンターでも参加者でもありません。Web上に公開されている情報を見て趣味で実装したものです。たぶん。

2014-07-07

Groongaユーザ向けの、はじめてのDroonga

昨年11月29日に開催したイベント「全文検索エンジンGroongaを囲む夕べ 4」 において、Groonga族の新たな一員としてDroongaが加わった事をお知らせしました。その後現在に至るまでにリリースを着実に重ね、現在はバージョン1.0.4となっているのですが、その間に方針や開発の優先度が変わってきている事についてのきちんとしたアナウンスができておらず、今改めてDroongaの事を知ろうとした時に「で、結局これって何なのよ?」ということが分かりにくくなってしまっています。

この記事は、そんなDroongaの現時点での情報を一旦整理して、特にGroongaを実運用されている方にとって「Droongaって何なの? どう便利になるの?」ということが一目で分かるようにする物です。Droongaの現状まとめとしてご参照下さい。

GroongaとDroongaの違い

Droongaは端的に言うと、「Distributedな(分散型の)Groonga」ということになります。詳しくご説明しましょう。

Groongaは全文検索エンジンであり、カラム指向のデータベースでもあります。アプリケーション組み込みのライブラリやHTTPサーバとして動作して、ユーザのリクエストに応じてデータの読み書きと検索を処理することができます。

簡単に利用し始められるのが魅力のGroongaですが、大規模なサービスのバックエンドとしてGroongaを安定運用するには若干の工夫が必要です。具体的には、Groonga自体にはレプリケーションなどの機能は含まれないため、耐障害性を高めるためやアクセス数の増加に対応するためなどの目的でデータベースを冗長化するには、複数のGroongaサーバを何らかの方法で協調動作させる必要があります。

Droongaは現在の所、Groongaの上記の問題点が解決された移行先となることを目指して開発が進められています。そのため、現時点で既に以下のような特徴を備えています。

Groongaとの互換性

Droongaは、GroongaのHTTPインターフェースと互換性があります。そのため、フロントエンドとなるWebアプリケーションの改修の必要はありません。

ただし、現時点では以下の制限事項があります。

  • 主要なコマンドのみに対応しており、未対応のコマンドがあります。(コマンドリファレンスに、現時点で対応しているGroonga互換コマンドの一覧があります)
  • 特に、サジェスト関係の機能には全く対応していません。
  • Groonga固有の通信プロトコルであるgqtpには対応していません。

これらの未対応項目については、将来的には互換性を改善していく予定です。とはいえ、現在Groongaを運用している中で使っている機能がDroongaが対応している機能の範囲内に収まっているのであれば、すぐにでもそのままDroongaへ移行することができます。GroongaからDroongaへの移行を検討する際には、まずこの点をチェックしてみて下さい。

レプリケーションへの対応

Droongaは複数ノードによるクラスタとして動作しますので、もちろん、基本機能としてレプリケーションにも対応しています。レプリケーション数を増やすことで、耐障害性の高い運用体制をとれます。

また、ノードの追加・削除を簡単に行えるため、急なアクセス数の増減にも対応しやすいという利点もあります。アクセス数の増大に応じてノードを追加することにより、安定したスループットを維持できます。

目的が変わった?

ここまでの説明を見て、「発表当初のDroongaと目的が変わっているのではないか?」と思われた方もいるかもしれません。この点についても説明しておきましょう。

発表当初、Droongaは「内部的にGroongaを使用した、汎用の分散データ処理システム」という位置付けで、Groongaとの互換性についてはそれほど重視はしていませんでした。もちろん、汎用システムの一つの応用形態としてGroonga互換の検索システムとしても利用できるようになるということは想定していましたが、そのための作業の優先度はそれほど高くはありませんでした。それよりも、汎用システムとしての基盤部分を固めていくことを優先しようというのが、当初の目標設定でした。

しかし、実際に開発を進めていくうちに、ノード構成の管理やレプリケーションなどの基盤部分の開発を進めていく上では、具体的な利用シーンを設定した方がそれらの開発を進めやすい(たくさんあるやるべき事の中から「まずここから実装していった方がよい」という判断を行いやすい)という事に気がついてきました。また、実運用に即した機能が早めに出揃うことで、実運用を開始してみないと気がつかない種類の不具合を見つけやすくなるというメリットもあります。

そこで、Droongaプロジェクトでは当面のところ、「Groonga互換の検索システム」を構築するという場面を想定して、開発やドキュメントの整備を進めることにしました。「汎用の分散型データ処理システム」としての開発自体を諦めたというわけでは決してありませんので、その点についてはご安心下さい。

Droongaを試してみよう

以上、Droongaの現状について簡単に紹介しました。ここからは、実際にDroongaを利用するための手順を解説します。DroongaがGroonga互換の検索システムとして動作することを、まずはお手元の環境で実際に試してみて下さい。

ここでの手順は、大まかに言って以下の通りです。

  1. Groongaをバックエンドに使った簡単なWebアプリケーションを用意する。
  2. Droongaクラスタを構築する。
  3. Groongaのデータベースの内容をDroongaクラスタに引き継ぐ
  4. Webアプリケーションのバックエンドを、GroongaからDroongaクラスタに置き換える。

Droongaクラスタは、レプリケーション機能を利用するためにサーバを2台使うことにします。以下の説明では、サーバとしてUbuntu 14.04LTSがインストールされたサーバが2台あり、それぞれのIPアドレスが192.168.0.10と192.168.0.11であると仮定します。IPアドレスなどは、お手元の環境に合わせて説明を適宜読み替えて下さい。

1. Groongaを使ったWebアプリケーションの用意

まず、Groongaをバックエンドに使って、TODOリストの作成と検索を行う簡単なWebアプリケーションを作成してみましょう。Groongaは複数台のサーバでの動作に対応していませんので、ここでは192.168.0.10の1台だけを使うことにします。

最初に、インストール手順の説明に従ってGroongaをインストールします。ここではPPAを使ってインストールする例を示します。

% sudo apt-get -y install software-properties-common
% sudo add-apt-repository -y universe
% sudo add-apt-repository -y ppa:groonga/ppa
% sudo apt-get update
% sudo apt-get -y install groonga

次に、データベースを作成します。 ~/groonga/db/ 以下にデータベースを置くことにします。

% mkdir -p $HOME/groonga/db/
% groonga -n $HOME/groonga/db/db quit

各テーブルも定義します。

% groonga $HOME/groonga/db/db table_create --name Topic --flags TABLE_PAT_KEY --key_type ShortText
% groonga $HOME/groonga/db/db column_create --table Topic --name title --flags COLUMN_SCALAR --type ShortText
% groonga $HOME/groonga/db/db table_create --name Term --flags TABLE_PAT_KEY --key_type ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
% groonga $HOME/groonga/db/db column_create --table Term --name topic_title --flags "COLUMN_INDEX|WITH_POSITION" --type Topic --source title

データベースができたら、GroongaをHTTPサーバとして起動します。

% groonga -p 10041 -d --protocol http $HOME/groonga/db/db

これでバックエンドの準備ができました。続いて、フロントエンドとなるWebアプリケーションを作成します。説明を簡単にするため、アプリケーションの機能はWebページ中に埋め込んだJavaScriptだけで実装することにします。

まずWebページを作成します。

% mkdir ~/groonga/public/
% vi ~/groonga/public/index.html

Webページの内容は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<meta charset="UTF-8">
<title>TODO List</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p><input type="text" size="30" id="title-field"><button id="add-button">登録</button></p>
<p><input type="text" size="20" id="search-field">
   <button id="search-button">検索</button></p>
<div id="result"></div>
<script type="text/javascript"><!--
  var base = 'http://' + location.hostname + ':10041';

  // レコードを追加する。
  $('#add-button').click(function() {
    var title = $('#title-field').val();
    if (!title)
      return;
    $.ajax({
      url:      base + '/d/load',
      data:     {
        table:  'Topic',
        values: JSON.stringify([{
          _key:  title,
          title: title
        }])
      },
      dataType: 'jsonp',
      success:  function() {
        $('#title-field').val('');
      }
    });
  });

  // レコードを検索する。
  $('#search-button').click(function() {
    var data = {
      table:          'Topic',
      output_columns: 'title',
      limit:          10
    };
    var term = $('#search-field').val();
    if (term)
      data.filter = 'title @ ' + JSON.stringify(term);
    $.ajax({
      url:      base + '/d/select',
      data:     data,
      dataType: 'jsonp',
      success:  function(result) {
        if (!result)
          return;
        var body = result[1];
        var searchResult = body[0];
        var count = searchResult[0][0];
        var topics = searchResult.slice(2);
        $('#result')
          .empty()
          .append($('<p>').text(count + '件見つかりました。'))
          .append($('<ul>')
                    .append(topics.map(function(topic) {
                      var title = topic[0];
                      return $('<li>').text(title);
                    })));
      }
    });
  });
// --></script>

Webページの準備ができたら、Webサーバを起動します。設定ファイルなどを用意しなくてもよく簡単に起動できるため、Rubyの標準機能を使ったWebサーバを使います。

% ruby -run -e httpd -- --port 8080 groonga/public &

Webブラウザを起動し、早速Webアプリケーションにアクセスしてみましょう。URLは 「http://192.168.0.10:8080/index.html」です。

(サンプルWebアプリケーションを開いた所のスクリーンショット)

試しにTODOタスクを追加してみましょう。「追加」ボタンの左の入力欄に「バナナを買う」と入力して「追加」ボタンをクリックします。すると、レコードが追加されて入力欄が空になります。続けて「リンゴを買う」「牛乳を買う」も追加しましょう。

(入力中の様子のスクリーンショット)

タスクの追加が終わったら、検索してみましょう。まずは、何も入力せずに「検索」ボタンをクリックします。すると、登録されているすべてのレコードのうち先頭10件が表示されます。(現在は3件しかレコードがないので、3件すべてのレコードが表示されます。)

(すべてのレコードが表示されたスクリーンショット)

続いて、全文検索もしてみましょう。「検索」ボタンの左の入力欄に「牛乳」と入力してから「検索」ボタンをクリックします。すると、登録されているレコードのうち、内容に「牛乳」を含んでいるレコードの先頭10件が表示されます。(現在は1件しか該当するレコードがないので、レコードが1件だけ表示されます。)

(検索結果のスクリーンショット)

ということで、無事にTODOリストの管理アプリケーションが実装されました。

2. Droongaクラスタの構築

それでは、先のGroongaと互換性があるDroongaクラスタを構築してみましょう。

192.168.0.10と192.168.0.11の両方に、Droongaを構成するパッケージをインストールします。詳細はチュートリアルを参照してください。

(on 192.168.0.10, 192.168.0.11)
% sudo apt-get update
% sudo apt-get -y upgrade
% sudo apt-get install -y libgroonga-dev ruby ruby-dev build-essential nodejs nodejs-legacy npm
% sudo gem install droonga-engine
% sudo npm install -g droonga-http-server
% mkdir ~/droonga

パッケージのインストールが完了したら、catalog.jsonを作成します。これは、Droongaクラスタのノード構成の設定が書かれたファイルです。droonga-engineパッケージに含まれているdroonga-engine-catalog-generateコマンドを使って簡単に作成することができます。 --hostsオプションには、クラスタを構成するすべてのノードのIPアドレス(またはホスト名)をカンマ区切りで指定します。この操作も、2台のサーバ両方で行ってください。

(on 192.168.0.10, 192.168.0.11)
% droonga-engine-catalog-generate --hosts=192.168.0.10,192.168.0.11 --output=~/droonga/catalog.json

catalog.jsonができたら、それぞれのサーバ上でDroongaのサービスを起動します。Droongaはdroonga-engineとdroonga-http-serverという2つのサービスに別れており、それぞれ個別に起動する必要があります。以下に、192.168.0.10で実行するコマンドを示します。

(on 192.168.0.10)
% host=192.168.0.10
% export DROONGA_BASE_DIR=$HOME/droonga
% droonga-engine --host=$host \
             --log-file=$DROONGA_BASE_DIR/droonga-engine.log \
             --daemon \
             --pid-file=$DROONGA_BASE_DIR/droonga-engine.pid
% env NODE_ENV=production \
    droonga-http-server --port=10042 \
                    --receive-host-name=$host \
                    --droonga-engine-host-name=$host \
                    --daemon \
                    --pid-file=$DROONGA_BASE_DIR/droonga-http-server.pid

起動オプションでそのノード自身のIPアドレスを指定していることにも注意して下さい。192.168.0.11では、起動オプションに含めるホストのIPアドレスを変える必要があります。

(on 192.168.0.11)
% host=192.168.0.11
% export DROONGA_BASE_DIR=$HOME/droonga
% droonga-engine --host=$host \
             --log-file=$DROONGA_BASE_DIR/droonga-engine.log \
             --daemon \
             --pid-file=$DROONGA_BASE_DIR/droonga-engine.pid
% env NODE_ENV=production \
    droonga-http-server --port=10042 \
                    --receive-host-name=$host \
                    --droonga-engine-host-name=$host \
                    --daemon \
                    --pid-file=$DROONGA_BASE_DIR/droonga-http-server.pid

これで、Droongaクラスタが動作し始めました。DroongaのHTTP APIにアクセスしてみて以下のような結果を得られれば、Droongaクラスタは正常に動作しています。

% curl "http://192.168.0.10:10042/droonga/system/status"
{
  "nodes": {
    "192.168.0.10:10031/droonga": {
      "live": true
    },
    "192.168.0.11:10031/droonga": {
      "live": true
    }
  }
}
3. データの引き継ぎ

Droongaクラスタを構築できたので、次は、Groongaデータベース内に定義済みのテーブルや投入済みのデータをDroongaクラスタに引き継ぎます。

まず、データの引き継ぎに必要なツールをインストールします。Groongaのダンプ形式をDroongaのリクエスト形式に変換するコマンドgrn2drnを含む同名パッケージと、DroongaのリクエストをDroongaクラスタに送信するコマンドであるdroonga-requestを含んでいるdroonga-clientパッケージの、2つのGemパッケージをインストールしましょう。

% sudo gem install grn2drn droonga-client

ツールがインストールされたら、Groongaのデータベースの内容をダンプ出力し、Droongaクラスタへ流し込みます。

% grndump ~/groonga/db/db | grn2drn | droonga-request --host=192.168.0.10

以上で、データの引き継ぎは完了です。ダンプ出力を通じて、テーブルの定義も含めてGroongaのデータベース内のすべての情報がDroongaクラスタに引き継がれました。

4. WebアプリケーションのバックエンドをGroongaからDroongaに切り替える

それではいよいよ、WebアプリケーションのバックエンドをDroongaに切り替えてみましょう。~/groonga/public/index.html でエンドポイントとして参照しているGroongaのHTTPサーバの接続先を、以下のようにしてDroongaの物に書き換えて下さい。

1
2
-  var base = 'http://' + location.hostname + ':10041';
+  var base = 'http://' + location.hostname + ':10042';

ファイルを編集したら、Webアプリケーションのページ( http://192.168.0.10:8080/index.html )をブラウザ上で再読み込みします。

これで、WebアプリケーションのバックエンドがGroongaからDroongaに切り替わりました。

それでは動作を試してみましょう。まずはデータが正常に引き継がれたことを確認するために、何も入力せずに「検索」ボタンをクリックします。すると、Groongaのデータベースから引き継いだレコード3つが検索結果として表示されます。

(すべてのレコードが表示されたスクリーンショット)

次はレコードの追加です。「追加」ボタンの左の入力欄に「ぶどうを買う」「パイナップルを買う」「コーヒー牛乳を買う」とそれぞれ入力し、レコードを追加して下さい。

(入力中の様子のスクリーンショット)

タスクの追加が終わったら、検索してみましょう。何も入力せずに「検索」ボタンをクリックすると、登録されているすべてのレコードのうち先頭10件が表示されます。ここでは、Groongaのデータベースから引き継いだレコード3つと、今追加したレコード3つを合わせて、計6つのレコードが表示されます。

(すべてのレコードが表示されたスクリーンショット)

続いて、全文検索もしてみましょう。「検索」ボタンの左の入力欄に「牛乳」と入力してから「検索」ボタンをクリックすると、登録されているレコードのうち、内容に「牛乳」を含んでいるレコードの先頭10件が表示されます。ここでは、Groongaのデータベースから引き継いだレコード1つと、今追加したレコードのうちの1つの、計2つのレコードが検索結果として表示されます。

(検索結果のスクリーンショット)

ということで、以上の一連の操作を通じて、DroongaのHTTPインターフェースはGroongaと互換性があるという事と、GroongaからDroongaへはデータを容易に引き継げるという事を確認できました。

なお、この状態ですでに2台構成のレプリケーションが実現されているため、仮にDroongaノードの片方が停止してもWebアプリケーションは正常に動作し続けますし、アクセス数が増大した場合でも2台のノードで処理を分担することができます。

まとめとイベントの告知

以上、Droongaの概要の紹介と、Droongaクラスタを構築してGroongaサーバをDroongaクラスタに移行する手順を簡単に解説しました。Droongaを少しでも身近に感じていただけたら、また、Groongaからの移行先としてDroongaを検討していただけたら幸いです。

最後に、イベントの告知もしておきます。

この記事で述べたような情報に加えて、Droongaの現状や今後の展望についてより詳細な紹介を行うイベント「Droonga Meetup」を7月30日水曜日 夜8時から開催します(参加無料)。 質疑応答の時間を長めに取る予定ですので、Groongaユーザの方々の生の声をぜひお聞かせ下さい。 イベントの詳細情報やお申し込みは、Doorkeeperのイベント案内ページをご覧下さい。

タグ: Groonga
2014-07-11

GStreamerのエレメントをつないでパイプラインを組み立てるには

はじめに

オープンソースのマルチメディアフレームワークとしてGStreamerがあります。音声・動画の再生、フォーマットの変換、録音・録画など基本的なことはもちろん、RTSPなどを用いたネットワーク通信を行うこともできます。

以前、あしたのオープンソース研究所: GStreamerという記事でGStreamerの概要や仕組みについて紹介しました。 今回はGStreamerで音声・動画の再生等を複数のエレメントを組合せて使うときに必要なパイプラインの組み立てかたについて紹介します。

GStreamerの仕組み

GStreamerの仕組み

あしたのオープンソース研究所: GStreamerでも触れたように、GStreamerの概要を理解するために大事な概念は以下の4つです*1

  • エレメント
  • リンク
  • パッド
  • パイプライン(ビン)

ただ、GStreamerで動画を再生するだけならplaybinを使うことで上記の概念を意識することなく簡単に再生できます。その際は、GStreamerに付属しているgst-launch-1.0を使います。Ubuntuのexample-contentパッケージにOggのサンプル動画が含まれているのでそれを再生してみましょう。次のコマンドを実行するとOgg動画を再生できます。

% gst-launch-1.0 playbin uri="file:///usr/share/example-content/Ubuntu_Free_Culture_Showcase/How fast.ogg"

playbinを使う場合には、動画を引数とするだけで簡単に再生できることがわかりました。では、個々のエレメントを組みあわせて動画再生したいときにはどのようにすればいいのでしょうか。

エレメントをつないでパイプラインを組み立てる

基本的には次の手順でエレメントをつないでパイプラインを組み立てることができます。

  • mediainfoで動画の情報を確認する
  • 動画フォーマットに対応したdemuxerを調べる
  • demuxerに対応するdecoderを調べる
  • decoderに対応するsinkを調べる
mediainfoで動画の情報を確認する

まずは、動画の情報を確認しましょう。mediainfoコマンドを使うと動画の情報を知ることができます。

% mediainfo /usr/share/example-content/Ubuntu_Free_Culture_Showcase/How\ fast.ogg
General
Complete name                            : /usr/share/example-content/Ubuntu_Free_Culture_Showcase/How fast.ogg
Format                                   : OGG
File size                                : 1.83 MiB
Duration                                 : 30s 584ms
Overall bit rate mode                    : Variable
Overall bit rate                         : 501 Kbps
Writing application                      : VLC media player

Video
ID                                       : 314165387 (0x12B9C88B)
Format                                   : Theora
Duration                                 : 30s 597ms
Bit rate                                 : 344 Kbps
Nominal bit rate                         : 800 Kbps
Width                                    : 1 280 pixels
Height                                   : 720 pixels
Display aspect ratio                     : 16:9
Frame rate                               : 29.970 fps
Compression mode                         : Lossy
Bits/(Pixel*Frame)                       : 0.012
Stream size                              : 1.26 MiB (69%)
Writing library                          : Xiph.Org libtheora 1.1 20090822 (Thusnelda)

Audio
ID                                       : 314165386 (0x12B9C88A)
Format                                   : Vorbis
Format settings, Floor                   : 1
Duration                                 : 30s 584ms
Bit rate mode                            : Variable
Bit rate                                 : 128 Kbps
Channel(s)                               : 2 channels
Sampling rate                            : 44.1 KHz
Compression mode                         : Lossy
Stream size                              : 478 KiB (26%)
Writing library                          : libVorbis (Everywhere) (20100325 (Everywhere))

Ogg動画であることと、映像のフォーマットがTheora、音声のコーデックはVorbisであることがわかります。

動画フォーマットに対応したdemuxerを調べる

動画の音声と映像を両方というのはひとまずおいて、映像だけ再生することをまず考えてみましょう。エレメントの詳細を調べるのには、GStreamerに付属しているgst-inspect-1.0を使います。

まずは、Oggに関係のあるエレメントをgst-inspect-1.0を使って探してみます。動画から映像を抜き出すには通常demuxerを使います。なので、oggdemuxというエレメントを使うとよさそうです。

% gst-inspect-1.0 | grep ogg
typefindfunctions: application/x-id3v2: mp3, mp2, mp1, mpga, ogg, flac, tta
typefindfunctions: application/x-id3v1: mp3, mp2, mp1, mpga, ogg, flac, tta
typefindfunctions: application/ogg: ogg, oga, ogv, ogm, ogx, spx, anx, axa, axv
typefindfunctions: application/x-ogg-skeleton: no extensions
libav:  avmux_ogg: libav Ogg muxer (not recommended, use oggmux instead)
ogg:  oggdemux: Ogg demuxer
ogg:  oggmux: Ogg muxer
ogg:  ogmaudioparse: OGM audio stream parser
ogg:  ogmvideoparse: OGM video stream parser
ogg:  ogmtextparse: OGM text stream parser
ogg:  oggparse: Ogg parser
ogg:  oggaviparse: Ogg AVI parser

では、oggdemuxがどんな機能を持っているかを確認してみましょう。それにはシンクとソースのCapabilities欄を見るとわかります。シンクではaudio/oggやvideo/oggに対応していることがわかります。また、ソースには制限がないことがわかります。

% gst-inspect-1.0 oggdemux
(途中略)
Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      application/ogg
      audio/ogg
      video/ogg
      application/kate

  SRC template: 'src_%08x'
    Availability: Sometimes
    Capabilities:
      ANY
demuxerに対応するdecoderを調べる

次はoggdemuxで抜きだした映像をデコードしてやる必要があります。映像のフォーマットはTheoraでした。gst-inspect-1.0で同じように探してみましょう。

% gst-inspect-1.0 | grep theora
theora:  theoradec: Theora video decoder
theora:  theoraenc: Theora video encoder
theora:  theoraparse: Theora video parser
typefindfunctions: video/x-theora: no extensions
rtp:  rtptheoradepay: RTP Theora depayloader
rtp:  rtptheorapay: RTP Theora payloader

theoradecというdecoderがあるのがわかりました。theoradecのCapabilitiesをgst-inspect-1.0で確認してみましょう。

% gst-inspect-1.0 theoradec
(途中略)
Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-raw
                 format: { I420, Y42B, Y444 }
              framerate: [ 0/1, 2147483647/1 ]
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]

  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-theora

シンクがvideo/x-theoraに対応していて、ソースがvideo/x-rawに対応していることがわかります。

decoderに対応するsinkを調べる

次は、video/x-rawに対応した表示のためのシンクエレメントを探せばよいことになります。先に答えを言うとxvimagesinkがvideo/x-rawに対応したシンクエレメントです*2

% gst-inspect-1.0 xvimagesink
(途中略)
Pad Templates:
SINK template: 'sink'
  Availability: Always
  Capabilities:
    video/x-raw
            framerate: [ 0/1, 2147483647/1 ]
                width: [ 1, 2147483647 ]
               height: [ 1, 2147483647 ]

というわけで、上記の情報から次のようなパイプラインを組み立てれば良いことがわかります。

% gst-launch-1.0 filesrc location="/usr/share/example-content/Ubuntu_Free_Culture_Showcase/How fast.ogg" ! oggdemux ! theoradec ! xvimagesink

実際に上記コマンドを実行すると動画がきちんと再生できました。

まとめ

今回はGStreamerのエレメントをつないだパイプラインの組み立てかたを紹介しました。gst-inspect-1.0を使うことで、エレメントの持つ機能を詳しく知り、どのエレメントとつなげることができるかがわかります。エレメント名にautoがついているエレメントはそのあたりを気にせずに使えて便利ですが、パイプラインがどこまできちんと動作しているかを知りたいときなど、明示的に特定のエレメントを使って確認する必要にせまられることもあります。そんなときに、どうやって組みたてたら良いか知っていると問題解決に役立つのではないでしょうか。

*1 概要より詳しいところまで理解する場合はもっと概念が増えます

*2 もちろんxvimagesinkのほかにもvideo/x-rawに対応したシンクエレメントはあります。

2014-07-22

LXCコンテナに名前でアクセスする方法

LXCコンテナを複数作って、開発に使用しているとIPアドレスではなく名前で各コンテナにアクセスしたくなります。 もちろんlxc-consoleを使えば名前でアクセスできるのですがlxc-consoleではC-aが取られてしまって不便です。

そこで、SSHを使って名前で各コンテナにアクセスできるととても便利です。

dnsmasqを使うと簡単にそのような環境を構築することができるので、その手順を紹介します。 なお、環境はDebian sidを想定しています。

Ubuntuだとlxcを入れるだけでやってくれる内容なのでUbuntu12.04でやっていることを調べてDebian向けにアレンジしています。

必要なパッケージをインストールする

$ sudo apt-get install -y lxc dnsmasq-base bridge-utils

lxcbr0を作成する

$ sudo brctl addbr lxcbr0

lxcbr0が自動起動するように設定する

/etc/network/interfaces:

...
auto lxcbr0
  iface lxcbr0 inet static
  address 192.168.30.1
  netmask 255.255.255.0
  pre-up  /path/to/lxcbr0-pre-up
  post-up /path/to/lxcbr0-post-up
  post-down /path/to/lxcbr0-post-down

pre-up, post-up, post-downでそれぞれスクリプトを指定して必要な処理を実行しています。

lxcbr0-pre-up:

1
2
3
4
5
6
#! /bin/bash

brctl show | grep -q lxcbr0
if test $? -ne 0; then
  brctl addbr lxcbr0
fi

lxcbr0が存在しなかったら作成するスクリプトです。

lxcbr0-post-up:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh
# This is the address we assigned to our bridge in /etc/network/interfaces
braddr=192.168.30.1
# ip address range for containers
brrange=192.168.30.2,192.168.30.254
iptables -A FORWARD -i lxcbr0 -s ${braddr}/24 -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A POSTROUTING -t nat -j MASQUERADE 
dnsmasq --bind-interfaces --conf-file= \
        --bogus-priv \
        --domain-needed \
        --expand-hosts \
        --domain=lxc.localdomain \
        --listen-address $braddr \
        --except-interface lo \
        --dhcp-range $brrange \
        --dhcp-lease-max=253 \
        --dhcp-no-override \
        --pid-file=/tmp/dnsmasq-lxcbr0.pid

このスクリプトが今回の最も重要な部分です。 iptablesでNATの設定をしてからdnsmasqを起動してDHCPサーバとDNSサーバの機能を有効にしています。

同等の設定をデーモンとして起動するdnsmasqに行っても同じことができます。 ただし、その場合iptablesの設定は別の方法で実行する必要があります。

lxcbr0-post-down:

1
2
3
#!/bin/bash
kill $(cat /tmp/dnsmasq-lxcbr0.pid)
rm -f /tmp/dnsmasq-lxcbr0.pid

lxcbr0-post-upで作ったPIDファイルを見てdnsmasqを終了させるだけのスクリプトです。

lxcbr0を起動する

$ sudo ifup lxcbr0

dnsmasqが起動していることを確認します。

$ ps aux | grep dnsmasq
nobody   13187  0.0  0.0  35104  1216 ?        S    15:05   0:00 dnsmasq --bind-interfaces --conf-file= --bogus-priv --domain-needed --expand-hosts --domain=lxc.localdomain --listen-address 192.168.30.1 --except-interface lo --dhcp-range 192.168.30.2,192.168.30.254 --dhcp-lease-max=253 --dhcp-no-override

コンテナを作成する

$ sudo lxc-create -t ubuntu -n ubuntu1204 -- --release precise --bind $USER

名前解決の確認をする

$ dig @192.168.30.1 ubuntu1204
; <<>> DiG 9.9.5-3-Debian <<>> @192.168.30.1 ubuntu1204
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56257
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;ubuntu1204.                    IN      A

;; ANSWER SECTION:
ubuntu1204.             0       IN      A       192.168.30.201

;; Query time: 0 msec
;; SERVER: 192.168.30.1#53(192.168.30.1)
;; WHEN: Mon Apr 28 15:57:44 JST 2014
;; MSG SIZE  rcvd: 55

/etc/resolve.confにnameserverを追加する

このままだとSSHなどを使えないので/etc/resolve.confにnameserverを追加します。

NetworkManagerを使用していない場合は単純に追加しても問題ありませんが、NetworkManagerを使用している場合はNetworkManagerが自動的に/etc/resolve.confを書き換えてしまうのでnamesererの設定が消えてしまうことがあります。 /etc/dhcp/dhclient.confに以下の内容を追加してNetworkManagerを再起動すると、自動的に/etc/resolv.confに反映されます。

/etc/dhcp/dhclient.conf:

...
prepend domain-name-servers 192.168.30.1;
...

SSHでコンテナにアクセスする

$ ssh ubuntu1204

まとめ

LXCコンテナに名前でアクセスする方法を紹介しました。

2014-07-30

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
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|