もっとクールにトポロジが組みたい!
お久しぶりです。趣味はパケット観察ですとかRubyでもパケット観察とか、ネットワーク系の記事ばっかり書いている、KOBA789です。みなさんゴールデンウィークはいかがでしたでしょうか。私は右目を怪我して連休の半分くらいが憂鬱な療養期間となりました。
さて、桜が目に眩しく希望に満ち溢れた季節である4月は終わり、季節性の病み期が日本各地で頻発する5月となりました。個人的に5月といえばもう夏の気分です。夏です。夏なんです。みなさん、夏といえば何を連想しますか? そうです、**サーバーやネットワーク機器の発熱**です。暑いんです、すごく。冬場は計算のできる暖房として重宝したコンピュータも、夏になると忌々しい熱源へと変貌するわけです。つまり、物理マシンを稼働するのは不快度指数的によろしくありません。そこで仮想化の出番、というわけです。(強引とか言うな!)
KVM
やXen
などを用いた、いわゆるx86環境の仮想化というものは随所で使い古されたネタですので、今回はネットワークの仮想化をしてみます。
ネットワーク仮想化
ネットワークの仮想化とは、読んで字のごとく、ネットワークを仮想化することです。LANケーブルをスイッチやマシンにつなぐ部分を仮想化するってことですね。仮想マシンを扱う上でネットワークの仮想化もほぼ必須の機能ですから、いろいろなハイパーバイザやOSに実装されています。今回はその中から、OpenIndiana
のCrossbow
というネットワーク仮想化技術を選びました。理由は言わずもがな、筆者が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”という名前ではないのかに関しては聞かないでください。みんな疑問に思ってるようです)
基本的にゾーン
に対する操作はzoneadm
やzonecfg
コマンドによって行います。先ほどのdladm
と見比べるとSolaris
系のコマンドの命名規則が見えてきそうですね。
OpenIndianaのインストール
まずはOpenIndiana
のインストールです。OpenIndiana公式サイトからサーバー用のイメージをダウンロードして適当なメディアに焼き、適当なマシンにインストールします。このとき、IPアドレスの割当てをDHCP
にすると後々サービスを止めたりする必要があり面倒なので避けるのが無難です。また私は、前記事で作ったキャプチャツールなどをLinux
上で動かすためにKVM
を使うつもりなので、仮想マシンではなく、物理マシンにインストールしました。Nested VM
にすることもできると思いますが、どうせ遊びつくすならやはり物理マシンでしょう。
以下、OpenIndiana
のKVM
について余談。(読み飛ばしてもOK)
実は、OpenIndiana
のカーネルであるillumos
にはJoyent社(Node.js
の開発は主にここがやってる)の力でLinux
からKVM
が移植されています。本当に頭がオカシイと思います。この成果の延長線上にSmartOS
があり、それが同社が提供するNode.js
向けのPaaSの基盤として利用されています。
いよいよ構築
さて、前置きが長くなりましたが、いよいよ仮想ネットワークを構築してみようと思います。今回は下図のような単純なトポロジを組んでみます。VM
の仮想NICvnic
をスイッチを介さず直接物理NICにつなげています。こうすると、vnic0
とvnic1
はあたかも物理NICの上のL2スイッチに直接つながっているかのように振舞います。
仮想NICの準備
まずは、仮想ネットワークに接続する仮想NICvnic
を準備します。名前はそれぞれvnic0
、vnic1
です。-l オプションによって物理NIC
e1000g1`へ接続しています。
# 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
が効いて高速ですし、ディスク容量も節約出来ます。
非帯域ゾーン
の設定方法にはzonecfg
のREPL
を用いる方法と、サブコマンドを用いる方法、手順をファイルから読み込む方法の3つがあります。時にはREPL
やサブコマンドの手軽さが嬉しいこともあるでしょうが、タイプミスをした時に悲しい気持ちになるので、今回は予め設定内容をファイルに書いておき、それを読み込むことで設定します。以下のような内容のファイルを用意します。ここではsheep.cfg
という名前で保存したとして進めます。
create -b
set zonepath=/zones/sheep
set ip-type=exclusive
set autoboot=false
commit
テンプレートになる非帯域ゾーン
の格納先は、世界初の哺乳類の体細胞クローンが羊であることにちなんで/zones/sheep
としておきました。テンプレートがインストール後に勝手に起動しても困るのでautoboot
はfalse
にします。
では以上のファイルを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
そしてping
をvm1
へ向けて打ちます。
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)でつないで簡易的なチャットをしてみます。vm0
とvm1
の両方で以下のコマンドを入力してnc
コマンドをインストールします。
vm0/vm1# pkg install netcat
そしてvm0
とvm1
でそれぞれ以下のようにコマンドを実行します。
vm1# nc -l 8124
vm0# nc 10.100.0.2 8124
ここでvm0
側のターミナルに何か文字を書き込めば、vm1
のターミナルへと送られ、表示されることが確認できると思います。
まとめ
久しぶりに超長い記事を書いて正直疲れました。でもOpenIndiana
やそこで使えるSolarisコンテナ
、Crossbow
、ZFS
の魅力についてもっと知ってもらいたいと思って頑張ってみました。次回あたり、更にKVM
でLinux
を動かしてみたいですね。やったことないので不安ですが。それができたら、Rubyで作ったパケットキャプチャツールも動かせますし、Rubyでルータを書いて実験、ということもできそうです。いろいろ夢は広がるので、みなさんも試してみてください!