言語非依存でバイナリデータをやりとりするには

はじめに

色々なシステム間でバイナリデータのやり取りをする方法に興味を持っています.調べている中でUniversal Binary Format(UBF)という規格を知ったので,その元になっている資料をまとめてみました.これは,Getting Erlang to talk to the outside worldという名前で,プログラミングErlangプログラミングErlangの著者でもあるJoe Armstrong氏が書いています.この資料を章ごとにまとめてみました.

ここから

ABSTRACT

Erlangを使ってErlang以外の世界とデータをやりとりするにはどうすればいいでしょう.これは,Erlangを構成要素として使う分散アプリケーションを構築する際には重要な課題です.一般的な解決策は,XML,XML-schemas,SOAP,WSDLと言ったXML標準を使うことですが,非効率,かつ,複雑すぎます.そこで我々はUniversal Binaru Format(UBF)という簡単なバイナリスキーマを提案します.UBFスキーマXML標準と同じ程度の記述能力を持ちますが,非常に簡単です.

INTRODUCTION

この資料で扱うのは,構成要素が非同期に通信する信頼性のある分散システムです.各構成要素は別々の言語で作られ,別々のOS上で動作し,ネットワーク上のどこかで動いています.このシステムでの課題は,これらの構成要素をどのように連携させればよいのか,ということと,システムを容易に構築可能な,使いやすい言語非依存なtransport layerをどのように作るか,の2点です.XMLを使うのは解決策のひとつですが,複雑すぎで冗長です.

XMLが大変だと言ってる

次の"PROBLEMS WITH XML"の章では,XMLの大変さについて書いてます.

OUR ARCHITECTURE

この資料では,下図のようなアーキテクチャのシステムを提案します.この図でBは"Black box"で,Cは"Contract Checker"を意味します.

        +-+     +-+
        |B|--C--|B|
        +-+     +-+
         |
         C
         |
+-+     +-+     +-+
|B|--C--|B|--C--|B|
+-+     +-+     +-+

Black boxはシステムの構成要素で,それぞれが必要に応じてクライアントやサーバとして動作します.構成要素間にはcontract checkerがあります.contract checkerは構成要素間でのメッセージが正当かどうかをチェックします.

        X     X
        -->   -->
+------+         +------+
|Client|----C----|Server|
+------+         +------+
        <--   <--
       (M,S1)  (M,S1)

contract checkerの初期状態がSだとします.クライアントがサーバに向けてXというメッセージを送ると,contract checkerはXが型に従っているかどうか,状態Sで受けてよい型かどうかをチェックします.チェックして問題なければサーバに送信します.サーバがメッセージと状態から構成される組み合わせ{M,S1}を返すと,contract checkerはこのメッセージが現在の状態で受けてよいレスポンスかどうかをチェックします.{M,S1}が問題なければクライアントに送信すると供にcontract checkerの状態をS1にします.contract checkerはクライアントサーバ間で許容されるメッセージの順番や型に関するcontractによってパターン化できます.contractは簡単なnon-deterministic finite state machineと簡単なtype languageを使って定義できるので,以下のようなモデル化できます.

{S_{in}, T_{in}, T_{out}, S_{out}}

これは,サーバがS_{in}という状態で,T_{in}という型のメッセージを受け取ると,T_{out}という型のメッセージを返し,状態がS_{out}になるということを現しています.

UBF - A UNIVERSAL BINARY FORMAT

contractはUBFという言語で記述します.UBFには

があります.

UBF(A) - A BINARY TRANSPORT FORMAT

この章の内容は間違いがあるようなので,quick summaryを見ながらまとめました.

表記 概要
'...' constantをスタックに積む
"..." stringをスタックに積む
Int ~...~ 長さIntのバイナリをスタックに積む
`xxx` スタックにxxx型というタグを付ける
%...% コメント
[-][0-9]+ Integer 資料上だと+でなく*になってるけど,おそらく間違い
\s\n\r\t (white spaceとして)無視する
# スタックにnilを積む
& スタックから2個エントリを取り出して(cons X Y)と置き換える
{ 構造体の開始
} 構造体の終了
$ スタックからエントリを返す
Control '"~%-0123456789{}#&$> の21個
>C スタックから取り出してregister[C]にstoreする
C register[C]をスタックに積む

となります.実際の例だと

'person'>p # {p,"Joe",123} & {p, 'fred', ~abc~} & $

というUBF(A)は

[{person, "Joe", 123}, {person, fred, <>}].

というerlangで記述したUBF(B)になります.4種類の基本型の後に`...`というsemantic tagを付けることができます.このタグはUBF(A)の世界では意味を持ちませんが,UBF(B)の世界で意味を持ちます.例えば

12456 ~...~ `jpg`

はsemangtic tag "jpg"がついた12456バイトのバイナリデータを現すことになります.複雑な構造を作るにはStructとListを使います.Structは,

{ Obj1 Obj2 ... Objn }

と表記します.Obj1..Objnは任意のUBF(A) objectで要素数は固定です.例えばC言語なら構造体に該当します.Listsは,

# ObjN & ObjN-1 & ... & Obj2 & Obj1 &

と表記します.Obj1..ObjNはある特定の型で要素数は任意です.最初の要素がObj1でObj2と続くので注意が必要です.

PROGRAMMING BY CONTRACT

UBFの特徴はcontractにあります.contractはUBF(B)で記述します.

+------+   +----------+   +--------+   +----------+   +------+
|      |-->|          |-->|Contract|-->|          |-->|      |
|Client|   |UBF Driver|   |checker |   |UBF Driver|   |Server|
|      |<--|          |<--|        |<--|          |<--|      |
+------+   +----------+   +--------+   +----------+   +------+
Java     /              /    /       /              /    Erlang
       Java        UBF(A)  UBF(B)    UBF(A)       Erlang
       objects     objects Contract  objects      terms

UBF(B)の実態はtype systemとprotocol description languageです.
type systemは

表記 概要
int() UBF(A) integer
sting() UBF(A) string
constant() UBF(A) constant
bin() UBF(A) binary data
X() Object type X

