2017/06/19

Debian GNU/LinuxのKVMでmacvtapを使用する

Debian GNU/Linux 8.8(jessie)の KVM で仮想マシンのネットワークインターフェースに macvtap を使用するために調べたことと libvirt のネットワークをおさらいした時のメモです。

はじめに

KVM の仮想マシンをホストと同じネットワークに接続するためにホスト OS のネットワークインターフェースをブリッジ化していましたが、Linux kernel の macvlan 機能を使用した macvtap を使うことでホスト OS の負荷が下がりパフォーマンスが良くなるらしいことを知りました。

参照先の文書を眺めただけではいまいちどのようなものか分かりませんでしたが、同じ著者による LXC の連載記事で macvlan について詳しい説明がありました。

これらを参考に手元の KVM 環境で libvirt の仮想ネットワークについて再確認したことと macvlan を試して分かったこをまとめます。

libvirtの仮想ネットワークおさらい

libvirt(virt-manager)で仮想マシンの仮想ネットワークインターフェースのネットワークソースとして指定できる以下の項目について確認します。

  • 仮想ネットワーク(NAT)※
  • 共有デバイス名を指定(ブリッジ)
※virt-manager では「仮想ネットワーク」という用語は NAT 構成で仮想マシンにネットワークアクセスを提供する方法を指すようですが、本記事でそれは「仮想ネットワーク(NAT)」と表記し、「仮想ネットワーク」は仮想マシンのネットワークという一般的な意味で使用します。

確認に使用するネットワーク環境と libvirt のバージョンは以下を想定しています。

  • ホスト OS には物理 NIC がひとつ(eth0)
  • IP アドレスは 192.168.1.X/24
  • デフォルトゲートウェイは 192.168.1.1
# virsh version
Compiled against library: libvirt 1.2.9
Using library: libvirt 1.2.9
Using API: QEMU 1.2.9
Running hypervisor: QEMU 2.1.2

仮想ネットワーク(NAT)

libvirt をインストールすると default という仮想ネットワークが作成されます。具体的には 192.168.122.1/24 のプライベート IP アドレスが割り当てられた virbr0 という仮想ブリッジがホスト OS に作成され、仮想ネットワークから外部へ通信する際に送信元 IP アドレスをホスト OS のものに変換する iptables の MASQUERADE が設定されます。仮想ブリッジに紐ついた dnsmasq プロセスも起動されますが今回は関係ないので説明は省きます。

仮想マシンを起動すると仮想 NIC に対応する疑似 Ethernet デバイス(tap デバイス)が作成され仮想ブリッジに接続されます。ゲスト OS は仮想ブリッジをゲートウェイとすることで外部ネットワークと通信できるようになります。

仮想ネットワーク(NAT)を使用する場合のネットワーク構成は以下のようになります。

仮想マシンのネットワーク定義は以下のようになります。

# virsh dumpxml guest1 | awk '/<interface/,/interface>/'
    <interface type='network'>
      <mac address='52:54:00:74:b4:1e'/>
      <source network='default'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>

仮想マシンを2つ起動した後のネットワークインターフェースの状態。

# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
3: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether fe:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge
4: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT group default qlen 500
    link/ether fe:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 1
    tun
    bridge_slave
5: vnet1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master virbr0 state UNKNOWN mode DEFAULT group default qlen 500
    link/ether fe:54:00:74:b4:1e brd ff:ff:ff:ff:ff:ff promiscuity 1
    tun
    bridge_slave
仮想ブリッジの状態。
# brctl show virbr0
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.fe54003b85b8       yes             vnet0
                                                        vnet1
iptable の状態。
# iptables -nL -v
Chain INPUT (policy ACCEPT 30053 packets, 4614K bytes)
 pkts bytes target     prot opt in     out     source               destination
    4   250 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:53
    0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53
    0     0 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:67
    0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:67

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    2   168 ACCEPT     all  --  *      virbr0  0.0.0.0/0            192.168.122.0/24     ctstate RELATED,ESTABLISHED
    2   168 ACCEPT     all  --  virbr0 *       192.168.122.0/24     0.0.0.0/0
    0     0 ACCEPT     all  --  virbr0 virbr0  0.0.0.0/0            0.0.0.0/0
    0     0 REJECT     all  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable
    0     0 REJECT     all  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable

