レベル: 中級 Matt Helsley, Linux Kernel Engineer, IBM
2009年 02月 03日 コンテナーを利用すると、完全仮想化に必要な命令解釈メカニズムなどの複雑な仕組みを用意しなくても軽量な仮想化を実現することができ、プロセスやリソースを分離することができます。LXC (Linux® Containers) と呼ばれるコンテナー・ツールを紹介するこの記事では、LXC の設定方法と実行方法を、順を追って説明します。
コンテナーは、1 つのオペレーティング・システムが管理するリソースを個々のグループへと効率的に分割し、そのグループ間で競合する (リソースの使用に対する) 要求を適切に調整します。コンテナーの場合は仮想化とは対照的に、命令レベルのエミュレーションも JIT コンパイルも必要ありません。コンテナーはコア CPU の命令をそのまま実行することができ、特別な解釈メカニズムを必要としません。また準仮想化やシステム・コールのサンクに伴う複雑さもありません。
オペレーティング・システムがコンテナーの作成手段やコンテナーへの入力手段を提供できると、各アプリケーションはまるで別のマシンで実行されているかのように動作する上、使用するリソースの多くを共有することができます。例えば共通ファイル (glibc など) のページ・キャッシュを効率的に共有することができます。これができるのは、すべてのコンテナーが同じカーネルを使用し、またコンテナーの構成に応じて同じ libc ライブラリーを何度も使用するからです。こうした共有動作は多くの場合、ディレクトリーの中にある書き込み不要の他のファイルにも拡張することができます。
このようにリソースを共有することでリソースの節約が図られ、その一方でリソース同士を分離することもできるということは、コンテナーを利用することによるオーバーヘッドが真の仮想化によるオーバーヘッドよりも大幅に少ないことを意味しています。
コンテナー技術はずっと以前からありました。Solaris Zones や BSD jails は、Linux 以外のオペレーティング・システムでのコンテナーの例です。Linux 用のコンテナー技術にも同様に、Linux-Vserver や OpenVZ、FreeVPS など、さまざまなものがあります。これらの技術はそれぞれが成熟していますが、こうしたソリューションには主流の Linux カーネルにコンテナー・サポートを統合するというほどの大発展はありませんでした。
Serge Hallyn による「Secure Linux containers cookbook」(developerWorks、2009年2月) では、SELinux と Smack ポリシーによって軽量コンテナーを本格的に強化する方法を解説しています。こうした技術の詳細については「参考文献」 を参照してください。
それらとは対照的に、Linux Resource Containers プロジェクト (IBM の Daniel Lezcano が開発および保守を行っています。コードは「参考文献」を参照) では、主流の Linux カーネルにコンテナーを実装しようとしています。このようにコンテナーを Linux カーネルに追加する方法は、成熟した Linux コンテナー・ソリューションにも有用かもしれません (成熟したコンテナー・プロジェクトに対して共通のバックエンドを提供することができます)。この記事では、LXC プロジェクトで作成されたツールの使い方を簡単に紹介します。
この記事を最大限に活用するためには、コマンドラインを使って make や gcc、patch などのプログラムを実行する方法に慣れている必要があります。また tarball (*.tar.gz ファイル) を展開する方法にも慣れている必要があります。
LXC の入手とビルド、そしてインストール
LXC プロジェクトは Linux カーネルのパッチとユーザー空間ツールで構成されています。ユーザー空間ツールは、パッチによってカーネルに追加される新機能を利用して、コンテナーを操作するために単純化されたツール・セットを提供します。
LXC を使用するためには、Linux カーネルのソース・コードをダウンロードして適切な LXC パッチを適用し、そのコードをビルドしてインストールし、それから Linux カーネルを起動する必要があります。次に LXC ツールをダウンロードしてビルドし、インストールします。
私はパッチを当てた Linux 2.6.27 カーネルを使いました (「参考文献」にリンクがあります)。2.6.27 Linux カーネル用の lxc パッチは皆さんが使用しているディストリビューションのカーネルのソースにはおそらく適用できませんが、2.6.27 の次バージョン以降の Linux には、このパッチで提供される機能の大部分が含まれている可能性があります。そのため、最新のパッチと主流のカーネル・ソースを使うことを強くお薦めします。またカーネルのソース・コードをダウンロードしてパッチを当てる代わりに、次のように git を使ってコードを入手することもできます。
git clone git://git.kernel.org/pub/scm/linux/kernel/git/daveh/linux-2.6-lxc.git
|
カーネルへのパッチの当て方、カーネルの構成、ビルド、インストール、そして起動の方法は kernelnewbies.org に説明されています (「参考文献」にリンクがあります)。
LXC ではカーネルを特別な方法で構成する必要がありますが、LXC 用にカーネルを適切に構成するためには、make menuconfig を使用して Container support を選択する方法が最も簡単です。そしてカーネルがサポートする機能に応じて他の構成オプションを選択します。
作業対象としての LXC 環境
コンテナーをサポートするカーネルの他に、コンテナーの起動や管理を容易に行うためのツールが必要です。この記事でコンテナーを管理するために使用する主なツールは liblxc に含まれています (「参考文献」にリンクがあります。また別の選択肢として libvirt も参照してください)。このセクションでは以下の内容について説明します。
- liblxc ツール
- iproute2 ツール
- ネットワークの構成方法
- コンテナー・ファイルシステムを作成する方法 (そのためにカスタムの Debian コンテナーを作成する方法と ssh コンテナーを実行する方法があります)
- コンテナー・ファイルシステムに接続する方法 (SSH、VNC、tty による VT (仮想端末)、GUI による VT)
liblxc ツール
liblxc をダウンロードして解凍し (「参考文献」を参照)、次に liblxc ディレクトリーの中で下記を実行します。
./configure --prefix=/
make
make install
|
ソース RPM のビルドに慣れている方はソース RPM を入手することもできます (「参考文献」を参照)。
iproute2 ツール
コンテナーの中でネットワーク・インターフェースを操作するためには iproute2 パッケージのバージョン 2.6.26 以降が必要です (「参考文献」を参照)。このパッケージが Linux のディストリビューションに含まれていない場合には、パッケージ・ソースの tarball の指示に従ってこのパッケージをダウンロードして構成し、ビルドを行ってインストールします。
ネットワークを構成する
多くの機能的なコンテナーが持つもう 1 つの重要なコンポーネントが、ネットワーク・アクセスのためのコンポーネントです。コンテナーをネットワークに接続するための方法として、現在最も優れた方法がブリッジング (複数のイーサネット・セグメントを接続して 1 つのイーサネット・セグメントに見えるようにする方法) です。LXC を使うための準備としてブリッジを作成し (「参考文献」を参照)、そのブリッジを使って実際のネットワーク・インターフェースとコンテナーのネットワーク・インターフェースを接続します。
br0 という名前のブリッジを次のように作成します。
brctl addbr br0
brctl setfd br0 0
|
既存のネットワーク・インターフェースの IP (この例では 10.0.2.15) とのブリッジ・インターフェースを作成します (ifconfig br0 10.0.2.15 promisc up)。既存のネットワーク・インターフェース (この例では eth0) をブリッジに追加し、そのインターフェースと IP アドレスとが直接関連付けられている状態を解除します。
brctl addif br0 eth0
ifconfig eth0 0.0.0.0 up
|
すると、br0 というブリッジに追加されるすべてのインターフェースは、この IP アドレスに応答するようになります。最後に、デフォルト・ルートがパケットをデフォルト・ゲートウェイに送信するようにします (route add -net default gw 10.0.2.2 br0)。後でコンテナーを構成する際には、外部へのリンクとして br0 を指定します。
コンテナー・ファイルシステムを作成する
ネットワークの構成に加え、コンテナーが独自のファイルシステムを必要とする場合がよくあります。コンテナー・ファイルシステムを作成する方法は、ニーズに応じたいくつかの方法があります。ここでは次の 2 つの方法を説明します。
- カスタムの Debian コンテナーを作成する方法
- ssh コンテナーを実行する方法
カスタムの Debian コンテナーを作成する方法は debootstrap コマンドを使えば非常に単純です。
debootstrap sid rootfs http://debian.osuosl.org/debian/
|
大量のコンテナーを作成する場合には、最初にパッケージを tarball の中にダウンロードすると時間を節約することができます (debootstrap --make-tarball sid.packages.tgz sid http://debian.osuosl.org/debian/)。一例として、このコマンドによって約 71MB のサイズ (圧縮されて 52MB) を持つ .tar ファイルが生成されますが、ルート・ディレクトリーは 200MB 近くを消費します。次に rootfs の中でルート・ディレクトリーの作成を開始します (debootstrap --unpack-tarball sid.packages.tgz sid rootfs)。(もっと小さな、あるいはもっと適切なコンテナーを作成するための詳細な方法が debootstrap の manpage に説明されています。)
これにより、環境を作成されますが (「参考文献」を参照)、この環境はホスト・コンテナーとしてはかなり冗長です。
ssh コンテナーを実行すると、コンテナー・ファイルシステム専用のディスク・スペースを大幅に削減することができます。例えばこの方法では、ほんの何キロバイトしか使わずに、さまざまなコンテナーのポート 22 で複数の ssh デーモンを実行することができます (例については「参考文献」を参照)。ssh コンテナーはこれを実現するために、重要なディレクトリー (/bin、/sbin、/lib など) を読み取り専用でバインド・マウントすることで既存の Linux システムの sshd パッケージの内容を共有します。またネットワーク名前空間を使用することで、必要最低限の読み書き可能な内容が作成されます。
こうした軽量コンテナーを生成するために使われる手法は、主に chroot 環境を生成するために使われます。この手法が他の手法と異なる点は、読み取り専用のバインド・マウントと名前空間を使用する点にあり、それによって chroot 環境の分離機能が強化され、有効なコンテナーになります。
次に、コンテナーに接続するための方法を選択する必要があります。
コンテナーに接続する
次のステップはコンテナーに接続することです。コンテナーをどのように構成するかによって、いくつかの方法があります。
- SSH
- VNC (GUI)
- tty (テキスト) による VT
- X (GUI) による VT
SSH で接続する方法は、コンテナーに GUI インターフェースが必要ない場合には適切です。この場合は簡単な ssh 接続で十分です (上記の「ssh コンテナーを実行する方法」を参照してください)。この方法のメリットは IP アドレッシングを利用して、ほぼいくつでもコンテナーを作成できる点です。
ssh 接続がパスワード入力を促す段階に達するまで長い時間がかかる場合には、DNS ルックアップ中に Avahi マルチキャスト DNS/Service Discovery デーモンがタイムアウトしている可能性があります。
VNC (Virtual Network Computing) を使って接続すると、コンテナーに GUI インターフェースを追加することができます。
それには vnc4server を使用して、VNC クライアントを提供するため専用に X サーバーを起動しますが、まずは vnc4server をインストールし、コンテナーの /etc/rc.local ファイルからvnc4server を起動します (例えば echo '/usr/bin/vnc4server :0 -geometry 1024x768 -depth 24' >> rootfs/etc/rc.local など)。すると、コンテナーの起動時に X の画面が 1024 x 768 の解像度と 24 ビット色で表示されます。次に接続を行いますが、接続は次のように非常に簡単です。
tty (テキスト) による VT を使って接続する方法は、コンテナーがホストと tty を共有する場合に便利です。この場合は Linux の VT を使ってコンテナーに接続することができます。VT の最も簡単な使い方は、まず共有されている tty デバイス (多くの場合は Linux の VT に対応します) の 1 つにログインします。このログイン・プロセスは getty と呼ばれます。VT 8 を使うためには次のようにします。
echo '8:2345:respawn:/sbin/getty 38400 tty8'
>> rootfs/etc/inittab
|
コンテナーは起動すると tty8 に対して getty を実行します。それによってユーザーはそのコンテナーにログインできるようになります。似たような手法を使うと、LXC ツールを使ってコンテナーを再起動することができます。
この手法ではコンテナーの GUI を有効にすることはできません。また、一度に 1 つのプロセスしか tty8に接続できないため、複数のコンテナーを有効にするためには構成を追加する必要があります。
X による VT を使って接続すると GUI を実行することができます。VT 9 に対して gdm (GNOME Display Manager) を実行し、次に rootfs/usr/share/gdm/defaults.conf を編集して FirstVT=7 を FirstVT=9 で置き換え、そして VTAllocation=true を VTAllocation=false で置き換えます。
この方法で GUI を有効にすることはできますが、限られた数の Linux 仮想端末の 1 つを使用してしまうことは同じです。
LXC ツールを実行する
これで適切なカーネルが実行されるようになり、LXC ユーティリティーがインストールされ、動作環境が用意されたので、この環境のインスタンスの管理方法を学ぶことにしましょう。(ヒント: ここで説明する内容の大部分は LXC の README に詳細に説明されています。)
LXC は cgroup ファイルシステムを使ってコンテナーを管理します。LXC を使う前に、まずこのファイルシステムをマウントする必要があります (mount -t cgroup cgroup /cgroup)。cgroup ファイルシステムは任意の場所に置くことができます。LXC は /etc/mtab にマウントされている最初の cgroup ファイルシステムを使います。
この記事のここから先では LXC の基本とおよびさまざまなポイントを説明し、さらに下位レベルのアクセスについて説明します。
LXC の基本
ここでは LXC ツールを使うための基本として、以下の内容について説明します。
- コンテナーを作成する方法
- 既存のコンテナーに関する情報を収集 (つまりリストアップ) する方法
- システム・コンテナーとアプリケーション・コンテナーを起動する方法
- コンテナー内で実行されているプロセスにシグナルを送る方法
- コンテナーを一時停止、再開、停止、破棄する方法
コンテナーを作成すると、名前と構成ファイルが関連付けられます。この名前は 1 つのコンテナーを管理するために使われます。
lxc-create -n name -f configfile
|
こうすることによって複数のコンテナーが同じ構成ファイルを同時に使うことができます。構成ファイルの中で、コンテナーの属性 (ホスト名やネットワーク、ルート・ファイルシステム、fstab など) を指定します。(構成を作成してくれる) lxc-sshd スクリプトを実行すると、ssh コンテナーの構成は次のようなものになります。
lxc.utsname = my_ssh_container
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = br0
lxc.network.ipv4 = 10.0.2.16/24
lxc.network.name = eth0
lxc.mount = ./fstab
lxc.rootfs = ./rootfs
|
構成ファイルがどのようなものであるかによらず、LXC ツールによって起動されたコンテナーは、システム中のプロセスに関する独自のビュー、独自のマウント・ツリー、利用可能な IPC (InterProcess Communication: プロセス間通信) リソースに対する独自のビューを持ちます。
コンテナーが起動したときに、こうした構成に含まれていないすべてのリソースは、ホストと共有される前提になります。そのため管理者はコンテナーとホストとの間の重要な違いを簡潔に特定できるようになります。また構成を移植することができるようにもなります。
既存のコンテナーに関する情報をリストアップする機能はコンテナーを管理する上で欠かせない機能です。特定のコンテナーの状態を表示するためには次のようにします。
コンテナーの一部であるプロセスを表示するためには次のようにします。
コンテナーを起動する LXC ではシステム・コンテナーとアプリケーション・コンテナーという 2 つのタイプのコンテナーが区別されます。システム・コンテナーは仮想マシンと似ています。真の仮想化とは対照的に、システム・コンテナーは分離を犠牲にすることでオーバーヘッドを少なくしています。これは、同じ Linux カーネルをすべてのコンテナーで利用することによる直接の結果です。システム・コンテナーは仮想マシンと同様に、Linux ディストリビューションが起動する場所と同じ場所で起動します。システム・コンテナーを起動するためには次のように init プログラムを実行します。
アプリケーション・コンテナーはシステム・コンテナーとは対照的に、1 つのアプリケーションを分離するために必要な、個々の名前空間のみを作成します。アプリケーション・コンテナーを起動するためには次のようにします。
コンテナーにシグナルを送る コンテナー内で実行されているすべてのプロセスに対してシグナルを送るためには次のようにします。
lxc-kill -n name -s SIGNAL
|
コンテナーを一時停止する コンテナーを一時停止する方法は、概念としてはコンテナー内のすべてのプロセスに SIGSTOP シグナルを送る方法と似ています。しかし誤った SIGSTOP シグナルを送ると一部のプログラムを混乱させる可能性があります。そのため LXC は cgroup インターフェースで利用できる Linux プロセス・フリーザーを使います。
コンテナーを再開する フリーズされたコンテナーを再開するためには次のようにします。
コンテナーを停止する コンテナーを停止すると、そのコンテナー内で起動されたすべてのプロセスが終了し、そのコンテナーがクリーンアップされます。
コンテナーを破棄する コンテナーを破棄すると、lxc-create ステップで名前と関連付けられた、構成ファイルとメタデータが削除されます。
その他の項目
知っておいた方がよい、さまざまな操作を以下に挙げます (一部は監視に関連しています)。
コンテナーの優先順位の表示と調整は次のように行います。
lxc-priority -n name
lxc-priority -n name -p priority
|
コンテナーの状態と優先順位の変化を連続的に監視するためには次のようにします。
コンテナーの監視を停止するためには Ctrl-C を押します。
コンテナーの状態が、ある一連の状態の 1 つになるのを待つためには各状態を | で区切ります。
lxc-wait -n name -s states
|
RUNNING 以外のすべての状態になるのを待つためには次のようにします。
lxc-wait -n name -s 'STOPPED|STARTING|STOPPING|ABORTING|FREEZING|FROZEN'
|
これはもちろん、即座に制御が戻りますが、予想外のエラーを防ぐためには、コンテナーが指定の状態になった場合にのみ lxc-wait から制御が戻るようにする必要があります。
下位レベルのアクセス
LXC は cgroup ファイルシステムを使ってコンテナーを管理します。LXC を使うと cgroup ファイルシステムのさまざまな部分を読み取ったり操作したりすることができます。例えば各コンテナーの CPU 使用率を管理するためには、次のようにコンテナーの cpu.shares を読み取って調整します。
lxc-cgroup -n name cpu.shares
lxc-cgroup -n name cpu.shares howmany
|
まとめ
この基本的なガイドでは、LXC ツールを使うための基本を説明しました。これで皆さんも独自の効果的なリソース・パーティションを作成することができるはずです。
この資料は Defense Advanced Research Projects Agency (米国防総省国防高等研究計画局) が Agreement No. HR0011-07-9-0002 の下に行った作業の成果を基にしたものです。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Matt Helsley は IBM の Linux Technology Center のソフトウェア・エンジニアです。 |
記事の評価
|