となります.複合型は再帰的に定義します.

表記 概要
{T1, T2, ..., Tn} T1~Tnが型を現す要素数が固定された組み合わせ
[T] 要素がT型である要素数が任意の組み合わせ
T1 | T2 T1型かT2型の型であることを意味します

さらに,UBF(B)では,新しい型を以下のように表現します.

+TYPE X() = Type

例えば

+TYPE persion() = {person, firstname(), lastname(), sex(), age()}.
+TYPE firstname() = string().
+TYPE lastname() = string().
+TYPE age() = int().
+TYPE sex() = male | female.
+TYPE people() = [person()].

と定義すると,

'person' >p
# {p "jim" "smith" male 10} &
# {p "susan" "jones" female 14} & $

はpeople()型になります.
さて,ここからはfile_serverというcontractについて考えてみます.

+NAME("file_server").
+VSN("ubf1.0").
+TYPES
info() = info;
description() = description;
services()    = services;
contract()    = contract;

file()        = string();
ls()          = ls;
files()       = {files, [file()]};
getFile()     = {get, file()};
noSuchFile()  = noSuchFile.

+STATE start
  ls()      => files()      & start;
  getFile() => binary()     & start
            |  noSuchFile() & stop.

+ANYSTATE
  info()        => string();
  description() => string();
  contract()    => term().

+TYPESは型定義が複数あることを意味します.STATEは状態についての記述で,start状態にあるときにls()というメッセージを受け取ったら,files()というレスポンスを返し,start状態に戻ります.同じように,getFile()を受け取ったらbinary()型のレスポンスを返しstart状態に戻るか,noSuchFile()型のレスポンスを返しstop状態に移行することを意味します.NAME,VSN,ANYSTATEに関しては資料に説明がなくてよくわからないですが,ANYSTATEはstatelessな状況を記述するために使っているようです.

省略

この後"IMPLEMENTATION DETAILS"と"PERFORMANCE"という章はerlangで作った場合の話がでます.さらに,"FUTURE WORK"の章ではさらに高度な状態記述を書く話がでています.

RUNNING THE SYSTEM

前章で定義したcontractで動いているサーバにtelnetできたとすると,以下のような応答になります.表記の都合上$が入力,|がサーバ出力としました.

$ 'info'$
| {"I am a mini file server",'start'}$
$'ls'$
| {{'files',
|   #
|   "ubf.erl"&
|   "client.erl"&
|   "Makefile"& ...}
|   'start'}$

さらに,'contract'$と入力すると,システムのcontractを出力するようですが,資料中にはその機能説明がありません.

$ 'contract'$
| {'contract',
|  {{'name',"file_server"},
|   {'info',"I am a mini file server"},
|   {'description',"
|
| Commands:
|   'ls'$ List files
|   {'get' File} => Length ~ ... ~
|                | noSuchFile
| "},
|   {'services',#},
|   {'states',
|   #{'start',
|     #{'input',{'tuple',#{'prim','file'}&
|       {'constant','get'}&},
|     #{'output',{'constant','noSuchFile'},'stop'}&
|      {'output',{'prim','binary'},'start'}&}&
|     {'input',{'constant','ls'},
|     #{'output',
|       {'tuple',
|         #{'list',{'prim','string'}}&
|       {'constant','files'}&},'start'}&}&}&},
|   {'types',
|       #{'file',{'prim','string'}}&}}}$

もういちど省略

この後の"A LARGER CONTRACT","EXPERIENCE","ACKNOWLEDGEMENTS"という章は省略します.

おわりに

概略はなんとなくわかってきたのですが,実際にシステム間でどんなデータをやりとりするのかはこの資料からはよくわかりません.githubから落としたプログラムがなんとか動き始めたので,それを使って確かめてみます.

UBFを動かす

はじめに

昨日erlangrpm化しましたが,これはUniversal Binary Format(UBF)を試してみたかったからです.UBFについてはまた日を改めて書きますが,XMLのようなテキストベースでの情報流通形式の代わりにバイナリベースでの情報流通を実現します.マッキンダーの「東欧を支配するものはハートランドを制し,ハートランドを支配するものは世界島を制し,世界島を支配するものは世界を制す」とかにならって言うと,

バイナリフォーマットを支配するものはWebSocketを制し,WebSocketを支配するものはクラウドを制し,クラウドを支配するものは世界を制す

とか考えてるからです.

make

gitから落として来てsrcディレクトリ配下でmakeすれば簡単に動くと思ったんですが,はまったのでそのまとめ.priv/make/erl_make.mkというmakeするための補助ツールを書き直す必要がありました.

diff --git a/priv/make/erl_make.mk b/priv/make/erl_make.mk
index ce9b680..b502065 100644
--- a/priv/make/erl_make.mk
+++ b/priv/make/erl_make.mk
@@ -58,7 +58,9 @@ endif
 export DIALYZER_PLT
 export DIALYZER
 
-ERLDIRNAME1 := $(shell which $(ERL))
+ERLDIRNAME0 := $(shell which $(ERL))
+export ERLDIRNAME0
+ERLDIRNAME1 := $(shell readlink -e $(ERLDIRNAME0))
 ERLDIRNAME2 := $(patsubst %/bin/erl,%,$(ERLDIRNAME1))
 ERLDIRNAME3 := $(notdir $(ERLDIRNAME2))
 
@@ -76,11 +78,11 @@ endif
 endif
 
 ## well-known ERT applications
-EDOCDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/erlang/lib/edoc-*)
-EIDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/erlang/lib/erl_interface-*)
-ERTSDIRNAME ?= $(wildcard $(ERLDIRNAME2)/lib/erlang/erts-*)
-XMERLDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/erlang/lib/xmerl-*)
-EUNITDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/erlang/lib/eunit-*)
+EDOCDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/edoc-*)
+EIDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/erl_interface-*)
+ERTSDIRNAME ?= $(wildcard $(ERLDIRNAME2)/lib/erts-*)
+XMERLDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/xmerl-*)
+EUNITDIRNAME := $(wildcard $(ERLDIRNAME2)/lib/eunit-*)
 
 EBIN = ../ebin
 DOC_DIR = ../doc.$(shell cat ../.otpversion)

