読者です 読者をやめる 読者になる 読者になる

進行中の計画をメモ

今進行中の諸々のアイディア。放っておくと忘れてしまうので、思いつくまま気ままにメモ。

VIVER CORE計画

未踏ユースで開発したVIVERの中から、ネットワークブート機能(開発者的にはネットワークブート機能ではなくinitramfs)だけを取り出し、ネットワークブートの利便性を良くすることだけに特化しようという計画。
VIVER 1.0では、分散ネットワークの自動構築やCopy-on-Writeまで一度にやろうとしたので、大きなポイントである「ディストリビューション非依存性」を徹底するのが難しくなってきてしまった。そこで、分散ネットワークの自動構築はディストリビューションに依存してしまうものだとして別に分離し、とりあえずブートする部分だけに集中しようというのがきっかけ。


ルートファイルシステムはカーネルモードで動作するものだけサポートし、シャットダウン時の問題が起きないようにする。シャットダウン時の問題というのは、すべてのプログラムにKILLシグナルが飛ばされる(ルートファイルシステムをリードオンリーで再マウントするため、どのディストリビューションでも行われる)と、ユーザーランドのプログラムが落ちてしまい、ルートファイルシステムにアクセスできなくなって正常にシャットダウンできなくなるという問題。
KILLシグナルが飛ばされる前に処理をフックしてVIVER用終了処理を実行できれば良いものの、そうするとディストリビューション非依存性を維持するが難しい。(網羅的に作業すればいいという話もあるが、それはモチベーションが…)
正常にシャットダウンできないと、sshでリモートから再起動できなくなる。これは良くない。


ユーザーランドのプログラムが必要なファイルシステムを使いたいときは、/etc/fstabでやるという方針。/etc/fstabで/usrにaufsでNFSをかぶせたりするのは問題なし。あるいはinitをフックしてpivot_rootする(2段目のinitrdのような)。iSCSIでブートだーとかとかはそのあたりで。そこはVIVER CORE的には何も関与しない。



現状で、HTTP/FTP/TFTP/rsync/NFS/(9p)を使ってルートファイルシステムをダウンロード(またはマウント)して起動するところまでは完成。それからpostboot=でパッチを当てられる。
今のところ他に考えているのは、

  • NBD+ext2/ext3/XFS/...でルートファイルシステムをダウンロード(ダウンロードが終わったらnbd-clientは終了する←ユーザーランドで動くので)
  • ローカルディスクをサポート
    • disk+fs://dev=dev/sda1
      • ローカルディスクをデバイスノード名を指定してマウント
      • ファイルシステムタイプは自動判別
    • disk+ext3://file=/etc/unique/file
      • ローカルディスクをディスク上にあるファイルを指定してマウント
      • ディスクのファイルシステムタイプはext3
    • disk+fs+loop+ext3://file=/vier.suqash&loop=/viver.squash
      • ローカルディスクをディスク上にあるファイルを指定してマウント
      • 指定したファイルをループバックマウント
      • ディスクのファイルシステムは自動判別
      • ループバックイメージのファイルシステムはext3
    • disk+ext3+aufs://file=etc/unique/file
      • ローカルディスクをディスク上にあるファイルを指定してマウント
      • ファイルシステムはext3
      • その上にtmpfsをaufsでかぶせてCopy-on-Write
    • disk+fs+loop+fs+aufs://file=/viver.squash&loop=/viver.squash
      • ローカルディスクをディスク上にあるファイルを指定してマウント
      • 指定したファイルをループバックマウント
      • その上にtmpfsをaufsをかぶせてCopy-on-Write
    • disk+fs+loop+squashfs+aufs+loop+ext3+disk+fs://file=/viver.squash&loop=/viver.squash&cowfile=/cow.img&cowloop=/cow.img
      • ローカルディスクをディスク上にあるファイルを指定してマウント
      • その上のファイルをループバックマウントして
      • その上にループバックマウントしたファイルシステムをaufsでかぶせる(FAT/NTFSでフォーマットされたディスクはCopy-on-Writeで使うには適していないので、ループバックマウントを挟む)
  • Fake NFS-Root
    • /etc/fstabと/etc/mtabを、/がNFSでマウントされているように改竄する。NFS-Rootが考慮されたディストリビューションだと、シャットダウン時までネットワーク落とさないようにしてくれたりする


ローカルディスク周りの実装はVIVER 1.0のものが使えるので、特に難しいことはない。
…ただdisk+fs+loop+fs+aufs+loop+fs+disk+fsは、シャットダウン時にほどけなくなる(→roでリマウントできなくなる)可能性が多分にあるので、やってみてダメだったら止めるかも。シャットダウンできなくなるわけではないので、無視するも可。



ローカルディスクのサポートができたら、Ubuntuあたりを使ってデモシステムが作れたら面白いなーなどなど。USBメモリ1本から複数のマシンを起動できる、と言うもの。(ここは分散ネットワークを自動構築するアイディアを組み合わせて)
今度はCDなんて「過去の」メディアは無視!全面的にフラッシュメモリで。書き込み可能か否かでできることが全然違ってきてしまう(→面白味が違う)。


