お久しぶりです。趣味はパケット観察ですとかRubyでもパケット観察とか、ネットワーク系の記事ばっかり書いている、KOBA789です。みなさんゴールデンウィークはいかがでしたでしょうか。私は右目を怪我して連休の半分くらいが憂鬱な療養期間となりました。

さて、桜が目に眩しく希望に満ち溢れた季節である4月は終わり、季節性の病み期が日本各地で頻発する5月となりました。個人的に5月といえばもう夏の気分です。夏です。夏なんです。みなさん、夏といえば何を連想しますか? そうです、**サーバーやネットワーク機器の発熱**です。暑いんです、すごく。冬場は計算のできる暖房として重宝したコンピュータも、夏になると忌々しい熱源へと変貌するわけです。つまり、物理マシンを稼働するのは不快度指数的によろしくありません。そこで仮想化の出番、というわけです。(強引とか言うな!)

KVMXenなどを用いた、いわゆるx86環境の仮想化というものは随所で使い古されたネタですので、今回はネットワークの仮想化をしてみます。

ネットワーク仮想化

ネットワークの仮想化とは、読んで字のごとく、ネットワークを仮想化することです。LANケーブルをスイッチやマシンにつなぐ部分を仮想化するってことですね。仮想マシンを扱う上でネットワークの仮想化もほぼ必須の機能ですから、いろいろなハイパーバイザやOSに実装されています。今回はその中から、OpenIndianaCrossbowというネットワーク仮想化技術を選びました。理由は言わずもがな、筆者がSolaris系のOSを好むからです。(OpenIndianaは旧OpenSolarisで、Solaris系のオープンソースOSです)

各種機構の説明

まず、今回の主役であるネットワーク仮想化機構のCrossbowについて紹介した後、Solaris系独自の計算機仮想化機構であるSolarisコンテナについて紹介します。計算機の仮想化機構はネットワーク仮想化機構と切っても切り離せない関係にあることは想像に難くないと思います。今回はKVMよりも軽量なこの仮想化機構Solarisコンテナを用いてVMを立ち上げ、それらをCrossbowで繋ぎあわせて通信させますので、合わせて紹介します。

Crossbowとは

おおまかに言うと、Solarisに搭載されているネットワーク仮想化機構です。個人的には優れた操作コマンドを持ち、OSとも綺麗に統合されているので、非常に直感的で扱いやすい機能だと感じます。さながら、「ぼくのかんがえたさいきょうのネットワーク仮想化機構」という感じです。

基本的に、仮想NICや仮想スイッチに対する操作はdladmコマンドによって行います。

Solarisコンテナとは

FreeBSDな人にはjail(+VIMAGE)、Linuxな人にはLXCと言えばわかりやすいでしょうか。Solarisコンテナとは、ひとつのSolarisマシン上に複数のSolarisマシンを構築できる技術です(Solarisと書きましたが、今回の場合はOpenIndianaですね)。要はカーネルをホストと共有するタイプの仮想化機構です。chrootにいろいろくっついて高級になったものだと思ってください。

Solarisコンテナではホストを大域ゾーン(Global Zone)、ゲストを非大域ゾーン(Non-Global Zone)と呼びます。(Non-Global Zoneがなぜ”Local Zone”という名前ではないのかに関しては聞かないでください。みんな疑問に思ってるようです)

基本的にゾーンに対する操作はzoneadmzonecfgコマンドによって行います。先ほどのdladmと見比べるとSolaris系のコマンドの命名規則が見えてきそうですね。

OpenIndianaのインストール

まずはOpenIndianaのインストールです。OpenIndiana公式サイトからサーバー用のイメージをダウンロードして適当なメディアに焼き、適当なマシンにインストールします。このとき、IPアドレスの割当てをDHCPにすると後々サービスを止めたりする必要があり面倒なので避けるのが無難です。また私は、前記事で作ったキャプチャツールなどをLinux上で動かすためにKVMを使うつもりなので、仮想マシンではなく、物理マシンにインストールしました。Nested VMにすることもできると思いますが、どうせ遊びつくすならやはり物理マシンでしょう。

以下、OpenIndianaKVMについて余談。(読み飛ばしてもOK)

実は、OpenIndianaのカーネルであるillumosにはJoyent社(Node.jsの開発は主にここがやってる)の力でLinuxからKVMが移植されています。本当に頭がオカシイと思います。この成果の延長線上にSmartOSがあり、それが同社が提供するNode.js向けのPaaSの基盤として利用されています。

いよいよ構築

さて、前置きが長くなりましたが、いよいよ仮想ネットワークを構築してみようと思います。今回は下図のような単純なトポロジを組んでみます。VMの仮想NICvnicをスイッチを介さず直接物理NICにつなげています。こうすると、vnic0vnic1はあたかも物理NICの上のL2スイッチに直接つながっているかのように振舞います。

トポロジ

仮想NICの準備