Chain OUTPUT (policy ACCEPT 27251 packets, 8486K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     udp  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            udp dpt:68
# iptables -t nat -nL -v
Chain PREROUTING (policy ACCEPT 620 packets, 89432 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain INPUT (policy ACCEPT 522 packets, 86192 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 474 packets, 36717 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 474 packets, 36717 bytes)
 pkts bytes target     prot opt in     out     source               destination
    8   551 RETURN     all  --  *      *       192.168.122.0/24     224.0.0.0/24
    0     0 RETURN     all  --  *      *       192.168.122.0/24     255.255.255.255
    0     0 MASQUERADE  tcp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    0     0 MASQUERADE  udp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    2   168 MASQUERADE  all  --  *      *       192.168.122.0/24    !192.168.122.0/24

外部ネットワークからゲスト OS と通信できるようにするには、ホスト OS の特定のネットワークポート宛てのパケットをゲスト OS 宛てに変換する iptables の DNAT を設定したり、仮想ネットワークのゲートウェイとしてホスト OS の IP アドレスを外部のルータなどに設定する必要があります。

共有デバイス名を指定(ブリッジ)

ゲスト OS をサーバ用途で使用する場合に仮想ネットワークが NAT ですと外部ネットワークから利用しづらいですので、ホスト OS の物理 NIC が接続しているネットワークを仮想マシンで共有できるように仮想ブリッジを作成します。

手元の環境ではホスト OS のネットワークインターフェースは1つしかありませんので仮想ブリッジに IP アドレスを割り当てて利用します。

共有デバイス名を指定(ブリッジ)する場合のネットワーク構成は以下のようになります。

仮想ブリッジの作成と物理 NIC の接続、物理 NIC に割り当てていた IP アドレスを仮想ブリッジに割り当てるには以下のようなコマンドを実行します。

# brctl addbr br0
# brctl addif br0 eth0
# ip addr del 192.168.1.X/24 dev eth0
# ip addr add 192.168.1.X/24 dev br0
# ip link set eth0 down
# ip link set eth0 up
# ip link set br0 up
# ip route add default via 192.168.1.1
リモートからの接続で IP アドレスの割り当てを切り替える場合はコマンドを1行にまとめて実行すると少し安心できると思います。
# ip addr del 192.168.1.X/24 dev eth0; ip addr add 192.168.1.X/24 dev br0; ip link set eth0 down; ip link set eth0 up; ip link set br0 up; ip route add default via 192.168.1.1
永続化する場合は /etc/network/interfaces に記載します。
auto br0
iface br0 inet static
  address 192.168.1.X
  netmask 255.255.255.0
  gateway 192.168.1.1
  bridge_ports eth0
  bridge_stp off
  bridge_fd 0
  bridge_waitport 0

また、仮想ブリッジを通過するパケットにも iptables が適用されますので、FORWARD チェーンのデフォルト・ターゲットを DROP にしている場合などは通信を許可する必要があります。

# iptables -A FORWARD -m physdev --physdev-is-bridged -j ACCEPT
仮想ブリッジを通過するパケットに iptables を適用しないようにするにはカーネル・パラメータを変更します。/etc/sysctl.conf に以下を追加して反映します。
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
# sysctl -p

仮想マシンのネットワーク定義は以下のようになります。

# virsh dumpxml guest1 | awk '/<interface/,/interface>/'
    <interface type='bridge'>
      <mac address='52:54:00:3b:85:b8'/>
      <source bridge='br0'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>

仮想マシンを2つ起動した後のネットワークインターフェースの状態。

# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 1
    bridge_slave
3: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge
4: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UNKNOWN mode DEFAULT group default qlen 500
    link/ether fe:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 1
    tun
    bridge_slave
5: vnet1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UNKNOWN mode DEFAULT group default qlen 500
    link/ether fe:54:00:74:b4:1e brd ff:ff:ff:ff:ff:ff promiscuity 1
    tun
    bridge_slave

仮想ブリッジの状態。

# brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.28924a2f0c66       no              eth0
                                                        vnet0
                                                        vnet1

macvlan/macvtapについて

macvlan については以下の記事でわかりやすいです。

macvlan は MAC アドレスベースの仮想 Ethernet デバイスで、その機能を KVM(qemu) 等のソフトウェアから扱えるように tap インターフェースとして実装したものが macvtap のようです。

Linux kernel のコミットメッセージや Kconfig に説明がありましたので引用します。

  • [NET]: Add macvlan driver
    Add macvlan driver, which allows to create virtual ethernet devices
    based on MAC address.
  • macvlan: implement bridge, VEPA and private mode
    This allows each macvlan slave device to be in one
    of three modes, depending on the use case:
    
    MACVLAN_PRIVATE:
      The device never communicates with any other device
      on the same upper_dev. This even includes frames
      coming back from a reflective relay, where supported
      by the adjacent bridge.
    
    MACVLAN_VEPA:
      The new Virtual Ethernet Port Aggregator (VEPA) mode,
      we assume that the adjacent bridge returns all frames
      where both source and destination are local to the
      macvlan port, i.e. the bridge is set up as a reflective
      relay.
      Broadcast frames coming in from the upper_dev get
      flooded to all macvlan interfaces in VEPA mode.
      We never deliver any frames locally.
    
    MACVLAN_BRIDGE:
      We provide the behavior of a simple bridge between
      different macvlan interfaces on the same port. Frames
      from one interface to another one get delivered directly
      and are not sent out externally. Broadcast frames get
      flooded to all other bridge ports and to the external
      interface, but when they come back from a reflective
      relay, we don't deliver them again.
      Since we know all the MAC addresses, the macvlan bridge
      mode does not require learning or STP like the bridge
      module does.
  • net: macvtap driver
    In order to use macvlan with qemu and other tools that require
    a tap file descriptor, the macvtap driver adds a small backend
    with a character device with the same interface as the tun
    driver, with a minimum set of features.
    
    Macvtap interfaces are created in the same way as macvlan
    interfaces using ip link, but the netif is just used as a
    handle for configuration and accounting, while the data
    goes through the chardev. Each macvtap interface has its
    own character device, simplifying permission management
    significantly over the generic tun/tap driver.
  • Kconfig\net\drivers - kernel/git/torvalds/linux.git - Linux kernel source tree
    config MACVLAN
    	tristate "MAC-VLAN support"
    	---help---
    	  This allows one to create virtual interfaces that map packets to
    	  or from specific MAC addresses to a particular interface.
    
    	  Macvlan devices can be added using the "ip" command from the
    	  iproute2 package starting with the iproute2-2.6.23 release:
    
    	  "ip link add link <real dev> [ address MAC ] [ NAME ] type macvlan"
    
    	  To compile this driver as a module, choose M here: the module
    	  will be called macvlan.
    
    config MACVTAP
    	tristate "MAC-VLAN based tap driver"
    	depends on MACVLAN
    	depends on INET
    	select TAP
    	help
    	  This adds a specialized tap character device driver that is based
    	  on the MAC-VLAN network interface, called macvtap. A macvtap device
    	  can be added in the same way as a macvlan device, using 'type
    	  macvtap', and then be accessed through the tap user space interface.
    
    	  To compile this driver as a module, choose M here: the module
    	  will be called macvtap.

仮想ネットワークのブリッジ接続にmacvtapを使用する

仮想ネットワーク(NAT)や共有デバイス(ブリッジ)と異なり、macvtap を仮想ネットワークのブリッジ接続に使用する上で特別な準備は必要ありません。

macvtap が実装された Linux kernel バージョン 2.6.34 以上であれば使用可能です。このバージョンのリリースは 2010年 5月のようですので、主要な Linux ディストリビューションであれば最新、またはそれよりも前のバージョンでも対応していると思われます。

後述の作業で ip コマンドを使用して macvlan インターフェースを作成しますので iproute2 パッケージはバージョン 2.6.23 以上のものが必要です。

手元の環境(Debian/GNU Linux 8)はどちらも満たしています。

# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
# uname -srv
Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.43-2 (2017-04-30)
# apt-show-versions iproute2
iproute2:amd64/jessie 3.16.0-2 uptodate

macvlan はインタフェースに複数の MAC アドレスを割り当てる機能ですので仮想ブリッジに設定することも出来ますが、メリットがなさそうなのでシンプルに物理 NIC に設定します。

ホストデバイス(macvtap)を使用する場合のネットワーク構成は以下のようになります。

ここでは仮想ネットワークを共有デバイス(ブリッジ)からホストデバイス(macvtap)に切り替えることを想定します。まず仮想ブリッジに割り当てた IP アドレスを物理 NIC に戻すために以下のようなコマンドを実行します。仮想ブリッジは不要になるので削除します。

# ip addr del 192.168.1.X/24 dev br0
# ip addr add 192.168.1.X/24 dev eth0
# brctl delif br0 eth0
# ip link set br0 down
# brctl delbr br0
# ip link set eth0 down
# ip link set eth0 up
# ip route add default via 192.168.1.1
仮想ブリッジから物理 NIC に IP アドレスを割り当てなおす場合は仮想ブリッジから eth0 を切断するまでは通信ができなくなりますので、リモートから接続している場合はコマンドを1行でまとめて実行すると安心できます。
# ip addr del 192.168.1.X/24 dev br0; ip addr add 192.168.1.X/24 dev eth0; brctl delif br0 eth0; ip link set br0 down; brctl delbr br0; ip link set eth0 down; ip link set eth0 up; ip route add default via 192.168.1.1

仮想マシンのネットワーク定義は以下のようになります。

# virsh dumpxml guest1 | awk '/<interface/,/interface>/'
    <interface type='direct'>
      <mac address='52:54:00:3b:85:b8'/>
      <source dev='eth0' mode='bridge'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>

仮想マシンを2つ起動した後のネットワークインターフェースの状態。

# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
3: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge
4: macvtap1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:74:b4:1e brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge
macvlan/macvtap の状況を確認する方法は分かりませんが、カーネルモジュールが読み込まれている事は確認できます。
# lsmod | grep ^macv
macvtap                17526  5 vhost_net
macvlan                18315  1 macvtap
# modinfo macvlan
filename:       /lib/modules/3.16.0-4-amd64/kernel/drivers/net/macvlan.ko
alias:          rtnl-link-macvlan
description:    Driver for MAC address based VLANs
author:         Patrick McHardy <kaber@trash.net>
license:        GPL
depends:
intree:         Y
vermagic:       3.16.0-4-amd64 SMP mod_unload modversions
# modinfo macvtap
filename:       /lib/modules/3.16.0-4-amd64/kernel/drivers/net/macvtap.ko
license:        GPL
author:         Arnd Bergmann <arnd@arndb.de>
alias:          rtnl-link-macvtap
depends:        macvlan
intree:         Y
vermagic:       3.16.0-4-amd64 SMP mod_unload modversions

macvtap使用時の考慮事項1. ホストOSとゲストOSが通信できない(回避策あり)

仮想ネットワークに共有デバイス名を指定(ブリッジ)した際には、仮想ブリッジに接続したネットワークインターフェースはホスト OS では利用できませんでした。しかし macvtap を設定した場合は MAC アドレスが異なりますのでホスト OS のネットワークインターフェースは利用可能です。ただし virt-manager で警告があるとおり、そのインターフェースを通じてホスト OS とゲスト OS は直接の通信ができません。

Red Hat Enterprise Linux のマニュアルにも記載があります。

私が理解しているイメージが以下になります。macvlan の bridge モードが有効となっているインターフェースでひとつの VLAN のようなものが構成されていますので、そこに接続していないホスト OS のネットワークインターフェースからではゲスト OS に通信できません。

ゲスト OS が外部ネットワークと通信できれば困らないかも知れませんが少し不便です。そこでホスト OS のインターフェースを macvlan の bridge として作成することでゲスト OS との通信が可能になるよ、という事が書かれていた記事が冒頭に紹介したものです。

その場合の構成は以下のようなイメージになるでしょうか。

物理 NIC(eth0)に macvlan インターフェース(macvlan0)をブリッジモードで作成して IP アドレスを割り当てます。

# ip link add dev macvlan0 link eth0 type macvlan mode bridge
# ip addr del 192.168.1.X/24 dev eth0
# ip addr add 192.168.1.X/24 dev macvlan0
# ip link set macvlan0 up
# ip route add default via 192.168.1.1 dev macvlan0
例によってリモート接続から実行する場合はコマンドを1行にまとめて実行します。
# ip addr del 192.168.1.X/24 dev eth0; ip addr add 192.168.1.X/24 dev macvlan0; ip link set macvlan0 up; ip route add default via 192.168.1.1 dev macvlan0

永続化する場合は /etc/network/interfaces に記載します。

auto eth0
iface eth0 inet manual

auto macvlan0
iface macvlan0 inet static
  address 192.168.1.X
  netmask 255.255.255.0
  gateway 192.168.1.1
  pre-up ip link set eth0 up
  pre-up ip link add link eth0 name $IFACE type macvlan mode bridge
  post-down ip link delete $IFACE type macvlan

仮想マシンを2つ起動した後のネットワークインターフェースの状態。

# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
3: macvlan0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether ae:69:46:86:b0:02 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvlan  mode bridge
4: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge
5: macvtap1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:74:b4:1e brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge
外部ネットワークから見たホスト OS のインターフェースは macvlan で物理 NIC とは異なる MAC アドレスになりますので、運用管理面で使用できないケースがあるかも知れません。その場合はホスト OS からゲスト OS にアクセスするためのネットワークインターフェースを別途追加するか、ゲスト OS でシリアルコンソール接続の設定をするなどの対応が必要になります。

macvtap使用時の考慮事項2. NetworkManagerとの相性が悪い

手元の環境では macvtap を設定した仮想マシンを一度停止すると、次回起動時に macvtap インターフェースがリンクアップしない事象が発生しました。

以下は仮想マシンを初回に起動した時の NetworkManager のログとホスト OS のインターフェースの状態。これは問題ありません。

<info> (macvtap0): link connected
<info> (macvtap0): carrier is ON
<info> (macvtap0): new Macvlan device (driver: 'unknown' ifindex: 3)
<info> (macvtap0): exported as /org/freedesktop/NetworkManager/Devices/2
<info> devices added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0)
<info> device added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0): no ifupdown configuration found.
<info> (macvtap0): device state change: unmanaged -> unavailable (reason 'connection-assumed') [10 20 41]
<info> (macvtap0): device state change: unavailable -> disconnected (reason 'connection-assumed') [20 30 41]
<info> Activation (macvtap0) starting connection 'macvtap0'
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) scheduled...
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) started...
<info> (macvtap0): device state change: disconnected -> prepare (reason 'none') [30 40 0]
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) scheduled...
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) complete.
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) starting...
<info> (macvtap0): device state change: prepare -> config (reason 'none') [40 50 0]
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) successful.
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) scheduled.
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) complete.
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) started...
<info> (macvtap0): device state change: config -> ip-config (reason 'none') [50 70 0]
<info> Activation (macvtap0) Stage 5 of 5 (IPv6 Commit) scheduled...
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) complete.
<info> Activation (macvtap0) Stage 5 of 5 (IPv6 Commit) started...
<info> (macvtap0): device state change: ip-config -> ip-check (reason 'none') [70 80 0]
<info> Activation (macvtap0) Stage 5 of 5 (IPv6 Commit) complete.
<info> (macvtap0): device state change: ip-check -> secondaries (reason 'none') [80 90 0]
<info> (macvtap0): device state change: secondaries -> activated (reason 'none') [90 100 0]
<info> Activation (macvtap0) successful, device activated.
ネットワークリンクの状態。
# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
3: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge

