Xenで複雑なネットワークを作る

せっかくサーバを仮想化しているのだから、ネットワークも仮想化して自由に構成したい。データセンターではセキュリティや管理の為にセグメントを分けるのは常識だし、クラスタ間は専用のネットワークを繋ぐ前提で作られているソフトウェアも多い。このネットワークシステムを、1台のサーバ内で仮想的に構築したい。

まずXenのネットワークの基礎を説明する。Xenでは仮想ネットワークは下記のような構造になる。

            peth0  vif0.0      eth0
[物理I/F]-----(xenbr0)----------[Domain0]
                |   |vif1.0    eth0
                |   +-----------[Domain-U]
                |vif2.0        eth0
                +---------------[Domain-U]

Domain0(実サーバ)やDomain-U(仮想サーバ)から見ると、自分が使っているインターフェースはおなじみのeth0という名前だが、その間はXenが作った仮想ブリッジ(xenbr0)でつながっている。また物理インターフェースも実サーバに直接つながっているわけではなく、仮想ブリッジにつながっている。こうすることで、仮想サーバも物理インターフェースから直接外部に出ることができるのである。

peth0、vif0.0、vif1.0などは仮想ブリッジ側のインターフェースであり、IPアドレスは付与しない。vif x.yのxは仮想サーバのIDをさしており、yは仮想サーバ側から見たインターフェース番号をあらわしている。つまり仮想サーバ1に2つのインターフェースを持たせてそれぞれeth0、eth1とすると、それにつながる仮想ブリッジのポートはvif1.0、vif1.1となる。なお仮想サーバのIDは起動するごとに変わるので不定である。

物理インターフェースが2つある場合は、単純に下記のようになる。

            peth0  vif0.0      eth0   eth1    vif1.0   peth1
[物理I/F]-----(xenbr0)----------[Domain0]----------(xenbr1)-----[物理I/F]
                |   |                               |   |
                |   |vif1.0    eth0   eth1    vif1.1|   |
                |   +-----------[DomainU]-----------+   |
                |vif2.0                           vif2.0|
                |              eth0   eth1              |
                +---------------[DomainU]---------------+

物理インターフェースが1つしかないが仮想サーバで2つのインターフェースを使いたい場合はdummyインターフェースを作成する。

            peth0  vif0.0      eth0   dummy0    vif1.0   peth1
[物理I/F]-----(xenbr0)----------[Domain0]----------(xenbr1)
                |   |                               |   |
                |   |vif1.0    eth0   eth1    vif1.1|   |
                |   +-----------[DomainU]-----------+   |
                |vif2.0                           vif2.0|
                |              eth0   eth1              |
                +---------------[DomainU]---------------+

Domain0のdummy0インターフェースにはIPを付与して仮想サーバと通信をすることも出来るし、IPを付与せず仮想サーバ同士の通信だけをさせることも出来る。

この考え方を元に、DMZや仮想サーバ間専用リンクを持った複雑なネットワークを作ってみる。なおvif名は設定上関係ないので省略する。

            peth1              eth1   eth0               peth0
[物理I/F]-----(xenbr1)----------[Domain0]----------(xenbr0)-----[物理I/F]
                                    |dummy0         | | | |    eth0
                                    |               | | | +-----[ns1]
                                    |               | | |      eth0
                                    |               | | +-------[ns2]
                                    |               | |        eth0
                                    |               | +---------[storage1]
                                    |               |          eth0
                                (xenbr2)            +-----------[manager1]
                                 |    |
                                 |    | eth0  eth1             eth0
                                 |    +---[lvs1]-----+   +------[www1]
                                 |          |eth2    |   |
                                 |       (xenbr4)   (xenbr3)
                                 |          |eth2    |   |     eth0
                                 +--------[lvs2]-----+   +------[www2]
                                        eth0  eth1

xenbr2~4はDMZに相当する。図では示していないがDomain0はxenbr3、xenbr4にdummy1、dummy2でつながっている。

①dummyインターフェースを複数使用可能にするため、カーネルモジュールの設定をする。またxenbrがループバックデバイスを消費し、デフォルト数の4を越えてしまうので増やしておく。(この2行の設定に行き着くまでにまる一日かかった...)

[root@ml115 ~]# vi /etc/modprobe.conf
...
options dummy numdummies=3
netloop nloopbacks=5
...

ここでいちど再起動をしたほうがいいと思う。

②dummyインターフェースの設定を作成し、起動させる。

[root@ml115 ~]# vi /etc/sysconfig/network-script/ifcfg-dummy0
DEVICE=dummy0
BROADCAST=192.168.2.255
HWADDR=00:16:3E:2B:02:fe
IPADDR=192.168.2.254
NETMASK=255.255.255.0
NETWORK=192.168.2.0
ONBOOT=yes

[root@ml115 ~]# ifconfig dummy0 up
dummy1、dummy2も同様

③Xenのネットワーク設定をする

/etc/xen/scripts/に下記の内容のファイルを作成する。

[root@ml115 ~]# vi /etc/xen/scripts/network-bridge.wrap
#!/bin/sh
# Exit if anything goes wrong.
set -e

# First arg is the operation.
OP=$1
shift

script=/etc/xen/scripts/network-bridge

