まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:kvm

自宅サーバー環境での KVM 仮想化サーバーがお亡くなりになってしまいました・・・ (ToT)

某メーカーのデスクトップPCに CentOS + KVM を導入して仮想化サーバーに使っていたのですが、初夏の温度上昇に耐えられなかったのか、うんともすんとも言わなくなってしまいました。マザーボードレベルでやられたっぽいです。ちなみに買ってまだ2年、ここのはもう買わない。

幸いにして、KVM のイメージファイルのバックアップは取っていたので、最新状態ではないにせよ救える仮想マシンもありそうです。で、別マシンを用意して CentOS + KVM を導入し、バックアップイメージファイルをインポートして起動させてみたら・・・ 期待通りに動いてくれません。。

症状として、仮想イメージ自体のインポートは成功して、KVM 内で起動はしてくれます。ただネットワーク(eth0)を認識してくれないため、ループバックアドレス(locahost)でしか使えません。もちろんインポート前には eth0 が使えていて、そのための ifcfg-eth0 などもちゃんと存在しているのに、です。単純なイメージインポートではこうなってしまうようです:
2015070401


もう少し詳しく(ifconfig -a コマンドで)調べてみると、どうも eth0 ではなく eth1 を認識している模様。ただ ifcfg-eth1 は用意していないのでネットワークとしては使えません:
2015070402



これがどういうことかというと、イメージファイルをインポートした際にネットワークインターフェースのハードウェアアドレス(MAC アドレス)が変わってしまって、eth0 ではなく eth1 として認識され、これまでの設定では正しく動かなくなってしまった、ということのようです。

対策としては2通りあります。1つは eth0 用の設定を eth1 用に書き換えて使う方法です。具体的には /etc/sysconfig/network-scripts/ 以下の ifcfg-eth0 を ifcfg-eth1 にリネームして、その中身の eth0 になっている箇所を eth1 に書き換えてリブートする方法です。この方法だと比較的簡単にできて、(eth1 をネットワークポートとして)動くことは動きます。

ただ、あくまで eth0 で動かしたい、という要望もあると思います。以下にその手順を紹介します。

まず上記の ifconfig -a コマンドの結果として、eth1 のハードウェアアドレスとして認識された結果(上記画像だと 52:54:00:9B:56:24)をメモしておきます。改めて /etc/udev/rules.d/70-persistent-net.rules をテキストエディタで開き、eth0 の ATTR{address}=="**:**:**:**:**:**" の **:**:**:**:**:** 部分をメモした内容で書き換えます:
2015070403


システムが DHCP でアドレスを割り当てられる設定になっている場合は /etc/sysconfig/network-scripts/ifcfg-eth0 を編集して、HWADDR の行をコメントアウトするか、または上記と同じ MAC アドレスの値に書き換えて保存します:
2015070405


これでシステムを再起動すると、eth0 が認識されるはずです:
2015070404


無事に KVM の CentOS イメージの引越ができました。

 

KVM(Kernel-based Virtual Machine) は CentOS/RHEL(RedHat Enterprise Linux) 環境に標準装備の仮想ハイパーバイザーです。わかりやすく言えば、VMWare Server のような機能がカーネルにはじめから用意されている(はじめから有効にはなっていない)ので、CentOS/RHEL でも、それ以外の Linux ディストリビューションでも単体で仮想環境を構築することができます。 僕も自宅の個人環境で使っています。KVM 環境の構築手順はこちらのエントリを参照ください:
CentOS に KVM 環境を構築する


KVM には GUI/CUI 両方の管理ツール/コマンドが用意されていて、仮想イメージのクローン(複製)なども行うことができます。ただこの複製ツールが手軽に使えるかというと、実用上ちょっとした問題がありました。
2015021801


その問題点は、この「複製」というのは「仮想イメージをまるごと複製」するのですが、ホスト名と MAC アドレスまでまるごと複製してしまう、という点でした。つまりあるサーバーインスタンスのイメージを複製すると、同じホスト名で、同じ MAC アドレスのネットワークインターフェースを持った異なるサーバーが用意される、ということです。 またネットワーク設定も複製されるため、例えば固定 IP アドレスが設定されている環境であれば同じ IP アドレスが割り振られることになりますし、DHCP 設定だとすると、同じ MAC アドレスのサーバーが別に存在していることになり、DHCP サーバーからは区別ができません(結果、正しい IP アドレスが取得できず、ネットワークが使えない状態で起動してしまいます)。


