KVM(Kernel-based Virtual Machine) は CentOS/RHEL(RedHat Enterprise Linux) 環境に標準装備の仮想ハイパーバイザーです。わかりやすく言えば、VMWare Server のような機能がカーネルにはじめから用意されている(はじめから有効にはなっていない)ので、CentOS/RHEL でも、それ以外の Linux ディストリビューションでも単体で仮想環境を構築することができます。 僕も自宅の個人環境で使っています。KVM 環境の構築手順はこちらのエントリを参照ください:
CentOS に KVM 環境を構築する
KVM には GUI/CUI 両方の管理ツール/コマンドが用意されていて、仮想イメージのクローン(複製)なども行うことができます。ただこの複製ツールが手軽に使えるかというと、実用上ちょっとした問題がありました。

その問題点は、この「複製」というのは「仮想イメージをまるごと複製」するのですが、ホスト名と MAC アドレスまでまるごと複製してしまう、という点でした。つまりあるサーバーインスタンスのイメージを複製すると、同じホスト名で、同じ MAC アドレスのネットワークインターフェースを持った異なるサーバーが用意される、ということです。 またネットワーク設定も複製されるため、例えば固定 IP アドレスが設定されている環境であれば同じ IP アドレスが割り振られることになりますし、DHCP 設定だとすると、同じ MAC アドレスのサーバーが別に存在していることになり、DHCP サーバーからは区別ができません(結果、正しい IP アドレスが取得できず、ネットワークが使えない状態で起動してしまいます)。
要は、複製コマンドそのものに問題があるわけではない(むしろ、複製という意味では正しい)のですが、イメージを複製した後にホスト名と MAC アドレスを変更したい(そこまで含めて自動化したい)のです。ただホスト名は AAA から BBB に変更すればよい、という正解が決まっているのでいいとして、MAC アドレスは変更したいといっても「では変更後の値を何にするのか?、一意に生成できるのか?」という問題もあったりして単純ではありません。またこれらの処理を自動化するとなると、起動していない仮想サーバーイメージ内のファイルシステムを外から操作することになるので、これも特殊なコマンドを使うことになります。その辺りを含めてシェルスクリプト化するまでを紹介します。
というわけで、目標として今回は clone-guest.sh というシェルスクリプトを作って、
まず最初に「そもそもホスト名と MAC アドレスの変更ってどうやるの?」という疑問が生じます。ホスト名については CentOS/RHEL では /etc/sysconfig/network 内に HOSTNAME=**** として記載されているので、ここを書き換えるだけで済みます。具体的には例えば複製元の /etc/sysconfig/network に HOSTNAME=AAA.domain.com のような記載が含まれていたら、複製先では HOSTNAME=BBB.domain.com と書き換えればいい、ということになります。
一方、MAC アドレスはちと複雑です。まず MAC アドレスを書き換える必要のあるファイルが、
- /etc/sysconfig/network-scripts/ifcfg-eth0
- /etc/udev/rules.d/70-persistent-net.rules
の2つあります。更に書き換え後の MAC アドレスは現在とは異なる一意の値にする必要があるため、どのように生成するか、という問題も含まれているのでした。ここは詳しくは後述しますが /usr/bin/uuidgen コマンドを使って動的に取得するようにします。
次に、起動していない仮想イメージファイル内のファイルシステムへどのようにアクセスするか? ですが、こちらは KVM 標準の以下のコマンドを活用して実現します:
これらのツールと、KVM の一般的な仮想管理コマンドである virsh(/usr/bin/virsh) を組み合わせて、clone-guest.sh を以下のような内容で作りました。青字は僕のコメントです:
sed だの awk だのを駆使してなんとか作った、という感じです(苦笑)。あとはこのファイルを clone-guest.sh という名前で保存し、実行権限を与え、パスの通ったディレクトリに置いておけば、
という感じで、既存の AAA という仮想環境を BBB という名前で複製して、各種設定ファイルまで書き換えて、起動する、という使い方ができるはずです。 ここまでできるようになると、KVM もかなり便利に使えてます。
CentOS に KVM 環境を構築する
KVM には GUI/CUI 両方の管理ツール/コマンドが用意されていて、仮想イメージのクローン(複製)なども行うことができます。ただこの複製ツールが手軽に使えるかというと、実用上ちょっとした問題がありました。

