wiki:linux/kvm

KVMメモ

このページは、kvmをlibvirt(virsh)で使いたい人向けのメモです。簡単に使いたい人は、virt-managerを使ってください。

準備

Fedora/CentOS

kvmを利用するのに、libvirtを利用します。libvirtをインストールしておきます。virt-managerをインストールすると必要なパッケージが一通り入って便利です。また、kvmで仮想マシンを実行するのに必要なqemu-kvmもインストールしておきます。

# yum install virt-manager
# yum install qemu-kvm

次に、kvmのモジュールをカーネルにロードします。AMDの場合は、kvm_intelの代わりにkvm_amdを使ってください。

# modprobe kvm
# modprobe kvm_intel

libvirtを利用するには、libvirtdの起動が必要です。libvirtdを起動しておきます。

# /etc/init.d/libvirtd start

その他、下記のパッケージを入れておくとよいかもしれません。

# yum install virt-top
# yum install qemu-kvm-tools

CentOS6.4を利用する場合、下記のフォルダにあるqemu-kvm-rhev(RHEV用のパッケージ)を利用するとよいです。スナップショットなどの標準のqemuでは利用できない機能が利用できます。

Ubuntu

# apt-get install qemu-kvm virt-manager
# apt-get install virt-top

Ubuntu 12.04 LTSのqemu/libvirtの最新パッケージは、Ubuntu Cloud Archiveにあります。 /etc/apt/source.listに下記のapt-lineを追加してqemu/libvirt-binパッケージをインストールしてください。

deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/havana main

virshを利用したKVM仮想マシンの作り方

イメージ作成

# qemu-img create -f raw ubuntu.img 10G

libvirt XMLファイルの作成方法

下記のXMLを適当に編集。特に、

  • name(他の仮想マシンとユニークな名前になるように)
  • memory(メモリ容量)
  • vcpu(CPUの数)
  • diskとcdromのsource file
  • acpiを有効にしないと、virshからshutdownコマンドでシャットダウンできない。
  • mac addressを他と被らないように

辺りに注意しておく。下記は、Fedora13の例。

<domain type='kvm' id='3'>
  <name>UbuntuVM</name>
  <memory>524288</memory>
  <currentMemory>524288</currentMemory>
  <vcpu>1</vcpu>
  <os>
    <type arch='x86_64' machine='pc'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>

  <features>
    <acpi />
  </features> 

  <clock offset='utc'/>
  <devices>
    <emulator>/usr/bin/qemu-kvm</emulator>
    
    <!-- DISKの定義 -->
    <disk type='file' device='disk'>  
      <driver name='qemu' type='raw'io='native'/>
      <source file='/mnt/sda5/ubuntu.img'/>
      <target dev='hda' bus='ide'/>
    </disk>
    
    <!-- CDROMの定義 -->
    <disk type='file' device='cdrom'>
      <driver name='qemu'/>
      <source file='/mnt/sda5/iso/ubuntu-ja-10.04-desktop-i386-20100512.iso'/>
      <target dev='hdc' bus='ide'/>
      <readonly/>
    </disk>
    
    <!-- ネットワークインタフェースの定義 -->       
    <interface type='network'>
      <mac address='52:54:00:1d:18:5e'/>
      <source network='default'/>
      <target dev='vnet0'/>  
    </interface>
    
    <input type='mouse' bus='ps2'/>
    <graphics type='vnc' port='5902' keymap='ja'/>
  </devices>

</domain>

Ubuntuの場合は、typeタグのmachineをpcなどにする。

...
<type arch='x86-64' machine='pc'>hvm</type>
...

virshに登録して起動

# virsh  (virsh実行。以下、virshのコンソールで実行)
virsh # define virt-kvm.xml

defineで仮想マシンを定義したXMLを登録すると、/etc/libvirt/qemuディレクトリにDomain名のファイルで定義ファイルが作られる。

# ls /etc/libvirt/qemu
UbuntuVM.xml  networks

起動と起動状態の確認は、次の通りです。

virsh # start UbuntuVM
virsh # list 
 Id 名前               状態
----------------------------------
  1 UbuntuVM           実行中

