第5回:Eucalyptusの仮想化管理アーキテクチャ


サーバー仮想化環境の管理ライブラリ

 今回は、サーバー仮想化環境の管理ライブラリであるlibvirtを紹介します。libvirtは、さまざまな仮想化ハイパーバイザーに共通の操作方法(API)を提供することを目指したオープンソースのライブラリです。

 KVMによるサーバー仮想化環境では、virshやvirt-managerなどの管理ツールが標準的に用いられていますが、これらは内部でlibvirtを使用しています。したがって、これらのツールは、libvirtに対応した仮想化ハイパーバイザーであれば、KVM以外の環境も同時に管理することができます。これは、EucalyptusのようなIaaSクラウドを構築するためのソフトウェアにとってもメリットがあります。

 例えば、最近では、Eucalyptusの他にもIaaSクラウドを構築するためのさまざまなオープンソースソフトウェアが登場しています。これらに共通の特徴として、複数の仮想化ハイパーバイザーへの対応が挙げられます。この時、libvirt経由でハイパーバイザーを操作するようにすれば、ハイパーバイザーごとにまったく個別のコードを用意する必要がなくなり、新しいハイパーバイザーへの対応が行いやすくなります。また、【図5-1】のように機能別のレイヤー構造が明確になりますので、問題判別の際には、問題が起きている箇所の切り分けが行いやすくなります。

【図5-1】IaaSクラウドソフトウェアのレイヤ構造

 【図5-1】にあるように、libvirtはサーバー仮想化に伴って必要となる、仮想ストレージや仮想ネットワークの構成についても統一的なAPIを提供します。

 現状では、Eucalyptusは、VMインスタンスの起動、停止など、仮想化ハイパーバイザーの操作のみにlibvirtを利用しており、仮想ストレージや仮想ネットワークについては、独自の実装で実現しています。つまり、【図5-1】はまだ完全に実現されているわけではありません。libvirtが想定するような一般的なサーバー仮想化環境で求められる機能とクラウド環境で求められる機能には、まだ差異があることが1つの理由と考えられます。

 しかしながら、本連載の第2回と第3回で説明したように、Eucalyptusが提供する仮想ストレージや仮想ネットワークの基礎となる技術は、Linuxが標準的に提供するものばかりです。Eucalyptus独自の魔法を使っているわけではありません。今後、libvirtの機能もよりクラウド環境に適したものに進化していくはずですので、Eucalyptusとlibvirtの連携もより深いものになると予想されます。

 

libvirtによる仮想ストレージの操作

 ここでは、libvirtの有用性を示す一例として、libvirtによる仮想ストレージの操作方法を説明します。libvirtが提供する仮想ストレージの機能は、前述のように、現在はEucalyptusで用いられているわけではありません。しかしながら、ディスクイメージファイル、LVMの論理ボリューム、iSCSIボリュームなど、さまざまな形態のストレージを統一的に扱うことができるため、一般的なサーバー仮想化環境でも十分に役立つ機能です。仮想ストレージの基礎知識として学んでおいてください。

 libvirtを経由して仮想化環境の操作を行うには、virshコマンドを利用するのが最も簡単です。プログラムから利用する場合は、libvirtが標準で提供するC言語、もしくはPython用のライブラリを用います。ここでは、virshコマンドによる操作の例を紹介します。また、仮想化ハイパーバイザーは、Red Hat Enterprise Linux 6のKVM環境を前提とします。

 まず、libvirtでは、仮想マシンに接続する、1つの仮想ディスク装置に対応するストレージ領域をボリュームと呼びます。これは、1つのディスクイメージ・ファイル、1つのLVM論理ボリューム、1つのiSCSI接続ディスク(LUN)などに対応します。また、これらのボリュームを一定のルールでグループ化したものをストレージプールと呼びます。

 ディスクイメージ・ファイルの場合は、同じディレクトリ上のファイルが同じストレージプールに属します。LVM論理ボリュームの場合は、同じボリュームグループに属するもの、iSCSI接続ディスクの場合は、同じiSCSIターゲットに属するものなどとなります。最初にストレージプールを定義すると、そのストレージプールに対するボリュームの作成などは、すべて同じコマンドで実施することが可能です。ストレージプールの種類を気にする必要はありません。

 libvirtを導入したデフォルトの状態では、ディレクトリ/var/lib/libvirt/imagesを保存場所とするディスクイメージ・ファイルのストレージプールが「default」という名称で作成されています。次はストレージプールの一覧を確認して、defaultという名称のストレージプールに含まれるボリュームの一覧を表示する例です。筆者のテスト環境で使用している、ディスクイメージ・ファイル形式のボリュームがいくつかあることがわかります。