仮想マシン停止時の NetworkManager のログ。

<info> (macvtap0): device state change: activated -> unmanaged (reason 'removed') [100 10 36]
<info> (macvtap0): deactivating device (reason 'removed') [36]
<info> devices removed (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0)

次に問題となる仮想マシンを2回目に起動した時の NetworkManager のログとネットワークリンクの状態。warn レベルのメッセージとして ip-config-unavailable が出力されて macvtap インターフェースがリンクアップしません。

<info> (macvtap0): link connected
<info> (macvtap0): carrier is ON
<info> (macvtap0): new Macvlan device (driver: 'unknown' ifindex: 4)
<info> (macvtap0): exported as /org/freedesktop/NetworkManager/Devices/3
<info> devices added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0)
<info> device added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0): no ifupdown configuration found.
<info> (macvtap0): found matching connection 'macvtap0'
<info> (macvtap0): device state change: unmanaged -> unavailable (reason 'connection-assumed') [10 20 41]
<info> (macvtap0): device state change: unavailable -> disconnected (reason 'connection-assumed') [20 30 41]
<info> Activation (macvtap0) starting connection 'macvtap0'
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) scheduled...
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) started...
<info> (macvtap0): device state change: disconnected -> prepare (reason 'none') [30 40 0]
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) scheduled...
<info> Activation (macvtap0) Stage 1 of 5 (Device Prepare) complete.
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) starting...
<info> (macvtap0): device state change: prepare -> config (reason 'none') [40 50 0]
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) successful.
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) scheduled.
<info> Activation (macvtap0) Stage 2 of 5 (Device Configure) complete.
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) started...
<info> (macvtap0): device state change: config -> ip-config (reason 'none') [50 70 0]
<info> (macvtap0): device state change: ip-config -> failed (reason 'ip-config-unavailable') [70 120 5]
<warn> Activation (macvtap0) failed for connection 'macvtap0'
<info> Activation (macvtap0) Stage 3 of 5 (IP Configure Start) complete.
<info> (macvtap0): device state change: failed -> disconnected (reason 'none') [120 30 0]
<info> (macvtap0): deactivating device (reason 'none') [0]
<info> (macvtap0): device state change: disconnected -> unmanaged (reason 'none') [30 10 0]
<info> (macvtap0): link disconnected
# ip -d link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 28:92:4a:2f:0c:66 brd ff:ff:ff:ff:ff:ff promiscuity 0
4: macvtap0@eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 500
    link/ether 52:54:00:3b:85:b8 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvtap  mode bridge