作者の環境と大分違ってたみたいです.これでmake checkも通りました.make checkを実行した際にできあがるcheck.logの最後はこのような出力がでてきました.

=INFO REPORT==== 1-Jul-2010::05:09:59 ===
    application: stateful_plugin
    exited: stopped
    type: temporary
  All 64 tests passed.

これでやっと次に進めます.

erlang rpmを作ってみる

はじめに

ubfとかebfとかっていうバイナリフォーマットに興味があってerlangをインストールしようとしました.CentOS5.5で,yumを使ってさくっとインストールしてみたんですが,肝心のプログラムがmakeできません.識者に聞いてみた所,バージョン古すぎとご神託を授かりました.そこで最新のバージョンをインストールとあいなったのですが,せっかくなのでrpm化しようとしてはまってしまいました.今回はrpm化するまでの苦闘の道のりです.

環境づくり

vmware上のcentosrpmを作ってみました.しばらくほっといたvmなので,まずはupdateから実行して開発環境をインストールします.

$ sudo yum update
$ sudo yum groupinstall "Development Tools"
$ sudo yum install rpmdevtools
$ mkdir -p ~/rpm/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
$ echo "%_topdir /home/daiba/rpm" > ~/.rpmmacros

これで基本的な環境はできあがったので,希望するerlangソースをダウンロードページから落とします

$ cd rpm/SOURCES
$ wget http://www.erlang.org/download/otp_src_R13B04.tar.gz

後はspecファイルを作ればOK.が,これが大変なわけです.

spec file

ぐぐってみたら,FC用のerlang spec fileを見つけました.これをベースに作ってみたspec fileは次のようになります.

Name:           my-erlang
Version:        R13B
Release:        13.4%{?dist}
Summary:        General-purpose programming language and runtime environment
Group:          Development/Languages
License:        Erlang Public License
URL:            http://www.erlang.org
Source:         otp_src_R13B04.tar.gz

BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root

BuildRequires:  ncurses-devel

%description
Erlang is a general-purpose programming language and runtime
environment. Erlang has built-in support for concurrency, distribution
and fault tolerance. Erlang is used in several large telecommunication
systems from Ericsson.

%prep
[ -d $RPM_BUILD_ROOT ] && rm -rf $RPM_BUILD_ROOT;
%setup -n otp_src_R13B04