要は、複製コマンドそのものに問題があるわけではない(むしろ、複製という意味では正しい)のですが、イメージを複製した後にホスト名と MAC アドレスを変更したい(そこまで含めて自動化したい)のです。ただホスト名は AAA から BBB に変更すればよい、という正解が決まっているのでいいとして、MAC アドレスは変更したいといっても「では変更後の値を何にするのか?、一意に生成できるのか?」という問題もあったりして単純ではありません。またこれらの処理を自動化するとなると、起動していない仮想サーバーイメージ内のファイルシステムを外から操作することになるので、これも特殊なコマンドを使うことになります。その辺りを含めてシェルスクリプト化するまでを紹介します。


というわけで、目標として今回は clone-guest.sh というシェルスクリプトを作って、
# clone-guest.sh AAA BBB
というコマンドで、AAA という仮想マシンイメージを BBB という仮想マシンに複製できるようにします。複製そのものは /usr/bin/virt-clone コマンドを使えばできるのですが、その際に BBB は AAA とは異なる MAC アドレスを持っているように変更します。また複製後にこの BBB を自動起動するようにしてみます。また今回作成するシェルスクリプトでは複製の対象を CentOS/RHEL とします(これらのシステムを前提として MAC アドレス変更の処理を追加するためです)。


まず最初に「そもそもホスト名と 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 もかなり便利に使えてます。










 

Cloud Foundry をベースとした PaaS 環境は色々な会社から提供されていますが、その商用プライベート版の1つに Stackato (スタッカート)があります。

Stackato は(PaaS なので当たり前といえば当たり前ですが)ホスティングされた環境を利用することもできますし、Amazon EC2 や HP Cloud Services 上にデプロイして利用するためのパッケージも用意されています。また小規模向け限定だと思われますが、仮想環境用のイメージファイルをダウンロードすることで、自社のオンプレミスなどの全くのプライベートな環境内に構築して利用することも可能です。

この最後のケースですが、イメージファイルは VirtualBox / VMWare / vSphere / KVM 環境それぞれ用意されているので、これらのいずれかの環境があれば試してみることができます。

というわけで、自分の KVM 環境を使って Stackato を導入してみた時の様子を、最初のセットアップまで紹介します。仮想環境を今から用意するのであれば PC に VirtualBox をダウンロードしてインストールするのが手軽かな、と思っていますが、もし KVM 環境を整えるのであればこちらを参照ください:
CentOS に KVM 環境を構築する

まずは Stackato のイメージをダウンロードします。 ダウンロードサイトから目的の仮想環境にあったものを選んで "Direct" と書かれた箇所をクリックするとダウンロードが始まります(1ファイルが2GB近くあります):
2014051400


ダウンロードしたファイルを展開すると、選択した仮想環境用のマシンイメージファイルが現れます。Stackato V3.2.1 の KVM 用イメージであれば stackato-img-kvm-v3.2.1.img というファイル名でした(展開後のサイズは10GB近く)。


これを KVM 内で稼働させます。VirtualBox や VMWare など、他の環境を使う方はその環境なりの方法でイメージファイルを仮想マシンとして起動させてください。以下しばらくは KVM で仮想マシンマネージャーを使って起動させる場合の説明になります。

まずは KVM のホストマシン上で「仮想マシンマネージャー」を起動します:
2014051401


仮想マシンマネージャーが起動したら "localhost" と書かれた箇所を右クリックし、「新規」を選択します:
2014051402


作成する仮想マシンの名前を適当に(図では "stackato")入力します。また先程展開したイメージファイルから作成するので「既存のディスクイメージをインポート」を選択して「進む」をクリックします:
2014051403


「既存のストレージパス」には「参照」ボタンをクリックして、先程展開した Stackato のディスクイメージファイルを選択します。また「OS の種類」には "Linux" を、「バージョン」はこのイメージの元になっている "Ubuntu 10.04" を選択します:
2014051404


「メモリー」欄には Stackato に割り当てるメモリサイズを指定します。推奨値は 2GB 以上となっているので 2048(MB) と入力します。メモリに余裕がある環境であればもっと大きな数値でも構いません(但し、このダウンロードイメージを使う場合の上限は4GBらしいです)。なお1GB(1024) を指定した場合でも起動はしましたが、起動時に警告メッセージが表示されました。CPU は Stackato 環境に割り当てる仮想CPU数を指定してください:
2014051405


最後に内容を確認して「完了」をクリックします:
2014051406


Stackato 仮想マシンが起動している様子です:
2014051407



仮想イメージの起動が完了するとこんな感じの画面になります(IP Address はDHCPで割り振られた、この Stackato マシンのIPアドレスです)。また画面内に "https://stackato-mm7d.local" と書かれたURL(赤字部分は環境によって変わります)は管理コンソールへアクセスするための URL ですが、後で使うのでメモしておきます。
ここからは KVM に依存しない内容に戻ります:
2014051408