検索してみると同じような事象がみつかりました。

NetworkManager を再起動すればこの状態(一度リンクダウンした macvtap がリンクアップしない)はリセットされるようですが、仮想マシンを2度目に起動するたびに NetworkManager を再起動するのは何か違いますので回避策を探します。

回避策1. NetworkManagerを無効にする

試した限りこれが一番確実です。

# systemctl stop NetworkManager
# systemctl disable NetworkManager
ですが少し乱暴な気がしますので NetworkManager を無効にする以外の方法を探します。

回避策2. IPv6を無効にする

ip-config-unavailable で検索すると以下の記事が見つかりました。

これにならって IPv6 を無効にしてみると仮想マシンを起動した時の NetworkManager のメッセージがずいぶんシンプルになり、2度目の起動でも問題なく macvtap インターフェースがリンクアップするようになりました。
# sysctl net.ipv6.conf.all.disable_ipv6=1
NetworkManager のログ。
<info> (macvtap0): link connected
<info> (macvtap0): carrier is ON
<info> (macvtap0): new Macvlan device (driver: 'unknown' ifindex: 3)
<info> (macvtap0): exported as /org/freedesktop/NetworkManager/Devices/2
<info> devices added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0)
<info> device added (path: /sys/devices/virtual/net/macvtap0, iface: macvtap0): no ifupdown configuration found.
IPv6 を使用していない環境であれば割り切って無効にしてしまっても良いのかも知れません。