%build
%configure
make

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)
%doc AUTHORS EPLICENCE README.md
%{_bindir}/*
%{_libdir}/erlang

%post
%define erlangdir %{_libdir}/erlang
%{erlangdir}/Install -minimal %{erlangdir} >/dev/null 2>/dev/null

ここまで作るのに涙なくしては語れないあれやこれやはありましたが,それはまた別の機会に.まねして作成したspec fileなのでよくわからないんですが,postセクションでInstallというコマンドを実行してるので,postunセクションで何か作業をしないといけない気がするんですが,よくわかりませんでした.とりあえず,インストールしても問題なかったので,しばらく使ってみようと思っています.

rpmを作る

spec file をerlang.specという名前にしてSPECSディレクトリに置いてbuildrpmを実行します.

$ cd ~/rpm
$ cd SPECS
$ vi erlang.spec
$ rpmbuild -bb erlang.spec

そうすると延々またされたあげくにRPMS配下にmy-erlang-debuginfo-R13B-13.4.x86_64.rpmとmy-erlang-R13B-13.4.x86_64.rpmというrpmファイルができます.これでできあがり,

$ sudo rpm -ivh my-erlang-R13B-13.4.x86_64.rpm

とやればインストールできます.インストールできたか試すと

$ erl
Erlang R13B04 (erts-5.7.5) [source] [64-bit] [rq:1] [async-threads:0] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> 123456789 * 9 .
1111111101

となりました.多分問題ないんでしょう.さて,最初にやろうとしたmakeは通るかなー

大規模ソーシャルサーチエンジンの構造

はじめに

Googleのように,どのドキュメントが適切なのかを選ぶのではなく,質問を誰にするのが適切かを選ぶ検索エンジンをAardvarkという会社が作り,その構造を論文で公開しました.この会社はもともとGoogleの社員だった人達が作った物で,最近Googleが買い上げました.今日はその論文の要旨をまとめてみました.

タイトルと著者

タイトルはGoogle創始者Larry PageさんとSergey Brinさんが1988年に発表した"Anatomy of a Large-Scale Hypertextual Search Engine"と韻を踏んでいます.論文を発表したのは,Aardvark社のDamon HorowitzさんStanford Univ.のSepandar D. Kamvarさんです.以下小見出しが章,少々見出しが節という形式で進めます.

ABSTRACT

Aardvarkは会社名であると供に,この会社が作ったSocial Search Engineの名前でもあります.ユーザはIM,email,web入力,text messageや音声で質問をします.Aardvarkは,質問をした人のextended social network中で,その質問に最も適切に答えられると考えた人にその質問を廻します.

INTRODUCTION

The Library and the Village

従来,情報を取得するためにはどうlibraryを使いこなせばいいか,という事が検索の理論的枠組みの基礎となっていました.Google自体"Stanford Digital Library project"から生まれました.一方で人々は,Aardvarkがvillage paradigmと呼んでいる情報取得方法を古くから使ってきました.情報は,村の中では薄く,かつ,人から人へ伝わりながら存在します.質問の答えを見つけるためには,正しい文章を見つけるのではなく,情報を持っている人を見つけなければなりません.

Aardvark

Aardvarkはvillage paradigmに基づいたsocial search engineです.

OVERVIEW

Main Components
  • Crawler and Indexer:情報源を見つけてラベル付けします.ここでいう情報源とは,文章ではなくて人のことです.
  • Query Analyzer:ユーザが必要としている情報を理解します.
  • Ranking Function:適切な情報源を選択します.
  • UI:人々に受け入れやすい,対話的な情報提供方法です
The Initiation of a User

ユーザが初めてAardvarkにアクセスすると,Aardvarkはどんな質問をそのユーザに廻すのが適切かを判断するindexing stepsを実施します.どんな質問を廻すかはそのユーザのextended social networkに依存するので,Aardvarkはユーザの友人をindexesすることと,ユーザとその友人との関係を調べます.その狙いは単にsocial networkを作る事にあるのではありません.ユーザから彼らのextended social networksを利用させてもらうことにあります.Aardvarkでは,ユーザは実社会における人間関係を反映したいくつかのgroupsで結びつきます.これらのgroupsはsocial networksから自動的に取り込むこともありますし,ユーザが編集することもできます.

さらにAardvarkは,ユーザのtopicにおける知識や経験がどのレベルにあるのかを複数の観点から判定しForward Indexにindexesします.Forward IndexにはuserIdに対するtopicごとのレベルのリスト,そのユーザが解答した質問内容と解答の質が格納されています.AardvarkはForward IndexからInverted Indexを作成し,そこでは任意のtopicIdに対して専門性があると判断したユーザのリストとそれらのユーザごとのレベル,回答の質と回答に要した時間を格納しています.

The Life of a Query

Aardvarkはユーザの質問をMessage data structureに正規化しConversation Managerに送ります.Conversation Managerがそのmessageを質問だと判断すると,その質問がどんなtopicsに属しているのかを調べる為Question Analyzerに問合せます.Conversation Managerは,Question Analyzerから得たtopics種別がユーザの考えと一致しているかどうかをユーザに確認し,違っていれば訂正してもらいます.それと同時にConversation ManagerはRouting Engineに対してRouting Suggestion Requestを発行します.Routing EngineはInverted IndexとSocial Graphを使って回答してくれそうな候補者リストを作成し,候補者の回答の質と質問者の希望にどれくらい合致するかを基にランク付けします.Routing Engineはランク付けしたRouting SuggestionsをConversation Mangerに返却します.Conversation ManagerはRouting Policyに従い,回答候補者に質問に返事できるかを問い合わせ続けます.最終的にConversation Managerは回答を質問者に返し,質問者と回答者が追加情報のやりとりを必要とする際には情報を中継します.

ANATOMY

The Model

Aardvarkのcoreは潜在的な回答者へ質問をroutingするための統計モデルにあります.このモデルは,aspect modelと呼ばれている物をネットワーク用にしたものを使っています.このモデルには2つの特徴があります.

    • ユーザu_iがtopic t(t \in T = t_1, ... tn)に属する質問qに答える確率p(u_i|q) = \sum _{t \in T} p(u_i|t)p(t|q)
    • どのような質問であるかに係らずユーザu_iがユーザu_jの質問に答える確率p(u_i|u_j)

これらふたつの確率を掛け合わせた物がscoring function s(u_i,u_j,q)です.

s(u_i,u_j,q) = p(u_i|u_j) \cdot p(u_i|q) = p(u_i|u_j) \sum _{t \in T} p(u_j|t)p(t|q)

ranking problemとしての目標は,ユーザu_jから質問qを与えられた時,s(u_i,u_j,q)を最大にするユーザu_i \in Uのリストを作成することにあります.

Social Crawling

Aardvarkにとっての情報源は人なので,active userを維持し増やして行くことが必要です.そのためには使ってよかったという体験をユーザに与える必要があります.

Indexing People

Aardvarkはユーザu_iについて以下の2点知る必要があります.

    • ユーザu_ip_{smoothed} (t|u_i)で答える事ができるtopic t
    • ユーザu_iとユーザu_jとのconnections p(u_i|u_j)

まずはtopicについて.
p(t|u_i)の値は,過去そのユーザが行った発言から推測します.特定のtopicに関してある確率で回答するコンテンツジェネレータとしてユーザを見るわけです.すべてのtopicに対してスコアを作成しユーザプロファイルに登録します.加えて,

  • ユーザが無視したtopic
  • 機会があったのに回答を拒否したtopic
  • 回答に対して別のユーザから否定的なコメントがついたtopic

を調べ,ユーザに対して送るべきではないtopicを学習します.
さらに,定期的にtopic strengthening algorithmを実行します.このアルゴリズムの基本的な考え方は,あるユーザが特定のtopicの専門家で,そのユーザのほとんどの友人がやはりそのtopicの専門家であれば,そのユーザが自分の友達グループの中でただ一人の専門家である場合よりも高い信頼を置く,というものです.
あるユーザをu_i,そのユーザの友達グループをU,特定のtopicをtとしたとき

if p(t|u_i) \neq 0 then s(t|u_i) = p(t|u_i) + \gamma \sum _{u \in U} p(t|u)

となります.ここで \gammaは小さな定数です.

加えて,ふたつのsmoothing algorithmsを実行します.これは明示的に回答していないtopicに対して確率を割り当てる手段で,ひとつは,basic collaborating filtering techniquesで,似たようなtopicに対しては似たような値にします.もうひとつはsemantic similarityです.

これらの作業を経て,あるユーザに対するすべてのtopicに対するスコアを得ることができるので,\sum _{t \in T}p(t|u_i) = 1となるように正規化しておきます.Bayesの定理から,あるtopicとユーザに対して

p(u_i|t) = \frac{p(t|u_i)(p(u_i)}{p(t)}

が得られます.ここで,p(u_i)は一様分布,p(t)はtopicを観測した割合です.Aardvarkは算出したp(u_i|t)をtopicをキーにした転地インデックスに格納し,質問が来た時に備えます.

次にConnectionsについて.
Aardvarkは任意のユーザ間のconnectionp(u_i|u_j)をさまざまな方法で算出します.ソーシャルネットワーク上での距離も重要ですが,demographics(人口統計学)や振る舞いの類似性も重要です.考慮している項目には以下のようなものがあります

  • Social connection (common friends and affiliations)
  • Demographic similarity
  • Profile similarity (e.g. common favorite movies)
  • Vocabulary match (e.g. IM shortcuts)
  • Chattiness match (freequency of follow-up messages)
  • Verbosity match (the average length of messages)
  • Politeness match (e.g. the use of "Thanks")
  • Speed match (responsiveness to other users)

ユーザ間のconnectionの強さはweighted cosine similarityを使って計算し, \sum _{u_i \in U}p(u_i|u_j) =1となるよう正規化します.
topicとconnectionは常時更新します.

Analyzing Questions

質問を解析する目的は質問qからtopicに対するスコアのリストp(t|q)を導きだすことにあります.まず,以下のClassifierを適用し,回答すべき質問を選別します.

  • NonQuwestionClassifier: 質問かどうか
  • InappropriateQuestionClassifier: Q&Aとして適切な文章かどうか
  • TrivialQuestionClassifier: 人に聞かないと答えられない質問かどうか
  • LocationSensitiveClassifier: 特定の地域に関する知識が必要かどうか

次に以下のTopicMapper algorithmsで得られた結果を統合して,質問に対するtopicのリストを得る事ができます.

  • A KeywordMatchTopicMapper: user profileのtopicに記載された文字列と一致するか
  • A TaxonomyTopicMapper: SVMを用いて約3,000種類に分類
  • A SalientTermTopicMapper: 質問からの特徴語の抽出
  • A UserTagTopicMapper: 質問者によって付けられたタグ
The Aardvark Ranking Algorithm

ランキングはRoutingEngineが行い,質問者と,Question Analyzerから受け取った情報を元に,よい回答をしてくれそうな人の順序リストを作成します.ユーザのランキングを決定するために必要な要素は,Topic Expertise p(u_i|t),Connectednessp(u_i|u_j),Availabilityです.

  • Topic Expertise: まず,Routing Engineは質問とsemantic matchesするユーザ群を探します.location-sensitiveな質問に関しては,ユーザプロフィールがその場所と一致するユーザのみ考慮します.
  • Connectedness: 次にRouting Engineは回答者のtopicに関する専門性とは無関係に,質問者とどれぐらいの親和性があるかを見極めます.
  • Availability: 3つ目にRouting Engineは質問に今回答してくれそうな人の優先度付けををします.これはIMでオンラインになっているかどうかなどを参考にします.

回答してくれそうな人のリストができるとAardvarkはガイドラインに基づいてコンタクトしてはならない人を除外します.残ったリストをConversation Managerに渡します.Conversation Managerはリストのそれぞれの人にアクセスして,回答が得られるまで,質問に答えてくれるかどうか訪ねて廻ります.

この後の論文は…

と続きます.UIは面白いと思うのですが,アーキテクチャではないと思うのでここでは取り上げません.(疲れたともいう)詳しくは元論文をごらん頂くか,The Anatomy of Large-Scale Social Search Engine: ソーシャル検索エンジンAardvark論文の輪講用資料 - シリコンの谷のゾンビに載っている資料をごらんください.

おわりに

いくつかアルゴリズムの名前が出てくるんですが,それがなんだかわかってません.勉強しないと.最近ソーシャルアプリ流行だし,答えを知ってる人を推薦してくれるアプリというのは面白いと思うんですが,いつになったら自分で作れることやら… とりあえず,睡眠不足の日々が解消できてうれしいです.

bash complete でperldoc

bash-completionがないー

bash-completionが入ってない環境でBig Sky :: perldocのbash-completionが激しく便利でうれしょん出た。のようなことができたらいいなと思って試してみました.bashは':'をファイルセパレータに使っているのでHackが必要です.bash-completionのcontribにperl用のコマンドがあって,それを見ながら作ったのがこれ.

コード

function _myperldoc ()
{
    local mod cur word prev
    local cur=${COMP_WORDS[COMP_CWORD]}
    local prev=${COMP_WORDS[COMP_CWORD-1]}
    export mod=${COMP_LINE/perldoc/}

    case $prev in
        -f)
            COMPREPLY=( $( compgen -W 'chomp chop chr crypt hex index lc \
                lcfirst length oct ord pack q qq reverse rindex sprintf \
                substr tr uc ucfirst y m pos quotemeta s split study qr abs \
                atan2 cos exp hex int log oct rand sin sqrt srand pop push \
                shift splice unshift grep join map qw reverse sort unpack \
                delete each exists keys values binmode close closedir \
                dbmclose dbmopen die eof fileno flock format getc print \
                printf read readdir rewinddir seek seekdir select syscall \
                sysread sysseek syswrite tell telldir truncate warn write \
                pack read syscall sysread syswrite unpack vec -X chdir chmod \
                chown chroot fcntl glob ioctl link lstat mkdir open opendir \
                readlink rename rmdir stat symlink umask unlink utime caller \
                continue do dump eval exit goto last next redo return \
                sub wantarray caller import local my our package use defined \
                formline reset scalar undef \
                alarm exec fork getpgrp getppid getpriority kill pipe qx \
                setpgrp setpriority sleep system times wait waitpid \
                import no package require use bless dbmclose dbmopen package \
                ref tie tied untie use accept bind connect getpeername \
                getsockname getsockopt listen recv send setsockopt shutdown \
                socket socketpair msgctl msgget msgrcv msgsnd semctl semget \
                semop shmctl shmget shmread shmwrite endgrent endhostent \
                endnetent endpwent getgrent getgrgid getgrnam getlogin \
                getpwent getpwnam getpwuid setgrent setpwent endprotoent \
                endservent gethostbyaddr gethostbyname gethostent \
                getnetbyaddr getnetbyname getnetent getprotobyname \
                getprotobynumber getprotoent getservbyname getservbyport \
                getservent sethostent setnetent setprotoent setservent \
                gmtime localtime time times' -- "$cur" ) )
            return 0
            ;;
    esac

    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W '-h -V -r -i -t -u -m -n -l -F -v -T -d -o -M -w -X -q'' -- "$cur" ))
    else


        word=" `perl -MExtUtils::Installed -le \
            '$n = $ENV{mod}; $n =~ s/\s+//; $i = ExtUtils::Installed->new; \
             @m = $i->modules; @m = grep{/^$n/}@m; @m = map{s/^$n//; $_}@m; print join q{ }, @m'` "
        COMPREPLY=( $( compgen -W "${word}" ) )
    fi
}
complete -F _myperldoc perldoc

モジュールと引数候補を出す所は作りましたが,関数一覧はcontribにあったものを使いました.一覧を作るにはどうするのがいいんでしょうね?

使い方

ちょっと時間はかかりますが,perldocと書いてからtabを3回押すと

$ perldoc 
Display all 451 possibilities? (y or n)

という感じで表示します.yを押すと一覧表示です.モジュールに関しては'::'まで入力してtabを2回押してください

$ perldoc IO::
All              Compress::Bzip2  Handle::Util     Socket::SSL      Stringy          Zlib
Compress::Base   Compress::Zlib   Prompt           String           Tty              
$ perldoc IO::

となります.下位のモジュールにアクセスする時も'::'まで書いてください.クラス名の補完は残念ながらできないです.私の場合は,.profileに

. ~/.myperldoc

という行を追加してみました.少し使ってみて使えるかどうか試してみようと思ってます.あ,まだgithubとかには上げてません.まだ安定してない感じです.もうshellでのプログラミング忘れてしまいました…

jetpackでtwitter client

何度目かのjavascript

普段xircdを使ってtwitterをチェックしていたのですが,いじっている内に動きがおかしくなってきたこともあって,クライアントを変えることにしました.firefox用のEcofonをしばらく使ってみたのですが,自分が欲しいのはlistなんて機能とは関係なく,リアルタイムにtwitをポップアップしてくれるだけのツールだと気づいたので,作ってみる事にしました.

ポーリングにチャレンジ

最終的にはstreaming APIを使ってリアルタイムにポップアップするようにしたいのですが,まずは肩ならしとしてapi.twitter.comをポーリングするスクリプトを書いてみました.久しぶりに書いたので結構色々なことを忘れていました...jetpackを使うにはjetpack用のjavascriptファイルとそれを呼び出すhtmlファイルを書く必要があります.今回はdropboxのpublicフォルダにjsとhtmlファイルをおいて,それを呼び出すようにしてみました.それぞれの名前をtwitter-notifier.html, twitter-notifier.jsとすると,twitter-notifier.htmlはこんな感じ.

<title>Twitter notifier</title>
<link rel="jetpack" href="twitter-notifier.js">
<h1>Twitter notifier</h1>
<p>Displays a notification bubble whenever you have an unread tweet in
Twitter.</p>
<p>Version 0.2 - 12 Feb 2010</p>
<p>Code is 
<a href="http://dl.dropbox.com/u/USERID/jetpack/TwitterNotifier/twitter-notifier.js">here</a>
.</p>

実際に動くjavascriptは以下のようになります。

jetpack.future.import("storage.simple");

twitter = {
 update: function () {
  $.ajax({
   type: "GET",
   url: "http://twitter.com/statuses/friends_timeline.json?since_id="+store.lastId,
   dataType: "json",
   success: function (tweets) {
    if (tweets.length == 0) return;
     tweets.sort(function (a, b) {
     return a.id - b.id;
    });
    $.each(tweets, function () {
     if (this.id > store.lastId) {
      store.lastId = this.id;
      queue.push({
       title: this.user.name,
       body: this.text,
       icon: this.user.profile_image_url
      });
     }
    });
    twitter.showNotifications();
   },
   error: function (req, status, error) {
    console.log(status + ' ' + error);
   }    
  });
 },

 showNotifications: function () {
  if (queue.length > 0) {
   jetpack.notifications.show(queue.shift());
   notifier = setTimeout(twitter.showNotifications, 6000);
  } else 
   clearTimeout(notifier);
 },
}

store = jetpack.storage.simple;
if (!store.lastId)
 store.lastId = 1;
queue = [];

twitter.update();
setInterval(twitter.update, 60*1000);

このAPIはベーシック認証を使っているので,FireFoxを起動したときにアカウントとパスワードを聞いてきます.後は1分毎にサイトをチェックして,新規twitがあればそれをNotificationとして表示します.私の環境はmacGrowlを使っているので,Growlがポップアップしてきます.

さて,

streamAPIを使うにはどのライブラリを使うと楽かなー

OpenVZを使ってみる

はじめに

共用サーバのような物を作ろうと思って,chrootSElinuxの組み合わせをどうすればいいのか考えていたらOpenVZというものを教えてもらいました.VMWare上で環境を動かすところまでうまくいったので,そのまとめです.

ホスト環境

windowsXPVMWare Serverを入れて,その上にCentOS 5.4 x86_64をインストールしました.サーバの名前はpotate,ネットワークはNAT構成で,512MBのメモリと8GBの仮想ディスクを設定しました.インストールに使ったのはCentOS-5.4-x86_64-netinstall.isoで,textベースのインストーラを使用し,ナビゲーションは英語で,キーボード設定はjp106,IPv4dhcpのみ使って,http://ftp.riken.jp/Linux/centos/5.4/os/x86_64から必要なデータをダウンロードしました.Package Selectionでは何も選択していません.SELinuxはdiabledにしておきました.

OpenVZのインストール

基本的にはQuick installationを見ながらの作業になりますが,x86_64のツールにはバグがあるようなので,途中からInstall OpenVZ on a x86_64を見る必要があります.

Quick installationを見ての作業

まずはパッケージを最新の状態にしてからOpenVZ用のカーネルをインストールします.

# yum update
# cd /etc/yum.repos.d
# wget http://download.openvz.org/openvz.repo
# rpm --import  http://download.openvz.org/RPM-GPG-Key-OpenVZ
# yum install ovzkernel.x86_64
...
Installed:
  ovzkernel.x86_64 0:2.6.18-164.10.1.el5.028stab067.4    

次にkernel周りの設定を変更します.まずは/boot/grub/grub.conf

--- grub.conf.org       2010-02-09 18:09:13.000000000 +0900
+++ grub.conf   2010-02-09 18:09:29.000000000 +0900
@@ -11,7 +11,7 @@
 timeout=5
 splashimage=(hd0,0)/grub/splash.xpm.gz
 hiddenmenu
-title CentOS (2.6.18-164.10.1.el5.028stab067.4)
+title OpenVZ (2.6.18-164.10.1.el5.028stab067.4)
        root (hd0,0)
        kernel /vmlinuz-2.6.18-164.10.1.el5.028stab067.4 ro root=/dev/VolGroup00/LogVol00
        initrd /initrd-2.6.18-164.10.1.el5.028stab067.4.img

それから/etc/sysct.confを変更します.

--- sysctl.conf.org     2010-02-09 18:26:50.000000000 +0900
+++ sysctl.conf 2010-02-09 18:29:56.000000000 +0900
@@ -4,7 +4,10 @@
 # sysctl.conf(5) for more details.
 
 # Controls IP packet forwarding
-net.ipv4.ip_forward = 0
+net.ipv4.ip_forward = 1
+
+# Controls proxy arp
+net.ipv4.default.proxy_arp = 0
 
 # Controls source route verification
 net.ipv4.conf.default.rp_filter = 1
@@ -12,8 +15,12 @@
 # Do not accept source routing
 net.ipv4.conf.default.accept_source_route = 0
 
+# We do not want all our interfaces to send redirects
+net.ipv4.conf.default.send_redirects = 1
+net.ipv4.conf.all.send_redirects = 0
+
 # Controls the System Request debugging functionality of the kernel
-kernel.sysrq = 0
+kernel.sysrq = 1
 
 # Controls whether core dumps will append the PID to the core filename
 # Useful for debugging multi-threaded applications

これで準備できたのでpotateをリブートします.Quick installationを見るのはここまでです.

Install OpenVZ on a x86 64 system Centos-Fedora を見ての作業

といっても,パッチをダウンロードして実行するだけです.

# cd /tmp
# wget http://linux.carreira.com.pt/ovzutils/setx86_64-0.5.tar.gz
# tar xvfz setx86_64-0.5.tar.gz
# sh setx86_64
---------------------------------------------
 Openvz to x86_64 platform (Ver. 0.5)
---------------------------------------------
Your OS seems to be........ CentOS release 5.4 (Final)
rpm, yum and python........ Found!
setx86_64: line 90: [: too many arguments
Python version... 2.4...... OK!
rpm-python for x86_64...... Found!
---------------------------------------------
ALL TESTS PASSED!

Do you want to go on with the installation? [N/y] 

...
 All templates installed!
vzrpm python is fixed for 64 bits...
Applying patches...
patching file /usr/share/vzpkg/cache-os
patching file /usr/share/vzpkg/functions
patching file /usr/bin/vzyum
patching file /usr/bin/vzrpm
patching file /vz/template/centos/5/x86_64/config/install-post

END INSTALL!

End INSTALL!がでれば作業終了です.ここで,コンテナ(OpenVZ用語でのゲストOS)でlocaleを変更するには/vz/template/centos/5/x86_64/config/.rpmmacrosの"%_install_langs C"と言う行を"%_install_langs C:pt_PT:pt_PT.UTF-8:en_US:en_US.UTF-8"のように変更するようですが,lang Cで構わなかったので,何も設定しませんでした.インストールできているかどうかは,x86_64系のテンプレートが存在するかどうかでチェックします.

# vzpkgls | grep x86_64
...
centos-5-x86_64-minimal
centos-5-x86_64-default
centos-4-x86_64-minimal
centos-4-x86_64-default

このテンプレートからキャッシュテンプレートを作ります.自分でも用語がよくわからなくなってきましたが,まぁそこはきにせず.出来上がる際にcron.daily関連のエラーが出ますが,これは無視します.

# vzpkgcache -f centos-5-x86_64-minimal
...
Complete!
chmod: cannot access `/etc/cron.daily/slocate.cron': No such file or directory
chmod: cannot access `/etc/cron.daily/makewhatis.cron': No such file or directory
Packing cache file centos-5-x86_64-minimal.tar.gz ...
Cache file centos-5-x86_64-minimal.tar.gz [120M] created.

これでできた/vz/template/cacheにあるキャッシュテンプレートからコンテナを作ります.

# vzctl create 101 --ostemplate centos-5-x86_64-minimal
Creating container private area (centos-5-x86_64-minimal)
Performing postcreate actions
Container private area was created

コンテナIDとしては101以上の値を使ってください.これでコンテナができたので,起動します.

# vzctl start 101
Starting container ...
Container is mounted
Setting CPU units: 1000
Configure meminfo: 65536
Container start in progress...

起動できたかどうかの確認はvzlistコマンドを使います.

# vzlist -a
      CTID      NPROC STATUS  IP_ADDR         HOSTNAME                        
       101          2 running -               -                               

動いているようなので,コンテナに入ってみます.exitすればコンテナから抜けます.

# vzctl enter 101
entered into CT 101
[root@potate /]# exit
logout

exited from CT 101

コンテナに入らずホストからモジュールをインストールしたりアップデートしたりできます.例えばコンテナにアップデートするには,

# vzyum 101 update
exec /usr/bin/yum -c /vz/template/centos/5/x86_64/config/yum.conf --installroot /vz/root/101 update
Excluding Packages in global exclude list
Finished
Setting up Update Process
No Packages marked for Update

となりますし,dhcpクライアントをインストールするには

# vzyum 101 install dhclient

となります.コンテナ制御のコマンドはContainer creationのページにまとまっています.

ネットワーク設定

コンテナは初期状態でネットワーク設定がまったくありません.ifconfig -aを実行するとこんな感じです.venetはコンテナ用の簡易インタフェースで,簡単に設定できるのですが,あえて使いません.

# vzctl exec 101 ifconfig -a
lo        Link encap:Local Loopback  
          LOOPBACK  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

venet0    Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          BROADCAST POINTOPOINT NOARP  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

いばらの道を求めてeth0をコンテナに作ります.

# vzctl set 101 --netif_add eth0 --save
Configure veth devices: veth101.0 
Saved parameters for CT 101
# ifconfig veth101.0 0

できあがったインタフェースをホストとコンテナの両方で確認してみると次のようになります.

# ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:0C:29:4F:48:12  
          inet addr:192.168.121.138  Bcast:192.168.121.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe4f:4812/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:884 errors:0 dropped:0 overruns:0 frame:0
          TX packets:451 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:83836 (81.8 KiB)  TX bytes:63347 (61.8 KiB)
          Base address:0x2000 Memory:d8920000-d8940000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:560 (560.0 b)  TX bytes:560 (560.0 b)

sit0      Link encap:IPv6-in-IPv4  
          NOARP  MTU:1480  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

venet0    Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          UP BROADCAST POINTOPOINT RUNNING NOARP  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

veth101.0 Link encap:Ethernet  HWaddr 00:18:51:54:D2:13  
          inet6 addr: fe80::218:51ff:fe54:d213/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:22 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

# vzctl exec 101 ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:18:51:53:19:C4  
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

lo        Link encap:Local Loopback  
          LOOPBACK  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

venet0    Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          BROADCAST POINTOPOINT NOARP  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

ホストとコンテナの間で通信ができるようにするため,ホストにブリッジユーティリティをインストールして,vzbr0というブリッジデバイスを作成します.

# yum install bridge-utils
# brctl addbr vzbr0
# brctl show
bridge name     bridge id               STP enabled     interfaces
vzbr0           8000.000000000000       no

vzbr0用にネットワーク設定を行います.

# ifconfig vzbr0 0
# ls /proc/sys/net/ipv4/conf
all  default  eth0  lo  venet0  vzbr0
# cat /proc/sys/net/ipv4/conf/vzbr0/forwarding
1
# cat /proc/sys/net/ipv4/conf/vzbr0/proxy_arp
0
# echo 1 > /proc/sys/net/ipv4/conf/vzbr0/proxy_arp

network-scripts配下のifcfg-eth0を変更して,ifcfg-vzbr0とifcfg-veth101.0を新規作成します.

# cd /etc/sysconfig/network-scripts

# diff -u ifcfg-eth0.org ifcfg-eth0
--- ifcfg-eth0.org      2010-02-10 11:44:20.000000000 +0900
+++ ifcfg-eth0  2010-02-10 11:46:06.000000000 +0900
@@ -1,6 +1,5 @@
 # Intel Corporation 82545EM Gigabit Ethernet Controller (Copper)
 DEVICE=eth0
-BOOTPROTO=dhcp
-HWADDR=00:0C:29:4F:48:12
+BOOTPROTO=none
 ONBOOT=yes
-DHCP_HOSTNAME=potate
+BRIDGE=vzbr0

# cat ifcfg-vzbr0
DEVICE=vzbr0
BOOTPROTO=dhcp
ONBOOT=yes
DHCP_HOSTNAME=potate
TYPE=bridge

# cat ifcfg-veth101.0
DEVICE=veth101.0
ONBOOT=yes
BRIDGE=vzbr0

これでネットワーク周りの設定ができたのでリブートします.リブート後にインタフェースが正常に動作していることを確認します.

# ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:0C:29:4F:48:12  
          inet6 addr: fe80::20c:29ff:fe4f:4812/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1231 errors:0 dropped:0 overruns:0 frame:0
          TX packets:102 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:699302 (682.9 KiB)  TX bytes:18000 (17.5 KiB)
          Base address:0x2000 Memory:d8920000-d8940000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:560 (560.0 b)  TX bytes:560 (560.0 b)

sit0      Link encap:IPv6-in-IPv4  
          NOARP  MTU:1480  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

venet0    Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          UP BROADCAST POINTOPOINT RUNNING NOARP  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

veth101.0 Link encap:Ethernet  HWaddr 00:18:51:54:D2:13  
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:7 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

vzbr0     Link encap:Ethernet  HWaddr 00:0C:29:4F:48:12  
          inet addr:192.168.121.138  Bcast:192.168.121.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe4f:4812/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:100 errors:0 dropped:0 overruns:0 frame:0
          TX packets:123 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:7624 (7.4 KiB)  TX bytes:19997 (19.5 KiB)

vzbr0にアドレスが正しく割振られていることがわかります.ちなみにブリッジの状態は以下のようになります.

# brctl show
bridge name     bridge id               STP enabled     interfaces
vzbr0           8000.000c294f4812       no              veth101.0
                                                        eth0

ここまでできたら,後はコンテナ側の作業です./etc/sysconfig/networkと/etc/sysconfig/network-scripts/ifcfg-eth0を新設します.

# vzctl enter 101
entered into CT 101

# cd /etc/sysconfig
# cat network
NETWORKING="yes"
NETWORKING_IPV6=no
HOSTNAME=taro

# cd /etc/sysconfig/network-scripts
# cat ifcfg-eth0
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
DHCP_HOSTNAME=taro

これで準備が完了したので,コンテナから抜けてリスタートさせます.

# exit

# vzctl restart 101
Restarting container
Stopping container ...
Container was stopped
Container is unmounted
Starting container ...
Container is mounted
Setting CPU units: 1000
Configure meminfo: 65536
Configure veth devices: veth101.0 
Container start in progress...

# vzlist
      CTID      NPROC STATUS  IP_ADDR         HOSTNAME                        
       101          3 running -               -                               
# vzctl exec 101 ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:18:51:53:19:C4  
          inet addr:192.168.121.139  Bcast:192.168.121.255  Mask:255.255.255.0
          inet6 addr: fe80::218:51ff:fe53:19c4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:11 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1251 (1.2 KiB)  TX bytes:1800 (1.7 KiB)

これでコンテナがdhcpを使ってIPアドレスを使えるようになりました