KVM + VIVER CORE Serverで画面転送型シンクライアントを作る

Linuxカーネルに組み込まれている仮想化技術「KVM」と、VIVER CORE Serverを使って、画面転送型シンクライアントシステムをサクッと作ってみました。


画面転送型シンクライアントというのは、X11やRDP(Windowsターミナルサービス)、CIA(Citrix Presentation Server)などを使って、アプリケーションはすべてサーバーで実行しつつ、画面だけをクライアントに送るシンクライアントシステムの1方式。製品にCitrixの製品群や、SunRayなどなど。

クライアントは貧弱でも良い(そのために独自端末が使われて、余計にコストが発生しているような気が…)代わりに、サーバーはクライアントの処理を一手に引き受けないといけないので大変なわけですが、最近はブレードサーバーが使われることがあるようです。クライアント1台にブレードを1枚割り当てる他に、ブレードサーバー仮想マシンをホストして動的に資源を割り振るなど、X端末時代からは着実に進化しているようです。

種類がたくさんあるのに全部「シンクライアント」と呼ばれている上に、大量の宣伝文句に埋もれて分かりにくいわけですが、画面転送型の他にネットワークブート型があります。



さて、そんな画面転送型シンクライアントを、今風に仮想マシン方式で作ってみました。クライアント1台に仮想マシンを1台割り当てます(セキュアですね〜)。
クライアントは専用端末ではなくて、普通のx86マシンを使います(既存のマシンが使えますね〜)。プロトコルはお手軽にVNCです(OS非依存ですね〜)。
図にすると↓こんな感じに。


クライアントはVIVER CORE Serverを使って、DSL(Damn Small Linux)をネットワークブートします。DSLは「大容量のKNOPPIXを逆に極限まで縮めたもの」(Damn Small Linux - Wikipedia)で、X11がインストールされていながら、なんとわずかに50MB。都合の良いことにvncviewerやrdesktopも入っています。

KNOPPIXベースなのでハードウェア自動認識機能も持っていますが、VIVER COREにハードウェア自動認識機能があるので、そのあたりの処理は全部端折ります。DSLはX11がインストールされているOSとしてだけ使います。


前置きはこのくらいに、実際の手順をば。


DSLのルートファイルシステムを取り出す

DSLを落としてきて、VIVER CORE Serverで使えるようにルートファイルシステムを取り出します。DSLのルートファイルシステムはcloopで圧縮されたisoイメージなので、cloop-utilsパッケージに入っているextract_compressed_fsコマンドを使って展開します。最近は各ディストリビューションにもcloop-utilsパッケージが用意されているようで、コマンド一発でインストールできます。良い時代になりました。

# ダウンロード
wget ftp://ftp.oss.cc.gatech.edu/pub/linux/distributions/damnsmall/current/dsl-3.4.4.iso

# isoイメージをループバックマウント
sudo mount dsl-3.4.4.iso /mnt/tmp -o loop

# cloopで圧縮されたルートファイルシステム(isoイメージ)を展開
extract_compressed_fs /mnt/tmp/KNOPPIX/KNOPPIX > dsl-root.iso

# ルートファイルシステムをループバックマウント
sudo mount dsl-root.iso /mnt/tmp2 -o loop

# ルートファイルシステムをtarで固める
sudo tar -C /mnt/tmp2 -cpf dsl.tar .

# tarにカーネルモジュールを追加
sudo tar -C / -rf dsl.tar /lib/modules/`uname-r`

# tarをgzで圧縮
gzip dsl.tar

VIVER COREはルートファイルシステムのホストにrsyncNFSも使えますが、今回はtar.gzで固めたイメージをHTTPでホストすることにしました。


VIVER CORE Serverを準備する

VIVER CORE Serverを落としてきて、先ほど作ったdsl.tar.gzを設置します。今までネットワークブートと言えば、DHCPサーバーが、ブートローダが、カーネルの設定が!と厄介だったわけですが、良い時代になりました ; -)

# ダウンロードして展開
wget http://syuki.skr.jp/core/release/viver-core-server-0.0.5.i386.tar.gz
tar zxvf viver-core-server-0.0.5.i386.tar.gz
cd viver-core-server.i386

# 先ほどのdsl.tar.gzを持ってくる
mv /path/to/dsl.tar.gz ./htdocs/

# カーネルをコピーしてくる
cp /boot/vmlinuz-`uname -r` ./tftpboot/

# viver-coreイメージを作る
./mkviverrd ./tftpboot/viver-core.img ./skel/viver-core-skel.tar.gz `uname -r`


それから ./tftpboot/pxelinux.cfg/default ファイルを書きます。↓こんな感じに。postboot=rsync://... や init=/start あたりがポイント。このあたりの意味は後述。

# ./tftpboot/pxelinux.cfg/default
default viver
prompt 0

label viver
# ここはvmlinuz-`uname -r`に変える↓
kernel vmlinuz-2.6.22.9-union-aufs
append initrd=viver-core.img rooturl=http://192.168.0.2:9000/dsl.tar.gz postboot=rsync://192.168.0.2:9100/client1 vga=791 init=/start panic=10
# ↑ここのIPアドレス(2箇所)は自ホストのIPアドレスに変える
# vga=オプションは必須なので注意

シンクライアント起動スクリプトを書く