回避策3. macvtapインターフェースをNetworkManagerで管理しないようにする

NetworkManager では管理しないインターフェースを定義できるようですので、そこに macvtap インターフェースを指定します。

/etc/NetworkManager/NetworkManager.conf の [keyfile] セクションに unmanaged-devices として記述すればよいようです。使用しているバージョンが異なるせいかマニュアルに記載してあるワイルドカードが機能しませんでしたので利用する可能性のある macvtap インターフェースの数だけ列挙しています。
# NetworkManager --version
0.9.10.0
[keyfile]
unmanaged-devices=interface-name:macvtap0;interface-name:macvtap1;interface-name:macvtap2;interface-name:macvtap3;interface-name:macvtap4;interface-name:macvtap5
ちなみに Debian/GNU Linux の場合は /etc/network/interfaces ファイルに記載のあるインターフェースは NetworkManager の管理対象外となるようです。

気になる性能

仮想ネットワークに macvtap を使用した場合に仮想ブリッジと比較してどの程度の差があるのか iperf コマンド実行時のリソース使用状況を Munin で確認してみました。

確認環境

手元に KVM ホスト以外で使用できそうな端末が NAS しかないので、NAS にパッケージ(ipkg)として提供されていた iperf を使用して KVM ゲストを iperf サーバに、NAS を iperf クライアントとして実行しました。

  • KVM ホスト
    • Debian GNU/Linux 8 (linux kernel 3.16.0-4-amd64)
    • CPU: AMD Turion™ II NEO N54L(2.2GHz)
    • NIC: Broadcom NetXtreme BCM5723 Gigabit Ethernet(tg3)
  • iperfサーバ(KVM ゲスト)
    • vCPU 1
    • Debian GNU/Linux 8 (linux kernel 3.16.0-4-amd64)
    • iperf 2.0.5
  • iperfクライアント
    • QNAP QTS 4.2.6 (linux kernel 3.4.6)
    • iperf 2.0.4 (ipkg)
