LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第7回LXCの歴史と特徴

今回からはいよいよLXCの話題です。まずは開発の歴史と現在の開発体制を少し紹介したあと、LXC自身が持つ特徴を紹介したいと思います。

LXCの歴史

LXCはLinuxカーネルにコンテナ関連の機能が実装されはじめた2008年頃から、当時IBMフランスのDaniel Lezcano氏を中心にして、カーネルの機能を使用してコンテナの操作を行うためのツールとして開発がはじまっています。当時の文書を読むと、カーネルに実装されたコンテナ関連の機能をカーネル開発者が簡単に試せるようにすることを目的としていたようです。

その後、開発はかなり活発に行われ2009年にはバージョンは0.6に達しています。筆者が初めてLXCを使い始めたのは2009年の年末で0.6.4の頃です。この頃にはすでに簡単にコンテナの起動や管理ができていました。この後はAppArmorのサポートのようにセキュアにコンテナを使用するための機能など、実用に向けた機能が徐々に実装されていきました。

2012年にリリースされたUbuntu 12.04 LTSではUniverseリポジトリですがLXCパッケージが含まれており、この頃にはかなり安定して実用的にコンテナが扱えるツールに成長していました。この年にリリースされた0.8.0で、2008年から開発が進められたLXCとしてはほぼ完成の域に達していたと言えるかもしれません。Ubuntu 12.04 LTSに含まれるLXCは0.7.5ですが、0.8.0の新機能がバックポートされており、実質0.8.0と言っても良い機能を持っています。

この頃までのLXCは、主要機能を扱うコマンドはC言語で実装されていましたが、周辺の管理コマンド等はLinuxに通常含まれているコマンドのラッパーとして実装されているシェルスクリプトだったりしました。手間をかけずにあるものをうまく工夫して使っている所があり、必要最低限の機能が実装されているものの、少し寄せ集め感がありましたし、コマンドによっては柔軟な使い方ができないこともありました。

2013年9月にはDaniel Lezcano氏が多忙のため、メンテナがこの頃実質的に開発を引っ張っていたSerge Hallyn氏とStéphane Graber氏に交代し、開発もGitHub上で行われるようになりました[1]⁠。両氏ともにCanonical社のエンジニアですので、この頃から開発はUbuntuを中心に行われていくようになりました。

この後0.9.0の頃からAPIの整備が始まり、その後も1.0を安定版としてUbuntu 14.04 LTSに入れることを目標にAPIの整備が進められました。各種コマンドはそのAPIを使用して一貫性を持つ実装になり、APIを使った各種言語のバインディングも整備されていきました。

1.0の開発はリリース直前までかなり激しく機能が追加されていました。それでも無事2014年の2月20日にリリースされ、4月にリリースされたUbuntu 14.04 LTSでは目玉となる新機能のひとつとしてLXC 1.0が含まれました。

以上のようにUbuntuを中心にして開発が進んでいるため、Ubuntu以外で動かすときは問題があるのではないかと心配される方もいらっしゃるかもしれません。しかし、アクティブな開発者の中にはOracleのエンジニアやFedora上で開発をしている開発者もいますので、Ubuntu以外のディストリビューションでも問題なく動きますし、問題がある場合でもすぐに修正がされます。

LXCの特徴

通常はLXCの特徴というとコンテナが持つ特徴をあげることが多いです。しかしそれは第2回ですでに紹介していますので、ここではLXC自身の特徴をいくつか紹介したいと思います。

豊富なテンプレート

コンテナを使用する際は、仮想マシンの時のようにCD、DVD、ISOイメージなどを用いて一からインストールできません。コンテナが起動したときの/(ルート)以下のディレクトリツリー(以降、コンテナイメージ)を作り、起動に必要な設定を行うのは少し面倒です。またコンテナとして起動させるために設定ファイルのカスタマイズが必要な場合もあります。

そこでLXCにはコンテナを作成するためのスクリプトとしてテンプレートが付属しています。テンプレートを使うと、簡単にコンテナイメージを作成し、コンテナの起動に必要な変更を行い、設定ファイルを作成できます。このテンプレートはコンテナを作成するためのlxc-createコマンドから直接使えます。