まずはこの Stackato 環境にログインしてみます。"l"(エル)キーを押して、ログイン画面に移ります。デフォルト状態ではユーザー名/パスワードともに "stackato" が設定されているので、この内容を入力してログインします:
2014051409


ログインできました。CPU やメモリの状態、内部アドレスなどが表示されてプロンプトになります。なお stackato ユーザーが sudo 権限を持っているので、sudo を使うことでシャットダウンやリブートを含めた管理者権限でのコマンド実行を行うことも可能です:
2014051410


管理コンソールへアクセスするには、自分の PC や仮想ホストマシンから先程のログイン画面に表示されていた URL にブラウザでアクセスします。が、このホスト名は Stackato が自動的に割り振ったものであって、当然ですがそのままでは(名前解決ができないため)アクセスできません。 これを解決するために DNS を設定してもいいのですが、手っ取り早い方法としてはブラウザを使うマシンの hosts ファイル(Unix/Linux 系であれば /etc/hosts、Windows 系であれば C:\Windows\System32\drivers\etc\hosts)を編集して以下の1行を追加します:
192.168.0.101 stackato-mm7d.local api.stackato-mm7d.local

この最初のアドレス部分は作成した Stackato 仮想マシンの IP アドレス(画面に表示されているもの)で、その右にホスト名、更にスペースを空けて api. を頭に付けたホスト名を記述します。

これでブラウザからホスト名指定でアクセスできるようになりました。改めてブラウザのアドレス欄に https://api.ホスト名/ (上記の一番右に追加した api. 付きの名前)と入力してアクセスします。すると以下の様な管理画面(の初期設定画面)が表示されます:
2014051411


それぞれ以下の内容を入力し、"Yes, I agree to the Stackato Terms of Services" にチェックを入れて、最後に右下の "Setup First Admin User" ボタンをクリックします:
- Username: 管理ユーザー名
- Email Address: メールアドレス
- User Password: 管理ユーザーのパスワード
- Confirm Password: (確認用)同じパスワード
- Organization Name: 組織名/社名
- Space Name: デプロイ空間名称(適当に "dev" とか)


するとログイン画面が表示されます。ここに先程入力した管理ユーザー名とパスワードを指定してログインします:
2014051412


正しくログインが完了するとこのような画面が表示され、各種管理機能にアクセスすることが可能になります:
2014051413


とりあえず導入から起動、そして一通りのセットアップをした上で、管理コンソールにアクセスするまでを紹介しました。

実際のアプリケーションのデプロイなどはもう少し調べた上で別途紹介したいと思っています。

(2014/May/17 追記)
続きはこちらです。


 

CentOS に仮想化環境の KVM を導入する手順を紹介します。

KVM は Kernel-based Virtual Machine で、仮想化環境のハイパーバイザーです。VMWare などと同様に、KVM 環境の中に仮想のマシン(「ゲストOS」といいます)を作って、あたかも独立した一台のマシンがあるかのように利用することができます。大きな特徴としてまず「無料である」こと、加えて名前の由来になっているように Linux のカーネルの一部として、標準搭載されている機能なので、導入が比較的容易であることが挙げられます。またハイパーバイザー型であるため、KVM 自体は1台の物理的な Linux マシン(「ホストOS」といいます)の中で動き、その制御も Linux を通して行うことになります。例えば管理用のコンソール機能などもホストOS上の Linux アプリケーションとして提供されており、仮想HDDはホストOSからは1つのファイルとして扱います。

上記でも触れましたが、KVM は Linux のカーネルに含まれる形で実装されています。今回は Linux ディストリビューションとして CentOS を利用する前提で説明します。CentOS 自体のインストール方法については過去のブログエントリを参照してください:
『CentOS の導入』

KVM を利用するにはハードウェア的な条件があります。CPU が仮想化支援機構(Intel-VT, AMD-V)に対応している必要がある、ことです。

/proc/cpuinfo に flags として vmx が含まれていれば仮想化支援機構に対応済みと判断できます。
/proc/cpuinfo に flags として vmx (Intel版)か svm (AMD版)が含まれていれば仮想化支援機構に対応済みと判断できます(2016/Mar/20 修正)。
# cat /proc/cpuinfo | grep '(vmx|svm)'
(この結果、何かが表示されれば対応していることになる)

また利用する前に BIOS の設定を変更し、この仮想化支援機構の機能を有効にしておきます。