iperf のオプションは特に設定せず、デフォルトを使用しました。
# iperf -s
クライアントではひたすら一定期間(約50分)サーバに対して計測を繰り返します。
# while :; do iperf -c 192.168.1.XX; done
------------------------------------------------------------
Client connecting to 192.168.1.XX, TCP port 5001
TCP window size: 22.5 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.YY port 56409 connected with 192.168.1.XX port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes    932 Mbits/sec

リソース使用状況

グラフ右側の 12:00 くらいから、左から順に iperf サーバを起動した時の条件は次のとおり。

  1. KVM ゲスト(br0)
  2. KVM ゲスト(macvtap)
  3. KVM ゲスト(br0)※vhost-net zero-copy無効
  4. KVM ゲスト(macvtap)※vhost-net zero-copy無効
  5. KVM ホスト(eth0)
以下のような傾向にありました。
  • CPU 使用率は macvtap 利用時のほうがわずかに user% が多いように見えますが guest% などと合わせると同じ程度。ホストが忙しいとグラフが欠けるのが謎(4時頃はバックアップ処理が動作中)
  • ネットワーク転送速度は、どのパターンでも 900MB/s ほど出ています
  • 割り込みとコンテキストスイッチは macvtap のほうが多い
仕組み上、ソフトウェアブリッジより macvtap のほうがコンテキストスイッチなどが減ってリソース消費は少なくなると思っていたのですが、リソースの乏しい手元の検証環境ではそれほど macvtap に優位な点は見られませんでした。