各種ディストリビューションに対応したテンプレートは、そのテンプレートと同じディストリビューション上で動かすのが基本ですが、DebianやUbuntuのように多数のパッケージが準備されているディストリビューションだと、rpmやyumのパッケージをインストールしてFedoraやCentOSのコンテナを作成できます。

ディストリビューション用のテンプレートだけでなく、アプリケーションコンテナとしてsshdやbusyboxを起動させるためのテンプレートも付属しています。

テンプレートは全てシェルスクリプトで書かれていますので簡単にカスタマイズできます。実際、LXC 1.0に含まれているlxc-plamoというテンプレートは、筆者がubuntu用のテンプレートなどの他のテンプレートを真似しながら作ったものをマージしてもらいました。

1.0に標準で含まれるテンプレートは以下の17個です。

$ ls /usr/share/lxc/templates/
lxc-alpine     lxc-centos    lxc-fedora        lxc-oracle  lxc-ubuntu-cloud
lxc-altlinux   lxc-cirros    lxc-gentoo        lxc-plamo
lxc-archlinux  lxc-debian    lxc-openmandriva  lxc-sshd
lxc-busybox    lxc-download  lxc-opensuse      lxc-ubuntu

テンプレートの中も少し紹介しておきましょう。以下は17個のうちで一番シンプルなlxc-sshdテンプレートの一部です。ご覧のようにシェルスクリプトで書かれていますし、sshdを起動するのに必要なファイルを作成したりしているだけで、複雑なことをしているわけでもありませんので、これを見ながらカスタマイズを行うのもそれほど難しくはありません。


  :
  :(略)
  :
configure_sshd()
{
    rootfs=$1

    cat <<EOF > $rootfs/etc/passwd
root:x:0:0:root:/root:/bin/bash
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
EOF

    cat <<EOF > $rootfs/etc/group
root:x:0:root
sshd:x:74:
EOF

ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key
ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key

    # by default setup root password with no password
    cat <<EOF > $rootfs/etc/ssh/sshd_config
Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
UsePrivilegeSeparation yes
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords yes
ChallengeResponseAuthentication no
EOF

    if [ -n "$auth_key" -a -f "$auth_key" ]; then
        u_path="/root/.ssh"
        root_u_path="$rootfs/$u_path"
        mkdir -p $root_u_path
        cp $auth_key "$root_u_path/authorized_keys"
        chown -R 0:0 "$rootfs/$u_path"
        chmod 700 "$rootfs/$u_path"
        echo "Inserted SSH public key from $auth_key into $rootfs/$u_path"
    fi

    return 0
}
  :
  :(略)
  :
install_sshd $rootfs
if [ $? -ne 0 ]; then
    echo "failed to install sshd's rootfs"
    exit 1
fi

configure_sshd $rootfs
if [ $? -ne 0 ]; then
    echo "failed to configure sshd template"
    exit 1
fi

copy_configuration $path $rootfs $name
if [ $? -ne 0 ]; then
    echo "failed to write configuration file"
    exit 1
fi

イメージのダウンロード

テンプレートには、手元でコンテナイメージを作成するためのもの以外にlxc-downloadという名前のテンプレートが付属しており、あらかじめ構築されたコンテナイメージをLXCのサイトからダウンロードしてコンテナを作成できます。

この機能は1.0リリース前に準備されました。あとで紹介する一般ユーザでコンテナの利用する場合は、テンプレートを使ってコンテナイメージの作成が難しいためです。もちろんroot権限でコンテナを利用する場合も利用できます。

このコンテナイメージは、LXCに付属しているテンプレートを使って定期的にLXCのサーバ上で構築されていますので、各種ディストリビューションのテンプレートを使って作成した場合と同じイメージが、手元のホスト環境で使用しているディストリビューションに関わらず使用できます。

Dockerではあらかじめ作成したイメージが提供されており、それを利用してコンテナを起動します。LXCでもダウンロード用のテンプレートを使ってそれと同じことができます。

執筆時点で準備されているダウンロード用のイメージは以下の通りです。