# virsh pool-list
名前               状態     自動起動
-----------------------------------------
default              動作中  yes

# virsh vol-list default
名前               パス
-----------------------------------------
CLIENT01.img       /var/lib/libvirt/images/CLIENT01.img
LDAP01.img         /var/lib/libvirt/images/LDAP01.img
PGSQL9.img         /var/lib/libvirt/images/PGSQL9.img

 続いて、LVMのボリュームグループをストレージプールとして定義してみます。始めにボリュームグループimagevg01を通常の手順で作成します。ここでは、物理ボリュームとして/dev/sda4を使用していますが、これは環境に応じて変更してください。

# pvcreate /dev/sda4
# vgcreate imagevg01 /dev/sda4

 次に、このボリュームグループをストレージプールlvmpool01として定義します。最初のコマンドで定義した後に、次の2つのコマンドで、libvirtサービス開始時の自動有効化を設定した後に、このストレージプールを有効化しています。最後のコマンドの結果から実際にストレージプールが作成された事が確認できます。

# virsh pool-define-as lvmpool01 logical - - - imagevg01 /dev/imagevg01
# virsh pool-autostart lvmpool01
# virsh pool-start lvmpool01
# virsh pool-list
名前               状態     自動起動
-----------------------------------------
default              動作中  yes
lvmpool01            動作中  yes

 LVMによるストレージプールの場合、ストレージプールの有効化とは、ボリュームグループのアクティベートに対応します。これでストレージプールが用意できましたので、この中に任意のサイズのボリュームを作成していくことができます。次は、defaultとlvmpool01のそれぞれのストレージプール内に100MBのサイズのボリュームを作成する例です。

# virsh vol-create-as default imgdisk01.img 100M
# virsh vol-create-as lvmpool01 lvdisk01 100M

 次のコマンドで、作成されたボリュームを確認すると、それぞれ/var/lib/libvirt/images/以下のディスクイメージ・ファイル、およびボリュームグループimagevg01内の論理ボリュームとして実体が存在することがわかります。

# virsh vol-list default
名前               パス
-----------------------------------------
CLIENT01.img         /var/lib/libvirt/images/CLIENT01.img
imgdisk01.img        /var/lib/libvirt/images/imgdisk01.img

# virsh vol-list lvmpool01
名前               パス
-----------------------------------------
lvdisk01             /dev/imagevg01/lvdisk01

 厳密には、ストレージプールの種類によって、ボリュームを作成する際に指定できるオプションの違いなどはありますが、基本的には、ストレージプールの種類を意識せずにボリュームを作成できる事が分かります。この後は、「パス」に示された値を仮想マシンに接続する仮想ディスクとして指定するだけです。

 最後にもう一つ、iSCSI接続ディスクを利用したストレージプールも定義してみます。iSCSIストレージ装置としては、第2回に紹介したLinuxサーバーを利用したターゲットサーバーを利用することにします。

 ここでは、backing-storeとして、4種類のディスクイメージ・ファイル(volume01.img~volume04.img)を構成したものとします。ターゲットサーバーでtgtdサービスを起動したら、ストレージプールを作成するサーバ側で次のコマンドを実行します。最初のコマンドでは、ターゲットサーバーのIPアドレス(191.168.122.21)とターゲット名(iqn.2011-06.com.example.server01:tgt01)を指定しています。最後のコマンド結果からストレージプールiscsipool01が追加されたことがわかります。