その問題点は、この「複製」というのは「仮想イメージをまるごと複製」するのですが、ホスト名と MAC アドレスまでまるごと複製してしまう、という点でした。つまりあるサーバーインスタンスのイメージを複製すると、同じホスト名で、同じ MAC アドレスのネットワークインターフェースを持った異なるサーバーが用意される、ということです。 またネットワーク設定も複製されるため、例えば固定 IP アドレスが設定されている環境であれば同じ IP アドレスが割り振られることになりますし、DHCP 設定だとすると、同じ MAC アドレスのサーバーが別に存在していることになり、DHCP サーバーからは区別ができません(結果、正しい IP アドレスが取得できず、ネットワークが使えない状態で起動してしまいます)。
要は、複製コマンドそのものに問題があるわけではない(むしろ、複製という意味では正しい)のですが、イメージを複製した後にホスト名と MAC アドレスを変更したい(そこまで含めて自動化したい)のです。ただホスト名は AAA から BBB に変更すればよい、という正解が決まっているのでいいとして、MAC アドレスは変更したいといっても「では変更後の値を何にするのか?、一意に生成できるのか?」という問題もあったりして単純ではありません。またこれらの処理を自動化するとなると、起動していない仮想サーバーイメージ内のファイルシステムを外から操作することになるので、これも特殊なコマンドを使うことになります。その辺りを含めてシェルスクリプト化するまでを紹介します。
というわけで、目標として今回は clone-guest.sh というシェルスクリプトを作って、
というコマンドで、AAA という仮想マシンイメージを BBB という仮想マシンに複製できるようにします。複製そのものは /usr/bin/virt-clone コマンドを使えばできるのですが、その際に BBB は AAA とは異なる MAC アドレスを持っているように変更します。また複製後にこの BBB を自動起動するようにしてみます。また今回作成するシェルスクリプトでは複製の対象を CentOS/RHEL とします(これらのシステムを前提として MAC アドレス変更の処理を追加するためです)。# clone-guest.sh AAA BBB
まず最初に「そもそもホスト名と MAC アドレスの変更ってどうやるの?」という疑問が生じます。ホスト名については CentOS/RHEL では /etc/sysconfig/network 内に HOSTNAME=**** として記載されているので、ここを書き換えるだけで済みます。具体的には例えば複製元の /etc/sysconfig/network に HOSTNAME=AAA.domain.com のような記載が含まれていたら、複製先では HOSTNAME=BBB.domain.com と書き換えればいい、ということになります。
一方、MAC アドレスはちと複雑です。まず MAC アドレスを書き換える必要のあるファイルが、
- /etc/sysconfig/network-scripts/ifcfg-eth0
- /etc/udev/rules.d/70-persistent-net.rules
の2つあります。更に書き換え後の MAC アドレスは現在とは異なる一意の値にする必要があるため、どのように生成するか、という問題も含まれているのでした。ここは詳しくは後述しますが /usr/bin/uuidgen コマンドを使って動的に取得するようにします。
次に、起動していない仮想イメージファイル内のファイルシステムへどのようにアクセスするか? ですが、こちらは KVM 標準の以下のコマンドを活用して実現します:
- /usr/bin/virt-cat 外部から仮想イメージ内の特定ファイルに対して cat を実行する - /usr/bin/virt-copy-in 外部から仮想イメージ内の特定ファイルにコピーする
これらのツールと、KVM の一般的な仮想管理コマンドである virsh(/usr/bin/virsh) を組み合わせて、clone-guest.sh を以下のような内容で作りました。青字は僕のコメントです:
#!/bin/sh # 作業ディレクトリ work_dir=/tmp/clone-guest # この作業で使う各種コマンド cmd_awk=/bin/awk cmd_cat=/bin/cat cmd_cut=/bin/cut cmd_grep=/bin/grep cmd_mkdir=/bin/mkdir cmd_sed=/bin/sed cmd_uuidgen=/usr/bin/uuidgen cmd_arp=/sbin/arp cmd_virsh=/usr/bin/virsh cmd_virt_cat=/usr/bin/virt-cat cmd_virt_clone=/usr/bin/virt-clone cmd_virt_copy_in=/usr/bin/virt-copy-in source_bridge=br0 # KVM のブリッジインターフェース名(デフォルトだと virbr0) original_domain_name=$1 # 複製元(AAA) clone_domain_name=$2 # 複製先(BBB) # 仮想イメージのあるディレクトリ domain_image_dir=/var/lib/libvirt/images clone_domain_image_path=$domain_image_dir/$clone_domain_name.img # 複製先仮想イメージファイル # パラメータを2つ(複製元と複製先)指定して実行していることを確認 if [ "$clone_domain_name" = "" ] ; then echo "Usage: clone-guest.sh <domain_name> <clone_name>" exit 0 fi # 作業ディレクトリが存在していなかったら作成 [ ! -e $work_dir ] && $cmd_mkdir -p $work_dir # virt-clone コマンドで普通に複製 $cmd_virt_clone \ --original $original_domain_name \ --name $clone_domain_name \ --file $clone_domain_image_path # 複製前の仮想イメージ内の /etc/sysconfig/network を取り出して作業ディレクトリに保存 $cmd_virt_cat -d $original_domain_name \ /etc/sysconfig/network \ > $work_dir/network.org # 複製前の仮想イメージ内の /etc/sysconfig/network-scripts/ifcfg-eth0 を取り出して作業ディレクトリに保存 $cmd_virt_cat -d $original_domain_name \ /etc/sysconfig/network-scripts/ifcfg-eth0 \ > $work_dir/ifcfg-eth0.org # 複製前後の仮想イメージの MAC アドレスを取り出す original_mac_addr=$( \ $cmd_virsh domiflist $original_domain_name \ | $cmd_grep $source_bridge \ | $cmd_awk '{print $5}' \ ) clone_mac_addr=$( \ $cmd_virsh domiflist $clone_domain_name \ | $cmd_grep $source_bridge \ | $cmd_awk '{print $5}' \ ) # 複製前後の仮想イメージの UUID を取り出す original_nic_uuid=$( \ $cmd_cat $work_dir/ifcfg-eth0.org \ | $cmd_grep -i uuid \ | $cmd_cut -d'=' -f2 \ | $cmd_sed 's/"//g' \ ) clone_nic_uuid=$($cmd_uuidgen) # 複製後の仮想イメージの ifcfg-eth0 を書き換え $cmd_cat $work_dir/ifcfg-eth0.org \ | $cmd_sed -e "s/$original_mac_addr/$clone_mac_addr/i" \ -e "s/$original_nic_uuid/$clone_nic_uuid/i" \ > $work_dir/ifcfg-eth0 # 複製後の仮想イメージに書き換え後の ifcfg-eth0 を書き戻す $cmd_virt_copy_in -d $clone_domain_name \ $work_dir/ifcfg-eth0 \ /etc/sysconfig/network-scripts/ # 複製前の仮想イメージ内の /etc/udev/rules.d/70-persistent-net.rules を取り出して作業ディレクトリに保存 $cmd_virt_cat -d $original_domain_name \ /etc/udev/rules.d/70-persistent-net.rules \ > $work_dir/70-persistent-net.rules.org # 複製後の仮想イメージの 70-persistent-net.rules を書き換え $cmd_cat $work_dir/70-persistent-net.rules.org \ | $cmd_sed "s/$original_mac_addr/$clone_mac_addr/i" \ > $work_dir/70-persistent-net.rules # 複製後の仮想イメージに書き換え後の 70-persistent-net.rules を書き戻す $cmd_virt_copy_in -d $clone_domain_name \ $work_dir/70-persistent-net.rules \ /etc/udev/rules.d/ # 複製後の仮想イメージの network を書き換え $cmd_cat $work_dir/network.org \ | $cmd_sed -e "s/$original_domain_name/$clone_domain_name/i" \ > $work_dir/network # 複製後の仮想イメージに書き換え後の network を書き戻す $cmd_virt_copy_in -d $clone_domain_name \ $work_dir/network \ /etc/sysconfig/ # 複製後の仮想イメージを起動 $cmd_virsh start $clone_domain_name echo "Clone $clone_domain_name has been started." exit 0
sed だの awk だのを駆使してなんとか作った、という感じです(苦笑)。あとはこのファイルを clone-guest.sh という名前で保存し、実行権限を与え、パスの通ったディレクトリに置いておけば、
# clone-guest.sh AAA BBB
という感じで、既存の AAA という仮想環境を BBB という名前で複製して、各種設定ファイルまで書き換えて、起動する、という使い方ができるはずです。 ここまでできるようになると、KVM もかなり便利に使えてます。