ディストリビューションバージョンアーキテクチャバリアント
CentOS6amd64 i386default
7amd64default
Debianjessie sid wheezyamd64 armel armhf i386default
Fedora19 20amd64 armhf i386default
Gentoocurrentamd64 armhf i386default
openSUSE12.3amd64 i386default
Oracle6.5amd64 i386default
Plamo5.xamd64 i386default mini
Ubuntulucidamd64 i386default
preciseamd64 armel armhf i386default
trusty utopicamd64 arm64 armhf i386 ppc64eldefault

「バリアント」とは

ディストリビューションによってはコンテナイメージ内に含まれるパッケージが異なる複数のイメージが準備されていることがあります。このイメージごとに名前が付けられており、これを「バリアント」と呼んでいます。ダウンロードテンプレートのオプションとして与えることができます。デフォルトでは "default" というバリアントが使われます。

APIと各種言語のバインディングの提供

LXCにはlxc-で始まるコンテナ用のコマンドが付属しています。これらは全てLXCライブラリのAPI(liblxc1)を使用して作られており、一部は後述のスクリプト言語のバインディングで書かれています。

このAPIを使って自分でアプリケーション内からコンテナが操作できます。また、このAPIを使用した各種言語のバインディングが提供されていますので、以下の言語からコンテナが操作できます。

  • Python3
  • Lua
  • Go
  • Ruby

このうち、Python3とLuaはLXCのソースに同梱されており、GoとRubyは別に配布されています。なおLXCのGoバインディングは、Go言語で書かれたDockerとは全く無関係です。また、最近githubのLXCプロジェクトにPython 2.7用のリポジトリができていました[2]⁠。

LXCでインストールされるコマンドのいくつかはPython3とLuaで書かれています。

Python3で書かれているコマンド
lxc-device, lxc-ls, lxc-start-ephemeral
Luaで書かれているコマンド
lxc-top

いろいろなストレージバックエンドのサポート

ストレージバックエンドは、LXCのマニュアルでは"backingstore"として説明されており、コンテナイメージの置き場として使う領域の形式です。デフォルトではファイルシステム上のディレクトリが使われますが、ここにさまざまなファイルシステムやデバイスを使い、それぞれの特徴を活かして管理もできます。

1.0でサポートされているストレージバックエンドは以下です。

  • ディレクトリ (デフォルト)
  • btrfs
  • zfs
  • lvm
  • ループデバイス
  • aufs
  • overlayfs

作成時やクローン、スナップショットの時にストレージバックエンドを指定すると、それぞれのストレージバックエンドに応じた処理がなされます。たとえばコンテナ用の領域としてbtrfsの領域を準備してコンテナを作成すると、コンテナ専用のsubvolumeが作成され、そこにコンテナイメージを作成します。

またlvmを使用する場合、その上に作成するファイルシステムは自由に選べますので、ここにないファイルシステムも使えます。

クローンとスナップショット

テンプレートを使ってコンテナを新規に作成するのでなく、既にあるコンテナをクローンして作成できます。デフォルトではrsyncを使ってコピーされます。

btrfsやzfsやlvmのような多機能なストレージバックエンドを使っている場合は、そのファイルシステム実装されているスナップショット機能を使ったりして、ストレージバックエンドの特徴を活かしたクローンが可能です。

通常のディレクトリを使用している場合でも、カーネルがサポートしていれば、aufsやoverlayfsと言った重ね合わせが可能なファイルシステムを使い、容量を節約したり、差分管理を行ったりできます。

また、あるコンテナの状態をスナップショットとして保存できます。クローンと違ってスナップショットは取得元のコンテナに紐付いた状態になり、コンテナをスナップショット時点のものにリストアできます。

セキュリティ

コンテナごとの隔離がされていない部分などに対するセキュリティの確保や、コンテナを安全に使用するための機能としてAppArmor、SELinuxがサポートされています。

また、seccompを使ってシステムコールのフィルタがコンテナごとに行えますし、コンテナごとに与えたり削除したりするケーパビリティを指定できます。

一般ユーザでのコンテナの起動