case ${OP} in
  start)
        $script start vifnum=0 bridge=xenbr0 netdev=eth0
        $script start vifnum=1 bridge=xenbr1 netdev=eth1
        $script start vifnum=2 bridge=xenbr2 netdev=dummy0
        $script start vifnum=3 bridge=xenbr3 netdev=dummy1
        $script start vifnum=4 bridge=xenbr4 netdev=dummy2
        ;;

  stop)
        $script stop vifnum=0 bridge=xenbr0 netdev=eth0
        $script stop vifnum=1 bridge=xenbr1 netdev=eth1
        $script stop vifnum=2 bridge=xenbr2 netdev=dummy0
        $script stop vifnum=3 bridge=xenbr3 netdev=dummy1
        $script stop vifnum=4 bridge=xenbr4 netdev=dummy2
        ;;

  status)
        $script status vifnum=0 bridge=xenbr0 netdev=eth0
        $script status vifnum=1 bridge=xenbr1 netdev=eth1
        $script status vifnum=2 bridge=xenbr2 netdev=dummy0
        $script status vifnum=3 bridge=xenbr3 netdev=dummy1
        $script status vifnum=4 bridge=xenbr4 netdev=dummy2
        ;;

  *)
        echo 'Unknown command: ' ${OP}
        echo 'Valid commands are: start, stop, status'
        exit 1
esac

次に/etc/xen/xend-config.sxpを編集する

...
(network-script network-bridge)
...
↓↓↓
...
(network-script network-bridge.wrap)
...

④各仮想サーバ定義ファイルのネットワーク設定をする。いじるのはvif=の行。ちなみにMACアドレスを重複しないように決めるのが大変なので、IPアドレスの下2オクテットを16進数にしてMACアドレスの下2オクテットとして使っている。

[root@ml115 xen]# vi /etc/xen/lvs1
name = "lvs1"
memory = "256"
disk = [ 'tap:aio:/var/lib/xen/images/lvs1,xvda,w', ]
vif = [ 'mac=00:16:3e:2b:02:02, bridge=xenbr2', 'mac=00:16:3e:2b:03:fc, bridge=xenbr3', 
'mac=00:16:3e:2b:04:01, bridge=xenbr4'] ←前の行の続き。間で改行しない。
vfb = ["type=vnc,vncunused=1"]  
uuid = "f83c4a0b-4831-f424-d341-ccf824e91410"
bootloader="/usr/bin/pygrub"
vcpus=1
on_reboot   = 'restart'
on_crash    = 'restart'

他の仮想サーバも同様に設定する。

⑤各サーバを立ち上げてネットワーク設定をする

[root@lvs1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0
# Xen Virtual Ethernet
DEVICE=eth0
BROADCAST=192.168.2.255
HWADDR=00:16:3E:2B:02:02
IPADDR=192.168.2.2
NETMASK=255.255.255.0
NETWORK=192.168.2.0
ONBOOT=yes
[root@lvs1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth1
# Xen Virtual Ethernet
DEVICE=eth1
BROADCAST=192.168.3.255
HWADDR=00:16:3E:2B:03:FC
IPADDR=192.168.3.252
NETMASK=255.255.255.0
NETWORK=192.168.3.0
ONBOOT=yes
[root@lvs1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth2
# Xen Virtual Ethernet
DEVICE=eth2
BROADCAST=192.168.4.255
HWADDR=00:16:3E:2B:04:01
IPADDR=192.168.4.1
NETMASK=255.255.255.0
NETWORK=192.168.4.0
ONBOOT=yes
[root@lvs1 ~]#

ここで仮想サーバを起動していたら

Error: destroyDevice() takes exactly 3 arguments (2 given)

などというエラーがでて起動しなくなってしまった。これを追っかけていって上記のnetloop nloopbacks=5にたどり着いた次第。

⑥Domain0のiptablesを設定する

Domain0からみると新しいインターフェースdummy0ができるので、その先からの通信を許可する必要がある。ここはDMZなので本当は厳しくフィルタをする必要があるが、まずはテストなのですべて許可にしてしまう。

[root@ml115 ~]# vi /etc/sysconfig/iptables
...
-A RH-Firewall-1-INPUT -i dummy0 -j ACCEPT
...

[root@ml115 ~]# service iptables reload

⑦通信確認

構成図のlvs1とlvs2が出来上がったら、相互にpingを打って通信確認をしよう。

[root@lvs1 ~]# ping 192.168.2.3
PING 192.168.2.3 (192.168.2.3) 56(84) bytes of data.
64 bytes from 192.168.2.3: icmp_seq=1 ttl=64 time=0.968 ms
64 bytes from 192.168.2.3: icmp_seq=2 ttl=64 time=0.384 ms
64 bytes from 192.168.2.3: icmp_seq=3 ttl=64 time=0.451 ms

--- 192.168.2.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.384/0.601/0.968/0.260 ms
[root@lvs1 ~]# ping 192.168.3.253
PING 192.168.3.253 (192.168.3.253) 56(84) bytes of data.
64 bytes from 192.168.3.253: icmp_seq=1 ttl=64 time=0.438 ms
64 bytes from 192.168.3.253: icmp_seq=2 ttl=64 time=0.368 ms
64 bytes from 192.168.3.253: icmp_seq=3 ttl=64 time=0.432 ms

--- 192.168.3.253 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.368/0.412/0.438/0.039 ms
[root@lvs1 ~]# ping 192.168.4.2
PING 192.168.4.2 (192.168.4.2) 56(84) bytes of data.
64 bytes from 192.168.4.2: icmp_seq=1 ttl=64 time=0.642 ms
64 bytes from 192.168.4.2: icmp_seq=2 ttl=64 time=0.476 ms
64 bytes from 192.168.4.2: icmp_seq=3 ttl=64 time=0.428 ms

--- 192.168.4.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.428/0.515/0.642/0.093 ms
[root@lvs1 ~]#