# virsh pool-define-as iscsipool01 iscsi 192.168.122.21 - iqn.2011-06.com.example.server01:tgt01 - /dev/disk/by-path
# virsh pool-autostart iscsipool01
# virsh pool-start iscsipool01
# virsh pool-list
名前               状態     自動起動
-----------------------------------------
default              動作中  yes
iscsipool01          動作中  yes
lvmpool01            動作中  yes

 ストレージプールを有効化したタイミングで、iSCSIデーモン(iscsid)が起動して、iSCSI接続によるLUNの認識が行われます。今回は、4種類のLUNがあらかじめ用意されていましたが、これらは自動的にストレージプール内のボリュームとして認識されます。

 少し長いですが、下記のコマンド出力の「パス」の部分を仮想マシンに接続する仮想ディスクとして指定します。iSCSIストレージの場合は、KVMサーバー側で自由にボリュームを追加することはできません(注1)。

# virsh vol-list iscsipool01
名前               パス
-----------------------------------------
6.0.0.1              /dev/disk/by-path/ip-192.168.122.21:3260-iscsi-iqn.2011-06.com.example.server01:tgt01-lun-1
6.0.0.2              /dev/disk/by-path/ip-192.168.122.21:3260-iscsi-iqn.2011-06.com.example.server01:tgt01-lun-2
6.0.0.3              /dev/disk/by-path/ip-192.168.122.21:3260-iscsi-iqn.2011-06.com.example.server01:tgt01-lun-3
6.0.0.4              /dev/disk/by-path/ip-192.168.122.21:3260-iscsi-iqn.2011-06.com.example.server01:tgt01-lun-4

 以上の例から、さまざまな種類のストレージに対して、統一的な操作方法で仮想マシン用の仮想ディスク領域が用意できることがわかりました。libvirtのこの機能は、仮想化ハイパーバイザーの種類に依存するものではありません。つまり、libvirtに対応した仮想化ハイパーバイザーは、独自にストレージ管理機能を用意する必要がなくなります。

 libvirtに対応するハイパーバイザーは数種類ありますが、現状では、KVMとXen以外についてはまだ対応が十分とは言えない状況です。しかしながら、このような仮想ストレージや仮想ネットワークの機能を組み合わせることができるなど、libvirtに対応することは、ハイパーバイザーの開発者にとってもメリットがあります。今後、より多くの仮想化ハイパーバイザーがlibvirtから利用できるようになることを期待しておきましょう。第1回の最後に紹介したLXC(Linuxコンテナ)への対応も進められているようです。

 


注1)iSCSIのLUNに対して、ファイルシステムを作成してディスクイメージファイルを保存する場合やLVMの論理ボリュームを作成して利用する場合は、libvirtを使用せずに、通常の手順でiSCSIのLUNを認識させて利用します。

 

 