第2回で説明したユーザ名前空間を使った一般ユーザでのコンテナの作成と起動が可能です。

root権限で実行するコンテナと全く同じことが一般ユーザでできるわけではありませんが、ホスト環境や他のコンテナに対してはセキュアになりますし、root権限を持たない一般ユーザでもコンテナの起動ができることで、利用の幅が大きく広がるのではないでしょうか。

ネストしたコンテナ

コンテナのネストが可能です。つまりLXCで作成し起動したコンテナの中でコンテナが動作します。

日本語マニュアルの付属

これは機能ではありませんし、JM Projectの成果としてLinuxの基本的なコマンドは日本語のオンラインマニュアルが提供されているので、マニュアルが日本語で読めるからといって珍しいことでもないのですが、筆者が貢献した成果なのでぜひ紹介させてください。(^_^)

LXC 1.0からは日本語のオンラインマニュアルが付属していますので、日本語環境では日本語でマニュアルが読めます。現時点で英語以外の言語でマニュアルがあるのは日本語だけです[4]⁠。

LXCの気になる点

LXCの改良すべき点はたくさんあると思います。しかし、こんな短所があるからLXCが使いづらい!という点は筆者はあまり思いつきません。

短所は思いつきませんが気になる点はあります。現時点で筆者が一番気になる所はcgroupの設定です。

コンテナに対してcgroupでリソース制限をかける場合、第4回第5回で一部説明した、各サブシステムの設定を行うcgroupfs以下のファイル名や、ファイルに書き込むべき内容をそのまま設定ファイルに書くことになります。

たとえばdevicesサブシステムの場合、devices.allowdevices.denyというファイルに許可したいデバイス用のアクセス権を設定しました。これをLXCで設定すると以下のようになります。

$ cat /usr/share/lxc/config/ubuntu.common.conf
  : (略)
# Default cgroup limits
lxc.cgroup.devices.deny = a
## Allow any mknod (but not using the node)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
## /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
  : (略)

lxc.cgroupに続いて、cgroupfsにできるファイル名をそのまま続けて設定のキーとしています。値はそのファイルに書き込む設定そのままです。

カーネルのバージョンやディストリビューションの設定によってcgroupfs以下に現れるファイルが異なることもあります。細かくコンテナのリソース制御を行いたい場合にはcgroupの知識が必要なことに加えて、使っているカーネルのcgroupを設定する知識も必要になる可能性があります。これは少し敷居が高い気がします。

この辺りを改善する提案はでていましたので、バージョン1.1が出る頃にはもう少し簡単にリソース制限が行えるようになっているかもしれません。

まとめ

今回はLXC開発の歴史について紹介したあと、LXC自身の持つ特徴をいくつか紹介しました。コンテナという仕組みが持つ以外のLXCが持つ特徴についておわかりいただけたのではないかと思います。

次回からは今回紹介した特徴の説明も含めて、LXCの機能を実際に紹介していく予定です。機能の紹介はUbuntu 14.04 LTS上で行っていきたいと思いますので、Ubuntuに依存したLXC環境の話も含めて説明していく予定です。

Ubuntuを使った基本的な機能の紹介が済んだ後に、Ubuntu以外のディストリビューションでのLXCの構築や活用のお話をする予定です。

最近のLXCのリリース

第4回の記事を執筆中にLXC 1.0.4がリリースされ(6月13日⁠⁠、前回(第6回)の記事を執筆中にLXC 1.0.5がリリースされています(7月14日⁠⁠。

1.0系列は仕様は変わらずバグフィックスのみ行う方針ですので、細かいバグフィックスが中心ですが、1.0.4ではsystemdベースのディストリビューションでの問題を解決するために若干仕様が変わっていたり、1.0.4, 1.0.5の両方で、まだまだ変化しているLinuxカーネルの仕様に合わせるために内部的な動きを変更したりしています。

一方で次のバージョンとなる1.1の話もメーリングリストでは話題にのぼりはじめました。

1.0系列の各バージョンでの変更点については日本語サイトでも翻訳して紹介していますのでご覧ください。

おすすめ記事

記事・ニュース一覧