XenのdomUを別のホストにssh経由で移動する

今回はdomUをネットワーク経由で別ホストにお引越しする方法のメモです。イメージファイルを利用している場合は単にファイルをコピーするだけですが、LVMを利用している場合は少しだけ面倒になります。

Xenとしては共有ディスクを利用するのが正しい姿な気がしますが、Xenを使うような環境ではそんな豪華な装備がないことが多いので、今回はネットワーク経由でディスクをコピーしてみます。また二つのホストは遠隔地にある可能性もありますので、そのような場合も考慮してssh経由でコピーを行います。

下記の例では移動元ホストをxen00とし、移動先ホストをxen01としています。

まず下準備として、移動先ホスト上でパスフレーズなしのssh用カギを作成します。

[xen01]# ssh-keygen -t rsa -b 4096

次に移動元ホスト上でsshログイン用のユーザを作成します。わざわざユーザを作成しないでrootユーザを利用しても良いとは思いますが、念のために別ユーザにしています。

[xen00]# groupadd xenmgr
[xen00]# useradd -m -g xenmgr xenmgr

先ほど作成したssh用カギの公開鍵の方を作成したユーザの authorized_keys に設定します。
この時authorized_keysでは、安全のためcommandを指定して実行可能なコマンドを制限しておきます。

ここではxenmgrというスクリプトを実行するようにしました。またLVMのLVを読み込むためにはroot権限が必要になりますのでsudoを利用しています。

command="sudo /usr/local/sbin/xenmgr",from="192.168.1.2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAA....  root@xen01

スクリプトをsudo経由で利用するために、visudoで設定を行います。

[xen00]# visudo

一つ目はrequirettyの設定をコメントアウトします。
これでssh経由で実行する際にsshが端末を割り当てなくても大丈夫になります。

...
#Defaults    requiretty
...

二つ目はssh経由で実行するコマンドをパスワードなしで実行可能にします。

Cmnd_Alias XENMGR_CMD = /usr/local/sbin/xenmgr
Host_Alias XENMGR_HOST = ALL
User_Alias XENMGR_USER = xenmgr

Defaults:XENMGR_USER env_keep = "SSH_ORIGINAL_COMMAND"
XENMGR_USER	XENMGR_HOST=(root)	NOPASSWD:XENMGR_CMD
...

xenmgrの中身は下記のような感じです。

#!/usr/bin/python

import re
import os
import sys

VGName = 'VolGroup00'
VMPrefix = 'vm'
XenConfigDir = '/etc/xen'
BlockSize = 8192

def main():
        if os.environ.has_key('SSH_ORIGINAL_COMMAND'):
                params = os.environ['SSH_ORIGINAL_COMMAND']
        else:
                params = ' '.join(sys.argv[1:len(sys.argv)])

        p = re.compile('^([a-zA-Z0-9_\-]+)\s+([a-zA-Z0-9_\-]+)$')
        m = p.match(params)
        if not m:
                sys.stderr.write("invalid parameters: " + params + "\n")
                return 1

        cmd = m.group(1)
        vmname = m.group(2)

        if cmd == 'dump':
                ret = command_dump(vmname)
        elif cmd == 'config':
                ret = command_config(vmname)
        else:
                sys.stderr.write("unknown command: " + cmd + "\n")
                return 2

        return ret

def command_dump(vmname):
        lvname = '/dev/%s/%s%s' % (VGName, VMPrefix, vmname)
        if not os.path.exists(lvname):
                sys.stderr.write("lv not found: " + lvname + "\n")
                return 3

        dd = 'dd if=%s bs=%d' % (lvname, BlockSize)
        os.system(dd)

def command_config(vmname):
        cfgfile = '%s/%s%s' % (XenConfigDir, VMPrefix, vmname)
        if not os.path.exists(cfgfile):
                sys.stderr.write("cfgfile not found: " + cfgfile + "\n")
                return 4

        cat = 'cat %s' % (cfgfile)
        os.system(cat)

if __name__ == '__main__':
        sys.exit(main())

commandで指定されたスクリプトには、環境変数SSH_ORIGINAL_COMMAND経由で、sshの実行時に指定された文字列が渡されます。スクリプトは指定された文字列をLV名として利用し、該当するLVの内容をddで標準出力に書き出すだけの単純なものです。

これで下準備は環境です。

実際にdomUを移動するには、最初に対象のdomUを停止します。

[xen00]# xm shutdown vm00

次に移動先にディスク領域を確保して、ssh経由でデータを書き込みます。

[xen01]# lvcreate -L 16G -n vm00 /dev/VolGroup00
[xen01]# ssh xenmgr@192.168.1.1 dump 00 > /dev/VolGroup00/vm00
[xen01]# ssh xenmgr@192.168.1.1 config 00 > /etc/xen/vm00

手元の環境(CPU:Celeron420, Mem:8GB, NIC:e1000e)で試したところ、16GBのLVに対してddのBlockSizeが4096の時に20分くらい、BlockSizeが8192の時に12分くらいかかりました。ディスク容量が大きくなると辛そうですが、そんなに頻繁に利用するものでもないので使えないこともなさそうです。

最後に移動先でdomUを起動させて無事に動作すれば完了です。ちなみに各ホストごとにVG名を別にしている場合は、事前に/etc/xen/vm00のdisk部分等を修正しておく必要があります。

[xen01]# xm create -c vm00