VIVER CORE自体はブートするだけなので、それだけでは使えない。VIVER CORE Serverパッケージは、その間にモチベーションをつなげるためと言うか何と言うか。


むしろ重要なのは、次の「分散ネットワークを自動構築するアイディア」と「分散ファイルシステム計画」の方で、3つ揃うと予てよりの構想が達成される!
この構想は、ネットワークを構築するノウハウを共有できるようにする、具体的には「ネットワーク」をyumでインストールできる!という感じのもの。yum hoge-networkと打ち込むと、その誰かが作ったネットワークが目の前で起動する、などなど。未踏ユース時点、と言うかAC入試時点でアイディアはあったのだけど、未踏ユース期間中ではやっぱりムリだった…そしてさらに1年が経とうとしているという…。



分散ネットワークを自動構築するアイディア

サーバーとネットワークの動的な構築について多重化・負荷分散の自動化のエントリに書いたアイディア。KLab勉強会#2」の資料を公開しますでも最後に少し紹介。


VIVER 1.0に搭載したRUNES(Role-based Unified Network Extension Systemのつもり)は、このアイディアを実装しようとしたプロトタイプで、次の方針の一部を実装したもの。

  • 1つの「モジュール」が1つ以上の「サービス」を提供する
    • モジュールとは、実行プログラムを1つ含むファイル一式
    • サービスとは、何か役に立つもの。欲しいもの。目的。
  • モジュールは簡単に追加できる
  • モジュール同士はメッセージをやりとりできる
    • モジュールは他のモジュールと通信するためのインターフェイスを持っている
  • モジュール同士はお互いを発見することができる
    • ホストとモジュールを指定すると、モジュールへの参照を取得できる
      • ホストの指定例:モジュールAが動いているホスト全部、モジュールBが動いていて一番応答が早かったホスト
      • モジュールの指定例:モジュールA、インターフェースXを持っているモジュール全部
  • モジュールは、他のモジュールが追加されたり離脱されたりするイベントを補足することができる


VIVER 1.0では、この内で、同一ホスト内で動いているモジュールとのみ通信できる。DHCPサーバーとTFTPサーバーを連携させつつ自動構築するあたりは、この機能が役に立っている。しかし、これだけではあまり嬉しくない。

RUNESの問題は、言語非依存にして使いやすいIPC(→モジュール間の通信)を実装しようとしたところにあった。それは難しい!できたとしても、あまり使いやすくないかもしれない。


そこで、今はモジュールの実装に使える言語をRubyに限定して実装しようとしていて、これならモジュール間の通信は簡単。同じホストであれば、1つのrubyインスタンスで複数のモジュールをloadすればいいだけの話で、単純にメソッドを呼べる。ホストを越えたとしても、dRubyを使えば普通にメソッドを呼べる。これでモジュール間通信の問題は解決した。
後は以前に作ったRubyでモジューラブルシステムを構築するプラットフォームのコードを転用して、クラス1つ→モジュール1つ/バンドル1つで実装できるようにする。かなり直感的にモジュールを書けるはず。



残る問題は、モジュール同士の発見と、モジュールが追加されたり離脱されたりを補足すること。


これはAvahi + D-BusでマルチキャストDNS+DNS-SDで実装できる。mDNS+DNS-SDなので、BonjourBrowserなどでブラウズすると、ネットワークを俯瞰できて面白いかも。
モジュールの発見がイベントドリブンになる(場合によっては排他処理も必要 - dRubyはマルチスレッド)あたりで、モジュールの実装が難しくなるかもしれない。でもそれはどうしようもない気がする…。



それからRUNESでは、モジュール自体がプログラム(dhcpdなど)を含んでいた。今度はこれはやめて、モジュールは「設定ファイルを書く」。デーモンの起動までやるかどうかは未定。daemontoolsの設定ファイルを書くモジュールを作るのが良いかも。
まず設定ファイルのテンプレートを作っておき(これはネットワーク全体の設定の一部)、モジュールがテンプレートエンジンを使ってそれを実際の設定ファイルに変換する。

テンプレートはたとえば↓こんな感じ。

<% virtual_server.each {|service| %>
virtual_server <%= @service.ip %> <%= @service.port %> {
    delay_loop 3
    lvs_ched lc
    lvs_method DR
    protocol TCP

    <% @service.realserver.each {|real| %>
    real_server <%= real.ip %> <%= real.port %> {
        weight 1
        inhibit_on_failure
        HTTP_GET {
            url {
                path /index.html
                status_code 200
            }
            connect_timeout 3
        }
    }
    <% } %>
}

vrrp_instance FLAT<%= service.port %> {
    interface <% service.interface.name %>
    state BACKUP
    garp_master_delay 5
    virtual_router_id <% service.vid %>
    priority 100
    nopreempt
    virtual_ipaddress {
        <%= service.ip %>/<% service.netmask %> dev <% service.interface.name %>
    }
}
<% } %>

どこを変数にするかはモジュールの作り方次第。設定ファイルの詳細は現状のネットワーク/作りたいネットワーク次第。