では CentOS に KVM を導入する手順を紹介します。まずは yum で KVM に必要なモジュールをまとめてインストールします:
# yum -y install libguestfs libguestfs-tools libguestfs-tools-c libvirt libvirt-client python-virtinst qemu-kvm virt-manager virt-top virt-viewer

インストールできたら KVM(libvirt) の自動起動を ON にしておきます。これで CentOS の起動と同時に KVM が有効になります:
# chkconfig libvirtd on

この段階で一度再起動して、KVM が有効になった状態で OS を起動します:
# shutdown -r now

再起動後、ホストOSにブリッジインターフェース(br0)を作成します。 このあと KVM 内に作成する全てのゲストOSはこのブリッジインターフェースを経由してインターネットを含めた外部ネットワークにアクセスすることになります。

ブリッジインターフェースは eth0 をベースに作成することにします。 そのため、まずは ifcfg-eth0 をコピーして ifcfg-br0 を作り、ここから br0 の雛形を作成します:
# cd /etc/sysconfig/network-scripts
# cp ifcfg-eth0 ifcfg-br0

ifcfg-br0 を編集します。内容は基本的に eth0 のものをそのまま使いますが、TYPE 属性を Bridge に変更します(大文字小文字に注意):
# vi ifcfg-br0

DEVICE=br0
TYPE=Bridge
ONBOOT=yes
 :

続いて ifcfg-eth0 を編集します。こちらは「(設定済みの)br0 経由で使う」ことを宣言することになります(大文字小文字に注意)。加えて IP アドレス関連の設定は br0 の方で行っていて、そちらへのブリッジをするだけなので eth0 では不要です(コメントアウトします):
# vi ifcfg-eth0

DEVICE=eth0
TYPE=Ethernet
ONBOOT=yes
BRIDGE=br0
#IPADDR=192.168.0.2
#PREFIX=24
#GATEWAY=192.168.0.1
#DNS1=192.168.0.1

DEFROUTE=yes
 :

この状態でネットワークを再起動します:
# /etc/init.d/network restart

ブリッジインターフェース(br0)が追加されたことを確認します:
# brctl show

bridge name bridge id STP enabled interfaces
br0 8000.002185aba9e1 no eth0
virbr0 8000.525400ca36c8 yes virbr0-nic

ブリッジインターフェースが2つ(br0, virbr0)確認できます。今作成した方(br0)ではない、もう1つのブリッジインターフェース virbr0 は KVM の導入と同時に作成されるものです。
これもブリッジインターフェースとして使えることは使えますが、パフォーマンスが悪いので明示的に br0 を作ったのでした。

この状態では virbr0 は不要なので削除します。また再起動時に自動作成されないよう設定します:
# virsh net-destroy default
# virsh net-autostart default --disable

以下のコマンドで default の State が inactive になっていて、 かつ Autostart が no になっていることが確認できれば設定完了です:
# virsh net-list --all
Name State Autostart
-----------------------------------------
default inactive no


ここまでの設定で KVM 関連ツール(仮想マシンマネージャーとか)が使えるようになっているはずです。KVM の使い方については別の機会に。
kvm01

 

SoftLayer をクラウドノードを開発サーバーとして1週間程度使っています(以前のエントリその1その2)。ただ使っているだけだと「快適です」以外にあまり言うことがなくてつまらないので、違う視点から改めて見てみました。ああ申し込み時にこうしておけばよかった、という思うこともいくつか・・・

まずはデータセンター。選択肢としてはワシントンDC、サンノゼ、シアトル、ダラス、シンガポール、そしてアムステルダムとあります。はっきり言って深く考えずにワシントンDCを選んでしまいましたが、よく見たらこんな資料が・・
sl2013101501


ああ、日本のネットワークセンター(品川)から直結しているのはシンガポールとサンノゼだったのか。このどっちかにするべきでした。

そして利用者向けポータルサイトで見つけた "Provisioning Scripts" の設定。
sl2013101502


実はいまだに申込時にどうやるとこれが有効にできるのか、まだハッキリと分かっているわけではないのですが、OS のロード直後に実行させるプロビジョン用のスクリプトファイルを指定できるもののようです。このクラウドノードからURLでアクセスできるところ(要はインターネット上のHTTPアクセス可能なサイト)にこのスクリプトを置き、その中にシェルスクリプト等で最初に行っておきたいセットアップ内容を記述しておけば、OS の起動直後に実行してくれる、というものです。したがって、ここで書いたような yum の実行コマンドを羅列したシェルスクリプトのテキストファイルを指定すれば、起動と同時に実行されて必要なミドルウェア環境が構築できる、ということだと思います。シェルスクリプトで記述できる範囲に限られてしまうとはいえ、すげー便利!

 

このページのトップヘ