VNCクライアントのスクリーン1で接続可能(vnc://localhost:1)です。vncのポートなどは、次の行で設定します。

    <graphics type='vnc' port='5902' keymap='ja'/>

デフォルトでは、仮想マシンを起動したホスト以外からのVNCクライアントの接続を受け付けないようになっています。ホストマシンとは別のマシンからvncで接続するには、/etc/libvirt/qemu.confに次の設定を追加します

vnc_listen = "0.0.0.0"

Windowsでvncを利用する場合カーソルがずれます。inputデバイスの設定を次のように変更してください。

<!--    <input type='mouse' bus='ps2'/>-->   
    <input type='tablet' bus='usb'/>    

停止はshutdownを使います。

virsh # shutdown UbuntuVM

acpiが有効になっていないと、virshからのシャットダウンを受け付けないので注意してください。また、linuxの場合、acpidをインストール/起動しておく必要があります。ubuntu serverの場合デフォルトではインストールされないので、

# apt-get install acpid

としてacpidをインストールします。

ハイバネートとレジューム

virsh # save UbuntuVM UbuntuVM.mem
virsh # restore UbuntuVM UbuntuVM.mem

Windows利用時に注意

libvirt xmlを記述するときの注意

  • clock offsetをlocaltimeにしないと時間がずれます。
  • acpiを有効にしないと、Windows7以降でインストールできません。
  • inputデバイスをtabletにしないと、VNCで接続したときにカーソルの動きが変になります。

Windowsゲストドライバ

Windowsゲストドライバを利用すると、ネットワークI/OやディスクI/Oが速くなります。 ゲストドライバの取得とインストール方法は下記のURLを参照。

ネットワーク接続

ルーティングして接続

仮想マシンのIPアドレスから、ホストOSのNICにルーティングして接続します。

  • ゲスト側のネットワークを適当設定(例えば192.168.100.2,GWを192.168.100.1)。ネットワークセグメントはホストと別にする。
  • ホスト側のネットワークを設定する。
    # ifconfig vnet0 192.168.100.1 netmask 255.255.255.0
    

IPフォワードの設定をしていなければ、IPフォワードの設定を行う。

# echo  1 >  /proc/sys/net/ipv4/ip_forward 

図で説明すると、上記の設定で次のようなネットワークが構築できる。

    eth0:10.9.123.3
         +--------------+            +---------------+
      +--+-+          +-+---+     +--+-+             |
LAN---|eth0| ホストOS |vnet0|-----|eth0|  仮想マシン |
      +--+-+          +-+---+     +--+-+             |
         +--------------+            +---------------+
             vnet0:192.168.100.1   eth0:192.168.100.2

NATによるLAN上のIPからの接続

プライベートIPをeth0に割り振り、NATで接続します。

  eth0:10.8.99.6,10.8.99.7(NATで192.168.100.2に接続)
         +--------------+            +---------------+
      +--+-+          +-+---+     +--+-+             |
LAN---|eth0| ホストOS |vnet0|-----|eth0|  仮想マシン |
      +--+-+          +-+---+     +--+-+             |
         +--------------+            +---------------+
         vnet0:192.168.100.1   eth0:192.168.100.2
  • eth0に10.8.99.6でアクセスするとホストOSに接続。
  • eth0に10.8.99.7でアクセスすると仮想マシン(192.168.100.2)に接続。

するように設定してみる。普通にeth0に10.8.99.6を設定しておく。 次に10.8.99.7へのアクセスを192.168.100.2に転送するNAT用のnat.iptファイルを用意する。

*nat
:PREROUTING ACCEPT [5318:237514]
:POSTROUTING ACCEPT [162:9112]
:OUTPUT ACCEPT [14132:847744]
-A PREROUTING -d 10.8.99.7 -j DNAT --to-destination 192.168.100.2
-A POSTROUTING -d 192.168.100.0/255.255.255.0 -j MASQUERADE
-A POSTROUTING -s 192.168.100.2  -j SNAT --to-source 10.8.99.7
-A OUTPUT -d 10.8.99.7 -j DNAT --to-destination 192.168.100.2
COMMIT

NATを読み込む。

# cat nat.ipt | iptables-restore

eth0に10.8.99.7を割り振らないとLAN上のマシンからアクセスできないので、ipコマンドでNICにIPを割り振っておきます。

# ip addr add 10.8.99.7 dev eth0

ブリッジによるホストのNICと同じセグメントへの設定

ホストIPアドレスが192.168.1.5で、仮想マシンのIPアドレスを192.168.1.6のようにホストのNICと同じセグメントのネットワークに仮想マシンを所属させたい場合は、ブリッジを利用します。

            virbr0:192.168.1.5
  +--------------+             +---------------+
  |           +-virbr0+        +               |
  |           |+-+---+|     +--+-+             |
  | ホストOS  ||vnet0||-----|eth0|  仮想マシン |
  |           |+-+---+|     +--+-+             |
  |           |  +    |        |               |
  |           |+-+---+|        |               |
  |           ||eth0 ||        |               |
  |           |+-+--++|        |               |
  |           +-----|-+        |               |
  +--------------+  |          +---------------+
                    |           eth0:192.168.1.6
                   LAN

※ブリッジを使うのでvnet0とeth0にはIPは振りません。 まず、/etc/network/interfacesに次のように記述し、ブリッジを有効にする。

auto virbr0
iface virbr0 inet static
        address 192.168.1.5
        netmask 255.255.255.0
        network 192.168.1.0
        broadcast 192.168.1.255
        gateway 192.168.1.1
        bridge_ports eth0

eth0を無効にし、ブリッジを有効にする。

# ifdown eth0
# ifup virbr0

libvirt.xmlのinterface定義にブリッジに含めるように定義

    <interface type='bridge'>
      <source bridge='virbr0' />
      <mac address='02:00:00:21:11:01'/>
      <target dev='vnet0' />
    </interface>

仮想マシンを起動すると、ブリッジに含まれた形で起動してくる。

# brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.0016d32ff458       no              eth0
                                                        vnet0

下記のようにすると、interfacesに書くことなくテストすることができる。

# brctl addbr virbr0
# brctl addif virbr0 eth0
# ifconfig virbr0 192.168.1.5 netmask 255.255.255.0

あとは、仮想マシン側のeth0でifconfigでIPアドレスを指定するなどすればok。

ポートフォワードによる仮想マシンIPを利用したルートコンソールの接続

仮想マシンのIPでVNCに接続すると、ブレードサーバのOAのようにルートコンソールが取得できると嬉しくありませんか? ポートフォワードを使うと実現することができます。

例えば、ゲストOSの5900番(VNCのデフォルトポート)をホストOS上の仮想マシンのコンソールのポートに転送するようにすれば、VNCで仮想マシンに接続するとルートコンソールが取れるようになります。

*nat
:PREROUTING ACCEPT [5318:237514]
:POSTROUTING ACCEPT [162:9112]
:OUTPUT ACCEPT [14132:847744]
-A PREROUTING -d 10.8.99.7 -p tcp --dport 5900 -j DNAT --to-destination 10.8.99.6:5900
-A PREROUTING -d 10.8.99.7 -j DNAT --to-destination 192.168.100.2
-A POSTROUTING -d ! 192.168.100.0/255.255.255.0 -j MASQUERADE
-A POSTROUTING -s 192.168.100.2  -j SNAT --to-source 10.8.99.7
-A OUTPUT -d 10.8.99.7 -j DNAT --to-destination 192.168.100.2
COMMIT

DHCPでIPを付与

DHCPでIPを付与するようにするには、/var/lib/libvirt/network/default.xmlを次のように編集します。

<network>
  <name>default</name>
  <uuid>631f86c8-5469-468a-a361-3a91d4b99a35</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0' />
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.100' end='192.168.122.254' />
      <host mac='52:54:00:1d:18:5e' name='foo.com' ip='192.168.122.28' />
    </dhcp>
  </ip>
</network>

上記のネットワーク設定ファイルを再設定して、ネットワークを再起動(net-destroy,net-start)します。

virsh # net-define /var/lib/libvirt/network/default.xml
ネットワーク default が /var/lib/libvirt/network/default.xml から定義されました

virsh # net-destroy default 
ネットワーク default は停止されました

virsh # net-start default 
ネットワーク default が起動されました

メモリバルーンニング

メモリバルーニングは、仮想マシンのメモリを動的に増減させます。setmemコマンドで、メモリを変更できます。

ゲスト

$ free
             total       used       free     shared    buffers     cached
Mem:        485656     229632     256024          0      49528      81960
-/+ buffers/cache:      98144     387512
Swap:       524284      58200     466084

この状態で、ホスト上で下記のコマンドを実行してみます。

ホスト

# virsh setmem DOMNAME 1000000

ゲスト上でfreeを実行すると、メモリが増えているのが分かります。

ゲスト

$ free
             total       used       free     shared    buffers     cached
Mem:        973656     229792     743864          0      49532      81984
-/+ buffers/cache:      98276     875380
Swap:       524284      58200     466084

メモリを減らすこともできます。

libvirtから仮想マシンへのルートログイン

libvirtでは、仮想マシンの実際に利用しているメモリなど、細かい情報が取れません。シリアルコンソールでルートログインできるように設定しておくと、ホストOSから状態の監視に使えます。

※言うまでもないことですが、仮想マシンのユーザがルートコンソールを管理者に取られるのが嫌な場合は、この方法は利用しないでください。

まず、libvirtのXMLファイルを定義して、ゲストのシリアルコンソールを有効にします。

...
    <serial type='pty'>
      <target port='0' />
    </serial>
    <console type='pty'>
      <target port='0' />
    </console>

</devices>

ゲスト側の設定

grub.cfgを設定

/boot/grub/grub.cfgに下記のようにシリアルコンソールの設定(console...115200n8を追加)

        linux   /boot/vmlinuz-3.2.0-40-generic \
                root=UUID=187b5cde-ed09-4d02-93c3-9c2fc162a97d ro quiet\
                console=tty0 console=ttyS0,115200n8

パスワードなしでシリアルログインできるように設定

mingettyをインストール

# apt-get install mingetty

/etc/init/ttyS0.confに下記のように設定

start on stopped rc RUNLEVEL=[2345] and (
            not-container or
            container CONTAINER=lxc or
            container CONTAINER=lxc-libvirt)

stop on runlevel [!2345]

respawn
exec /sbin/mingetty --autologin root ttyS0

ホスト側から接続

ゲストを再起動すると、ホスト側から

$ virsh DOMAINNAME console

でルートログインできる。

ネットワーク帯域制限

ネットワークインタフェース定義の部分で、bandwidthを定義すれば、帯域制限をかけることができる。単位はaverage/peakはkbyte/s。burstはkbyte。

  <interface type='...'>
    ...
    <model type='virtio' />
    <bandwidth>
      <inbound average='128' peak='256' burst='256'/>
      <outbound average='128' peak='256' burst='256'/>
    </bandwidth>

Ubuntuだと、kernel 3.5.0にしないと動作しない。RHEL6などでは多分利用できない。

QMP

KVM(というかqemu)には、ハイパーバイザからVMのイベントや情報を取得するQMP(Qemu Messaging Protocol)が提供されています。QMPを利用することにより、様々な情報を取得することができます。下記は利用例です。

# virsh qemu-monitor-command DOMNAME '{"execute":"query-blockstats"}' |python -mjson.tool
{
    "id": "libvirt-27",
    "return": [
        {
            "device": "drive-virtio-disk0",
            "parent": {
                "stats": {
                    "flush_operations": 0,
                    "flush_total_time_ns": 0,
                    "rd_bytes": 0,
                    "rd_operations": 0,
                    "rd_total_time_ns": 0,
                    "wr_bytes": 0,
                    "wr_highest_offset": 3860278784,
                    "wr_operations": 0,
                    "wr_total_time_ns": 0
                }
            },
            "stats": {
                "flush_operations": 0,
                "flush_total_time_ns": 0,
                "rd_bytes": 163434496,
                "rd_operations": 9976,
                "rd_total_time_ns": 8262845363,
                "wr_bytes": 13394944,
                "wr_highest_offset": 10235260416,
                "wr_operations": 1439,
                "wr_total_time_ns": 147967748900
            }
        },
        {
            "device": "drive-ide0-1-0",
            "parent": {
                "stats": {
                    "flush_operations": 0,
                    "flush_total_time_ns": 0,
                    "rd_bytes": 0,
                    "rd_operations": 0,
                    "rd_total_time_ns": 0,
                    "wr_bytes": 0,
                    "wr_highest_offset": 0,
                    "wr_operations": 0,
                    "wr_total_time_ns": 0
                }
            },
            "stats": {
                "flush_operations": 0,
                "flush_total_time_ns": 0,
                "rd_bytes": 49218,
                "rd_operations": 15,
                "rd_total_time_ns": 1671233,
                "wr_bytes": 0,
                "wr_highest_offset": 0,
                "wr_operations": 0,
                "wr_total_time_ns": 0
            }
        }
    ]
}

QMPについては、あまりドキュメントが整備されていません。QMPの詳細は、下記のスキーマに記載されています

ログメッセージの出力

libvirtdのデバッグログを出力するには、/etc/libvirt/libvirtd.confに下記の記述を追加し、libvirtdを再起動する。

log_level = 1
log_outputs="1:file:/var/log/libvirt/libvirtd.log"

リソース取得

下記のvirt-xxxコマンドにより、CPU、メモリ、ディスクのリソースを取得することができます。

コマンド説明
virt-top仮想マシンのCPU使用率、メモリの使用率を表示
virt-df仮想マシンのディスクの使用量を表示(Ubuntuではlibguestfs-toolsパッケージを利用)
virt-dmesg ???

KVMのもうちょっと高度な使い方

そのうち書く予定

  • リソースの取得方法
  • iscsiのDISKから起動
  • ライブマイグレーション
  • block migration
  • pythonからの操作