ネットワーク全体の設定ファイルは、↓こんな感じになるはず。言語内DSLと言うか、Rubyそのもの。

keepalived_service1 = {
    :name => "SomeService"
    :ip => "192.168.0.100"
    :port => 80
    :vid => 50
    :template => File.read("keepalived_service1.conf.erb")
}

case @interfaces[0].ipv4  # IPの他にMACアドレス、PCIデバイスのID、SCSIディスクのID、パーティションのUIDなど
when "192.168.0.2", "192.168.0.3"
    @config[:Keepalived][0] = keepalived_service1
    loadModule(:Keepalived)    # ここでclass Keepalivedがinitializeされる
when "192.168.0.10", "192.168.0.11", "192.168.0.12", "192.168.0.13"
     @config[:Apache][:Keepalived] = "SomeSerice"
     @config[:Apache][:template] = File.read("apache1.conf.erb")
     loadModule(:Apache)    # ここでclass Apacheがinitializeされる
end

# ここからメインループ開始

設定ファイルは実は設定ディレクトリ(バンドル)で、その構造は↓こんな感じ。

conf/
conf/config.rb   # メイン設定ファイル(Rubyスクリプト)
conf/apache.conf.erb     # 自由にファイルを配置できる
conf/keepalived_service1.conf.erb

conf/をカレントディレクトリとしてconf.rbがloadされる。


設定ファイルをどこに置くか(ネットワークに置きたい。ホストに置きたくない)、設定を変更したいときはどうするか、などなどの問題がこれから出てくるはず。



分散ファイルシステム計画

スケールアウトする分散ファイルシステムのアイディア。高速多重化共有ブロックデバイス V-FIELDリポジトリ)を実装した経験を活かして、今度は書き込みもできる分散ファイルシステムを作ろうという計画。


結局のところUNIX系システムはファイルでできているので、ファイルをネットワーク中に仮想化できれば、システム自体を仮想化できる…わけでもないけど、特定のホストで動いていたアプリケーションを、別のホストで引き継ぐことが比較的簡単にできるようになる。

いろいろと設計を考えた後、これはかなり難しい!(でも不可能ではない!)と言うことが判明したので、まずは分散ネットワークを自動構築するアイディアの方の実装を優先して、その間にしっかりと構成を練ろうと画策中。


一番のポイントは、できる限りサーバーがクライアントの状態を保持しないようにする、と言うこと。(あるノードがクライアントになったりサーバーになったりする)
それからreadとwriteを明確に別けること。writeは冗長化すべき対象で、readは負荷分散すべき対象。


writeを数珠状につなげていくあたりは大体固まった。今はノードの参加をどのようにするか考え中。


WikiForme

まったく趣が変わって、WikiForme。1つのWiki記法の文章から、HTMLもPDFもTeXInDesignXMLも書き出せるようにしようというもの。「XMLって1ソースマルチユースで嬉しいとか言うけど、人間が書くものじゃないよね!」というのがきっかけ。
包含可能要素と親要素補完を使って、フラットな文章に構造を作り、その構造を記法プラグインに渡して目的のフォーマットに変換する。


Rubyの習作だったハズが作ってみたら面白かったので、作り込んでみたら実装がひどいことにorz

ソースを見ていただければ分かるのですが、トンでもない勢いでリフレクションを使いまくってます。Rubyの限界に挑戦!と思ったら、Ruby 1.8と1.9の挙動の違い(module_evalのブロック内でのインスタンス変数の扱いが違う)で悩まされるなどなど…。おかげでRubyをフルパワーで使ってどんな記法でも書けるようになったものの、逆に柔軟すぎていろんな記法を書きたくなってしまって困る。ネストできる表や自動番号付きの見出しなどなど。
逆に、シンプルに画像を貼るなどの機能が未実装。1ソースマルチユースにしようとすると、画像のフォーマットを複数用意して、変換先によって使う画像を変える、ということをしたい。HTMLならPNG、PDFならepsなど。



ある程度「使える」まで記法が揃っていないので、面白いけどイマイチ実用的に使えない。もっと使えるように作りたいのですが、問題が多数。

  • エディタでWiki記法を書く→コマンドラインでWikiFormeを起動→結果を見る と使い勝手が良くない
    • エディタにWikiFormeを内蔵→ボタン一発で変換できると良い?
    • Webインターフェース?Wikiとして使えるようにする?
    • 他のWikiやCMSのプラグインにする?
  • 記法を定義するが面倒
    • 誰か作ってくれないかなー ^_^;)
  • 記法を定義するAPI群が未定orz
    • どういう機能が必要なのか良く分からない
    • と言うかコードが混沌として良くない。何とかしたいけどデザインが思いつかない
  • JavaScript/ECMAScriptで実装したい
    • ブラウザで動かせる!
    • Flexで動かせる!
    • あのコードをRuby以外の言語に移植するのはムリな気が…
    • 結構処理が重いのでJavaScriptだと大変かも


残念なことに、人間はノンプリエンプティブなシングルタスクなようで、同時に複数のことを進められない。今のところVIVER方面に力を入れているので、今度WikiFormeを進めるのはVIVER方面が行き詰まったときになりそう。