クライアントが起動すると同時に、サーバーで起動しているVNCに繋げるようにします。vncviewerをフルスクリーンで起動することで、あたかも目の前のマシンでアプリケーションが動いているように見えます。実際はサーバーで動いているので、USBメモリをさしてもファイルを持ち出せない!などなど。

この仕組みとしては、ブートパラメータで init=/start と指定しているので、通常は起動と同時に /sbin/init が実行されるところが、/startが使われます。ここにvncviewerの起動スクリプトを書いておくことで、起動と同時にサーバーに繋がるようにします。さっきルートファイルシステムはもう作ってしまったのに、また展開するのか!なんてことを心配する必要はありません。VIVER COREの機能を使って、起動時に動的に配置します : -)

というわけで、viver-core-server-0.0.5.i386ディレクトリの中にある、rsyncディレクトリに起動スクリプトを置きます。

# rsyncディレクトリに client1ディレクトリを作る
mkdir ./rsync/client1

# ./rsync/rsyncd.confファイルに2行追加
echo "[client1]" >> ./rsync/rsyncd.conf
echo "        path = ./rsync/client1" >> ./rsync/rsyncd.conf

# ./rsync/client1/start ファイルを書く
vi ./rsync/client1/start


./rsync/client1/startファイルの内容↓

#!/bin/sh
/usr/bin/X11/Xfbdev :1 &
export DISPLAY=localhost:1
/usr/bin/vncviewer 192.168.0.2:11 -shared -fullscreen
#   ここのIPアドレスは↑自ホストのIPアドレスに変える


実行権限を付けるのを忘れずに。

chmod 755 ./rsync/client1/start


これでクライアントの起動時に、自動的にサーバーの11番ディスプレイにVNCで接続されるようになりました。
ちなみに、vncviewerの代わりにrdesktopを使えばRDPが使えるはずです。


仮想マシンを起動

サーバーで動かす仮想マシンを起動します。何を動かそうかなーと迷ったのですが、せっかくDSLが手元にあるので、それを使ってしまいました ^_^;)
もちろん、もっとリッチなopenSUSEでもMandrivaでもWindowsでも、QEMU上で走れば何でもOKです。

↓こんな感じで起動します。-vnc :11 がポイント。そろそろ手抜き加減がバレてきますね…

qemu -cdrom /path/to/dsl-3.4.4.iso -boot d -m 256 -vnc :11

サーバーを起動

VIVER CORE Server関連のサーバープログラムを起動します。VIVER CORE Serverに全部起動スクリプトが入っているので、サクサクコマンドを叩けばOK。

sudo ./start-tftpd
sudo ./start-pxe-pdhcp eth0
./start-httpd
sudo ./start-rsyncd

DHCPサーバーが既にある環境を前提にしています。DHCPサーバーがない場合は、start-pxe-pdhcp の代わりに start-udhcpd)


準備完了

以上で準備完了です。近隣のマシンをPXEでネットワークブートすれば、サーバーで起動している仮想マシンが画面に現れるはずです。


クライアントを増やす

仮想マシン1台につきクライアント1台が対応するので、このままではクライアントを1台しか起動できません。というわけでQEMUをもっと起動して、クライアントを増やします。メモリを大量に消費するわけですが、これが仮想マシン1対1方式のシンクライアントの現実なので :-p


まずは2台目のQEMUを起動します。vncの番号を1つ増やして12におきます。

qemu -cdrom /path/to/dsl-3.4.4.iso -boot d -m 256 -vnc :12


クライアントの起動スクリプトも増やします。

cp -r ./rsync/client1 ./rsync/client2
echo "[client2]" >> ./rsync/rsyncd.conf
echo "        path = ./rsync/client2" >> ./rsync/rsyncd.conf


ここからがポイントで、クライアントごとに違う起動スクリプトが使われるようにする必要があります。通常はクライアントのMACアドレスを調べる必要がありますが、クライアントのIPアドレスDHCPサーバーで固定されている場合は、IPアドレスが使えます。
IPアドレスが使えて、クライアントのIPアドレスが192.168.0.11、192.168.12だとすると、設定ファイルは↓こうなります。

[user@server]$ find ./tftpboot/pxelinux.cfg
./tftpboot/pxelinux.cfg/192.168.0.11
./tftpboot/pxelinux.cfg/192.168.0.12

[user@server]$ cat ./tftpboot/pxelinux.cfg/192.168.0.11
# ./tftpboot/pxelinux.cfg/192.168.0.11の内容
default viver
prompt 0
label viver
kernel vmlinuz-2.6.22.9-union-aufs
append initrd=viver-core.img rooturl=http://192.168.0.2:9000/dsl.tar.gz postboot=rsync://192.168.0.2:9100/client1 vga=791 init=/start panic=10

[user@server]$ cat ./tftpboot/pxelinux.cfg/192.168.0.12
# ./tftpboot/pxelinux.cfg/192.168.0.12の内容(postboot=が違うだけ)
default viver
prompt 0
label viver
kernel vmlinuz-2.6.22.9-union-aufs
append initrd=viver-core.img rooturl=http://192.168.0.2:9000/dsl.tar.gz postboot=rsync://192.168.0.2:9100/client2 vga=791 init=/start panic=10


本当は/startスクリプトで/proc/cmdlineを読み込んで、ブートパラメータを変えるだけでVNCでつなぐ先が変わるようにするのがより良いと思いますが、今回はpostboot=で当てるパッチを変えられるデモを含めてこれで。


これでおわりです。