Python APIによるlibvirtの操作

 「Eucalyptus入門」第8回では、VMインスタンスをAPI経由で操作するためのライブラリが紹介されています。これらを利用して、RubyやPythonなどのプログラムからEucalyptusの環境を操作することで、クラウド環境の運用を自動化することができるわけです。

 これは、【図5-1】で言うと「クラウドAPI」を通して、Eucalyptus環境を操作することになります。一方、libvirtが提供するAPIは、【図5-1】の「仮想化API」に相当する部分になります。libvirtが標準で提供するライブラリを使用すると、C言語やPythonなどのプログラムから、libvirtが管理する仮想化レイヤーを操作することができます。Eucalyptusも内部的には、このAPIを使用して、仮想マシンの起動、停止などを行っています。

 libvirtのAPI使用方法を知っていると、Eucalyptusで問題が発生した時の問題判別の助けになります。あるいは、一般的なサーバー仮想化環境を扱う際にもさまざまな自動化が実現できるので便利です。筆者は以前、1台のサーバー上に100個以上の仮想マシンを構成するデモを実施した事がありますが、この時もlibvirtのAPIを利用して、さまざまな処理を自動化して行いました。ここでは、libvirtのPython APIライブラリを使用して、仮想マシンの起動、停止を行うプログラムの例を紹介します。

 Python APIを使用する時は、libvirtライブラリをimportした後に、libvirtクラスのopenメソッドでlibvirtが管理する仮想化ハイパーバイザーに接続します。リモートサーバー上の仮想化ハイパーバイザーに接続することも可能です。この時に得られる接続オブジェクトのメソッドを利用して、この環境に含まれる仮想マシンや前述の仮想ストレージなどの情報を取得することができます。また、仮想マシンオブジェクトを取得すると、このオブジェクトのメソッドによって、仮想マシンの操作が行えます。

 次は、停止中の仮想マシンに対する仮想マシンオブジェクトを取得して、各仮想マシンを起動するスクリプトと、起動中の仮想マシンに対する仮想マシンオブジェクトを取得して、各仮想マシンを停止するスクリプトの例です。

#!/usr/bin/python

import libvirt, time
Conn = libvirt.open( "qemu:///system" ) # ローカルのKVMハイパーバイザーに接続
for name in Conn.listDefinedDomains():  # 停止中の仮想マシン名のリストを取得
    vm = Conn.lookupByName( name )      # 仮想マシン名から仮想マシンオブジェクトを取得
    print "Starting " + vm.name()
    vm.create()                         # 仮想マシンを起動
    time.sleep( 1 )

 

#!/usr/bin/python

import libvirt, time
Conn = libvirt.open( "qemu:///system" ) # ローカルのKVMハイパーバイザーに接続
for id in Conn.listDomainsID():         # 起動中の仮想マシンIDのリストを取得
    vm = Conn.lookupByID( id )          # 仮想マシンIDから仮想マシンオブジェクトを取得
    print "Stopping " + vm.name()
    vm.shutdown()                       # 仮想マシンを停止
    time.sleep( 1 )

 仮想ストレージや仮想ネットワークについても基本的にはこれと同じ方法で操作ができます。仮想マシンオブジェクトの代わりに、ストレージプールオブジェクト、ボリュームオブジェクト、ネットワークオブジェクトなどを取得して、さまざまな操作を行います。libvirtのPython APIは、ライブラリ自身に詳細なリファレンス文書が含まれており、次のコマンドで表示することができますので、参考にしてください。

# pydoc libvirt

 

さいごに

 IaaSクラウドは、サーバー、ストレージ、ネットワークなどさまざまな仮想化技術を統合することで実現されています。

 この連載では、単純なものを組みあわせて複雑なものを創り上げていくという発想に何度か触れましたが、ただ組み合わせればうまくいくというものではありません。どのような技術をどのように組み合わせることで最適なクラウドを実現できるのか、すなわち、クラウド全体のアーキテクチャが重要になります。今回紹介した仮想化API(libvirt)とクラウドAPI(Eucalyptus)のレイヤー構造は、その一例と考えられます。

 もちろん、クラウドはまだまだ新しい技術ですので、そのアーキテクチャについてはこれからもさまざまな発展が予想されます。本連載では、クラウドを構成する基本技術の理解を深めることを目的としてきましたが、これらを足がかりとして、次はクラウド全体のアーキテクチャについて考えてみるのも面白いのではないでしょうか。

 


中井 悦司
素粒子論の研究、予備校講師、外資系ベンダーのインフラSE、Linux/OSSエバンジェリストと堅実なのか奔放なのかよくわからない経歴を重ねた後、Linux/OSSを愛するあまりレッドハットに転職。オープンソースの「エンジニアを幸せにする力」を信じながら、企業システムにおけるLinux/OSSの活用促進に情熱を注いでいます。
関連情報