まずは、仮想ネットワークに接続する仮想NICvnicを準備します。名前はそれぞれvnic0vnic1です。-l オプションによって物理NICe1000g1`へ接続しています。

# dladm create-vnic -l e1000g1 vnic0
# dladm create-vnic -l e1000g1 vnic1

dladm show-linkとすることでリンクの状態を表示できます。今回の環境では以下のようになりました。

LINK        CLASS     MTU    STATE    BRIDGE     OVER
e1000g1     phys      1500   up       --         --
e1000g0     phys      1500   unknown  --         --
vnic0       vnic      1500   up       --         e1000g1
vnic1       vnic      1500   up       --         e1000g1

このマシンには2つの物理NICが搭載されているため、e1000g0というもう1つの物理NICが見えています。

仮想マシンの準備

次に、ネットワークに繋ぐVMの準備です。前述の通り、Solarisコンテナを使います。ZFSプールゾーンを格納するためのZFSファイルシステムを作ります。これから作る非帯域ゾーンはすべてこのファイルシステム内に格納します。

# zfs create rpool/zones
# zfs set mountpoint=/zones rpool/zones

最後にzfs listとすればファイルシステムが追加されているのが確認できると思います。

次に zoneadmコマンドとzonecfgを用いて非帯域ゾーンを作っていきます。まずはテンプレートとなる非帯域ゾーンを作ります。こうすることで、2台目以降はテンプレートをクローンするだけで構築できるので便利です。またクローンはZFSの機能を用いますのでCopy-on-Writeが効いて高速ですし、ディスク容量も節約出来ます。

非帯域ゾーンの設定方法にはzonecfgREPLを用いる方法と、サブコマンドを用いる方法、手順をファイルから読み込む方法の3つがあります。時にはREPLやサブコマンドの手軽さが嬉しいこともあるでしょうが、タイプミスをした時に悲しい気持ちになるので、今回は予め設定内容をファイルに書いておき、それを読み込むことで設定します。以下のような内容のファイルを用意します。ここではsheep.cfgという名前で保存したとして進めます。

create -b
set zonepath=/zones/sheep
set ip-type=exclusive
set autoboot=false
commit

テンプレートになる非帯域ゾーンの格納先は、世界初の哺乳類の体細胞クローンが羊であることにちなんで/zones/sheepとしておきました。テンプレートがインストール後に勝手に起動しても困るのでautobootfalseにします。

では以上のファイルをzonecfgで読み込みましょう。

# zonecfg -z sheep -f sheep.cfg

sheepという名前で非帯域ゾーンの設定が作られました。続いてこれをインストールします。

# zoneadm -z sheep install

パッケージなどをダウンロードしてからインストールするため、少々時間がかかります。テンプレートをクローンする場合にはこの作業が必要ないということからも、テンプレートの利便性が理解できるかと思います。

テンプレートができたので、実際に使うVMも作りましょう。まずは先ほどと同じように設定ファイルを書きます。これはvm0用です。vm0.cfgとして保存しました。

create -b
set zonepath=/zones/vm0
set ip-type=exclusive
set autoboot=false
add net
set physical=vnic0
end
commit

これをvm1.cfgとしてコピーし、一部を変更して以下のようにしたファイルも用意します。

create -b
set zonepath=/zones/vm1
set ip-type=exclusive
set autoboot=false
add net
set physical=vnic1
end
commit

これで2台分の設定ファイルができました。これらをzonecfgにこのファイルを食わせます。

# zonecfg -z vm0 -f vm0.cfg
# zonecfg -z vm1 -f vm1.cfg

ここまではテンプレート用のゾーンを作ったときと同じ手順です。このあと、先ほどはzoneadm installとしましたが、今度はzoneadm cloneとします。先ほどのsheepをクローンすることで素早くゾーンを用意するのです。

# zoneadm -z vm0 clone sheep
# zoneadm -z vm1 clone sheep

先ほどよりも圧倒的に素早くセットアップが完了します。

VMの起動

では、それぞれのVMを起動し、初期設定を行いましょう。起動するときにはzoneadm bootを使います。

# zoneadm -z vm0 boot
# zoneadm -z vm1 boot

VMの初期設定

起動したらコンソールに接続し、初期設定をします。まずはvm0から行います。

# zlogin -C vm0

対話型のインストーラが表示されるので、指示される通りに進めます。また、同様にvm1に関しても設定をします。

# zlogin -C vm1

疎通確認

初期設定が終わったらホストの端末から以下のようにしてvm0へログインします。

host# zlogin vm0

そしてpingvm1へ向けて打ちます。

vm0# ping 10.100.0.2
10.100.0.2 is alive

ここでもし、ping: sendto No route to hostなどと表示された場合はデフォルトルータの設定が間違っている可能性があります。その場合には以下のようにして調べます。

vm0# netstat -rn
Routing Table: IPv4
  Destination           Gateway           Flags  Ref     Use     Interface
-------------------- -------------------- ----- ----- ---------- ---------
10.0.0.0             10.100.0.1           U         4         51 vnic0
127.0.0.1            127.0.0.1            UH        2          0 lo0

この場合、デフォルトルータが設定されていません。設定するには次のようにします。

vm0# route add default 10.0.0.1

ここで、10.0.0.1はデフォルトルータのIPアドレスです。ただし、この設定は揮発性で、再起動により失われるので、/etc/defaultrouterにデフォルトルータのIPアドレスを記してもよいでしょう。

簡易チャット

nc(netcat)でつないで簡易的なチャットをしてみます。vm0vm1の両方で以下のコマンドを入力してncコマンドをインストールします。

vm0/vm1# pkg install netcat

そしてvm0vm1でそれぞれ以下のようにコマンドを実行します。

vm1# nc -l 8124
vm0# nc 10.100.0.2 8124

ここでvm0側のターミナルに何か文字を書き込めば、vm1のターミナルへと送られ、表示されることが確認できると思います。

まとめ

久しぶりに超長い記事を書いて正直疲れました。でもOpenIndianaやそこで使えるSolarisコンテナCrossbowZFSの魅力についてもっと知ってもらいたいと思って頑張ってみました。次回あたり、更にKVMLinuxを動かしてみたいですね。やったことないので不安ですが。それができたら、Rubyで作ったパケットキャプチャツールも動かせますし、Rubyでルータを書いて実験、ということもできそうです。いろいろ夢は広がるので、みなさんも試してみてください!