おわりに

仮想環境を扱う上でネットワーク仮想化は非常に重要なコンポーネントですが、 virt-manager でエイヤっ!と作っていてあまり理解していなかったところを macvtap の動作を確認するついでに復習できました。今後は Open vSwitch による仮想ブリッジや各種オーバーレイネットワークを調べてみようと思います。

参考資料

文中にも幾つか記載しましたが、分かった気になるまで何度も参照した資料をまとめます。

  1. Slides | LinuxCon Japan 2014 | Linux Conferences and Linux Events | The Linux Foundation
  2. Internet Week 2012 D1 パケットフォワーディングを支える技術 - JPNIC
    • 極める!KVM
      KVM 環境における物理 NIC と仮想 NIC の接続方法(bridge/openvswitch/macvtap)と仮想 NIC の実装(e1000/virtio-net/vhost-net)の説明とそれぞれの組合せでの性能計測。
  3. Internet Week 2011 S9 仮想化時代のパケットフォワーディング - JPNIC
  4. 仮想化環境におけるパケットフォワーディング
    仮想 NIC の実装毎の送受信処理や仮想ネットワークの構成の図がとても理解の助けになりました
  5. KVM徹底入門 Linuxカーネル仮想化基盤構築ガイド
  6. プロのためのLinuxシステム・ネットワーク管理技術
  7. Virtualized bridged networking with MacVTap - Seravo
  8. Some notes on macvlan/macvtap « \1
  9. TipAndDoc/network/macvlan – lab

1 件のコメント:

  1. 勉強になる情報をご紹介いただきありがとうございます。

    当方も記事にご紹介されているようなものと同じセットアップでMacvlan/macvtapの動きの検証をしているのですが、動きを確認出来ない箇所が2点ありまして、お知恵をお借りできませんでしょうか。


    (1) macvtapがns内から自分自身へPingを飛ばせない
    ip link add br-macvlan0 link eth0 type macvlan mode bridge
    ip addr add 192.168.1.X1/24 dev br-macvlan0
    ip link set br-macvlan0 up
    ip link add macvtap0 link br-macvlan0 type macvtap mode bridge
    ip netns add test
    ip link set macvtap0 netns test
    ip netns exec test ip link set macvtap0 up
    ip netns exec test ip addr add 192.168.1.X2/24 dev macvtap0
    ip netns exec test ip route add 192.168.1.0/24 dev macvtap0

    (2)KVMのゲスト(Windows7)にMacvtapインターフェースをアサインし、ホストのMacvlanブリッジとの
    通信を試みたものの、ゲストでもルーティングを行っており、同一セグメントで異なるアドレスにもかかわらず通信できていない。

    よろしくお願いします。

    返信削除