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

第14回LXCの構築・活用 [2] ― コンテナを作成・構築する

Plamoテンプレートを使ったコンテナの作成

前回Plamoテンプレートlxc-plamoを題材に、テンプレートの中身を詳しく紹介しました。テンプレートは、lxc-createコマンドを使ってコンテナを作成するときに使われます。以下のように実行するのでしたね。

~$ sudo lxc-create -n ct01 -t plamo

これで、ct01という名前のPlamoコンテナ環境が作成されました。このコンテナは、root権限で利用することになります。では次に、root権限ではなく、一般ユーザ権限で利用するコンテナを作成しましょう。

コンテナの作成に先立ち、あるユーザに紐づいたUID/GIDの範囲の指定を行うため、root権限でユーザに対する/etc/{subuid,subgid}の設定が必要です。ここでは、100000から65536個のUID/GIDをtaroというユーザに割り当てます。

~$ sudo usermod -v 100000-165535 -w 100000-165535 taro
~$ cat /etc/subuid
taro:100000:65536
~$ cat /etc/subgid
taro:100000:65536

また、lxc-createコマンド実行時に読み込まれるtaroユーザ用のデフォルトファイル~/.config/lxc/default.confを用意します。後で説明するネットワークの設定と併せて、以下のように設定します。

~$ cat ~/.config/lxc/default.conf
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up

これらの非特権コンテナに関する詳細は、この連載の後の回で説明する予定です。さて、準備ができたので、以下のように実行してみましょう。

~$ lxc-create -n ct01 -t plamo
This template can't be used for unprivileged containers.
You may want to try the "download" template instead.
lxc_container: container creation template for ct01 failed
lxc_container: Error creating container ct01

コンテナの作成に失敗してしまいました。そうです。一般ユーザ権限では、通常のテンプレートを使ったコンテナ環境の作成が難しいので、非特権コンテナの作成が制限されています。そこで、ダウンロードテンプレートを使って実行することになります。

~$ lxc-create -n ct01 -t download -- -d plamo -r 5.x -a amd64

アーキテクチャにamd64と指定するあたりは、⁠Plamo Linuxでは、そのような名前で呼ばれていないので)少々気持ち悪いと感じるかも知れませんが、ここでは気にしないことにします。

LXCのダウンロード用サーバから、作成済みコンテナイメージをダウンロードして、コンテナ環境が作成されました。コンテナ環境を作成した一般ユーザ権限で(正確には、lxc.id_mapで設定したユーザ名前空間内で)コンテナを利用することができます。

一般ユーザ権限で作成したコンテナ環境

ダウンロードした作成済みコンテナイメージは、基本的にPlamoテンプレートlxc-plamoなどのテンプレートを実行して、作成したコンテナのルートファイルシステムをtar.xzで固めているだけのものです。それでは、root権限で通常のテンプレートを使って作成したコンテナ環境と、一般ユーザ権限でダウンロードテンプレートを使って作成したコンテナ環境とでは、どのような違いがあるのでしょうか。

まず、作成したコンテナのルートファイルシステム上のファイル/ディレクトリのUID/GIDが、通常のコンテナ環境と異なります。

lxc-createコマンドを実行すると、内部でテンプレートが呼び出されます。このとき、一般ユーザ権限で実行した場合は、lxc-usernsexecコマンドの引数として、テンプレートが呼び出されます。このコマンドを実行すると、lxc.id_mapで設定したユーザ名前空間内で、rootとしてタスクが実行されます。その結果、コンテナ内のファイル/ディレクトリは、lxc.id_mapで設定したユーザ名前空間内にマッピングしたUID/GIDで、コンテナのルートファイルシステム上に展開されます。

なお、lxc.id_mapで設定したユーザ名前空間において、キャラクタデバイスやブロックデバイスを作成するmknodは実行できません。そのため、作成済みコンテナイメージを展開する際、mknodの実行をともなう処理はスキップして展開されます。代わりに、ユーザ名前空間向けに追加した個別の設定/usr/share/lxc/config/plamo.userns.confでバインドマウントを行うことで、スキップしたデバイスを見せています。

このように、一般ユーザ権限でダウンロードテンプレートを使って作成したコンテナ環境では、root権限で通常のテンプレートを使って作成したコンテナ環境とほぼ同等の環境になるように、いろいろ工夫されていることがおわかりいただけたかと思います。

Plamoコンテナイメージのバリアント

現在、LXCのダウンロード用サーバには、Plamo Linuxの作成済みコンテナイメージのバリアントとして、defaultminiが用意されています。先に示した例では、デフォルトのdefaultバリアントのコンテナイメージが使われます。一般ユーザ権限で、miniバリアントのコンテナ環境を作成する場合は、以下のように実行します。

~$ lxc-create -n ct01 -t download -- -d plamo -r 5.x -a amd64 --variant=mini

また、Plamo Linuxの個別ミラーサーバでは、上記以外にlargeバリアントとfullバリアントのコンテナイメージも用意されています。以下のように実行することで、Plamo Linuxの個別ミラーサーバから作成済みコンテナイメージをダウンロードできます。

~$ lxc-create -n ct01 -t download -- -d plamo -r 5.x -a amd64 --variant=large \
>     --server=repository.plamolinux.org --keyid=0xC0B578C84772EC0D

各バリアントのパッケージ構成は以下のようになっています。

バリアント 作成済みコンテナイメージのパッケージ構成 サイズ
default 00_base01_minimumカテゴリのパッケージを収録し、若干調整した構成 約240MB
mini 00_base03_xclassicsカテゴリのパッケージを収録し、若干調整した構成 約475MB
large 00_base05_extカテゴリのパッケージを収録した構成 約1.2GB
full 00_base10_lofすべてのカテゴリとcontribの一部のカテゴリのパッケージを収録した構成 約3.2GB

largeバリアントやfullバリアントはサイズが大きく、ダウンロードするのに時間が掛かりますが、興味があれば試してみてください。

コンテナの初期パスワード

前回「コンテナ内の設定ファイルの調整」の項で説明したように、lxc-createコマンドを使ってPlamoコンテナを作成すると、rootのパスワードが暫定的に"root"に設定されます。この仕様は、Plamoテンプレートに限ったことではなく、筆者らがPlamoテンプレートをマージしてもらった当時、パスワードが不要なテンプレートを除いて、Ubuntuテンプレートを始めとするすべてのテンプレートが、そのようになっていました。

LXCの開発当初は、利用者数が比較的少なかったこともあり、この仕様に異論を唱える開発者は、当時あまりいなかったように思います。しかし、このような牧歌的な状況が長く続くわけでもなく、この連載が始まる少し前のUbuntu 14.04 LTSがリリースされた頃から、各ディストリビューションでのLXCの正式採用が相次ぎ、利用者数が急速に増えていきました。同時に、コンテナの初期パスワードに対する指摘が、定期的にlxc-develメーリングリストに投稿されるようになり、そして今年(2014年)8月、これについて問題視しているバグレポートが2件、Debian Bug reportRed Hat Bugzillaが発行されるに至りました。

この問題を受けて、Michael H. Warfieldさんが解決に向けて精力的に活動されています。彼は、テンプレート内でパスワードを設定する際、ランダム文字列による設定や、パスワード入力による設定、パスワードを期限切れにしたり、アカウントを無効化するなど、さまざまな状況に対応できる共通関数を作成し、まずFedoraテンプレートとCentOSテンプレートに適用し、順次他のテンプレートへの適用を目指しています。

ただし、この方法はroot権限で通常のテンプレートを使って作成したコンテナ環境において効果を発揮するものであり、一般ユーザ権限でダウンロードテンプレートを使って作成したコンテナ環境においては効果的ではありません。はい、パスワード設定済みのコンテナイメージをダウンロードするので、毎回同じパスワードになってしまいますね。実際のところ、Michael H. Warfieldさんは、非特権コンテナは自分の領域ではないと仰っていました。

筆者は、root権限で通常のテンプレートを使って作成したコンテナ環境でも、一般ユーザ権限でダウンロードテンプレートを使って作成したコンテナ環境でも、同等の効果を発揮するように、Michael H. Warfieldさんが適用を目指している初期パスワードの設定を、テンプレート内ではなく、lxc-createコマンドからテンプレートが呼び出されて、戻ってきた直後に行うように提案しました。しかしながら、Michael H. Warfieldさんの提案と競合しているため、最終的にどうなるか、まだ着地点は見えていません。

一方、LXCメンテナであり、ダウンロードテンプレートの生みの親でもあるStéphane Graberさんが現在考えていることは、大きく以下の2点です。

  • 作成済みコンテナイメージのパスワードを一律無効化にする
  • ダウンロードテンプレートをメインのLXCソースツリーで管理しない

パスワードの一律無効化は、LXCのダウンロード用サーバにおいて、最近実施されました。この変更によって、一部のディストリビューションのテンプレートを使って作成したコンテナで、初回起動時にパスワード設定のプロンプトを出して止まってしまうなど、意図しない動きをするケースもあるようなので、テンプレート側で何らかの修正が必要でしょう。もう一つ、ダウンロードテンプレートの扱いについては、lxc-1.2での実装を目標として、作成済みコンテナイメージを作っている人たちのブランチに、ダウンロードテンプレートを管理してもらうことを考えているようです。その際、メインのLXCソースツリーで用意しているテンプレートだけでなく、他のいろいろな作りのテンプレートを考慮して、それらをカバーできるように、ダウンロードテンプレートを整備しておく必要があるでしょう。

メインのLXCソースツリーで、ダウンロードテンプレートを管理しなくなると、今までのように、LXCツール単体で非特権コンテナを作ることができなくなります。現実的に可能かどうかわかりませんが、将来的には通常のテンプレートだけで、非特権コンテナを作れるようになると嬉しいのですが。さらに、Plamoテンプレートのように、他のすべてのテンプレートが、任意のディストリビューション上で作成できるようになると素晴らしいですね。

いずれにしても、将来の方向性はこれから議論を重ねることによって定まってくるでしょうから、これからの動向を注意深く見守っていきたいと思います。

複数のユーザによるコンテナの作成とネットワーク構成

ここでは、複数のユーザがコンテナを作成することを考えてみます。

Plamo Linuxでは、lxcパッケージをインストールすると、cgroupサブシステムごとにcgroupファイルシステムをマウントするcgroup-mountというinitスクリプトと、LXC用ネットワークブリッジを作成するlxc-netというinitスクリプトは、デフォルトで起動しないようになっています。root権限で、以下の実行して有効にしてください。

~$ sudo chmod +x /etc/rc.d/init.d/cgroups-mount
~$ sudo chmod +x /etc/rc.d/init.d/lxc-net

なお、すでにlibcgroupパッケージをインストールしていて、cgconfigを起動させている場合は、cgroup-mountを起動させる必要はありません。

また、ユーザが書き込めるcgroupを作成するために、以下のinitスクリプトを追加しておきます。

~$ cat /etc/rc.d/init.d/cgroups-mount-user
#!/bin/sh
# mount cgroup filesystems per subsystem for user name space

USERNS="taro hanako"

start() {
  echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy
  for c in /sys/fs/cgroup/* ; do
    echo 1 > $c/cgroup.clone_children
    for u in $USERNS ; do
      mkdir -p $c/$u
      chown -R $u $c/$u
      if [ ${c##*/} == cpuset ] ; then
        echo 0 > $c/$u/cpuset.cpus
        echo 0 > $c/$u/cpuset.mems
      fi
    done
  done
}

stop() {
  :
}

case "$1" in
start)
  start
  ;;
stop)
  stop
  ;;
restart)
  stop
  start
  ;;
*)
  echo $"Usage: $0 {start|stop|restart}"
  exit 1
  ;;
esac

exit 0

新たなユーザでのコンテナの作成に先立ち、root権限でユーザに対する/etc/{subuid,subgid}の設定を行ってください。200000から65536個のUID/GIDをhanakoというユーザに割り当てます。

~$ sudo usermod -v 200000-265535 -w 200000-265535 hanako
~$ cat /etc/subuid
taro:100000:65536
hanako:200000:65536
~$ cat /etc/subgid
taro:100000:65536
hanako:200000:65536

また、lxc-createコマンド実行時に読み込まれるhanakoユーザ用のデフォルトファイル~/.config/lxc/default.confを用意します。上記で設定した/etc/{subuid,subgid}の設定と合うように設定してください。

~$ cat ~/.config/lxc/default.conf
lxc.id_map = u 0 200000 65536
lxc.id_map = g 0 200000 65536

lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up

hanakoユーザのコンテナを作成します。

~$ lxc-create -n ct01 -t download -- -d plamo -r 5.x -a amd64

次に、LXCでユーザが使えるネットワークインタフェースの数を設定するため、root権限で/etc/lxc/lxc-usernetを編集します。各ユーザがvethタイプのインタフェースをlxcbr0にアタッチする形で、それぞれ2個利用できるようにします。

~$ cat /etc/lxc/lxc-usernet
taro veth lxcbr0 2
hanako veth lxcbr0 2

これで、コンテナを起動する準備ができました。コンテナを起動するときは、現在実行中のシェルのPIDを、自身のcgroupのタスクに登録します。

~$ for c in /sys/fs/cgroup/* ; do echo $$ > $c/$USER/tasks ; done

いよいよコンテナを起動します。Plamo Linuxのデフォルト設定では、セキュリティ確保のため、inetd経由で起動するサービスについて、外部からのリクエストはすべて拒否されるようになっているので、コンテナ内の/etc/hosts.allowを適切に設定するのを忘れないでください。

iptablesのNAT(IPマスカレード)機能を利用する方法

Plamo LinuxのLXCパッケージで、デフォルトで用意されているネットワーク構成です。上記の手順に従ってコンテナを作成すれば、ネットワークの設定はすでに出来上がっています。

図1 NAT機能を介して物理NICに接続
図1 NAT機能を介して物理NICに接続

ホストOSの物理NICが有線/無線にかかわらず、ネットワークを利用することができます。

外部と通信する際は、図1のようにNAT機能を介して物理NICに接続します。複数のコンテナがホストOSのIPアドレスを共有するため、コンテナから外部のサーバに接続することはできますが、外部のサーバからコンテナに接続することはできません。

物理NIC(有線)を仮想ブリッジに接続する方法

外部のサーバからコンテナに接続する必要がある場合は、図2のようにホストOSの物理NIC(有線)に、仮想ブリッジを接続する構成にします。

図2 物理NIC(有線)を仮想ブリッジに接続
図2 物理NIC(有線)を仮想ブリッジに接続

ホストOSでのネットワークの設定は、あらかじめ以下のように設定しておきます。br0という仮想ブリッジを作成し、これに対してホストOS用のIPアドレスを与えています。ネットワーク設定用rcスクリプト/etc/rc.d/rc.inet1.tradnetの適切な場所に入れておくと良いでしょう。

~$ sudo ip link set dev eth0 up promisc on
~$ sudo brctl addbr br0
~$ sudo brctl addif br0 eth0
~$ sudo dhclient br0

各ユーザのLXCのネットワーク設定を以下のように変更します。

~$ cat ~/.config/lxc/default.conf | grep network
lxc.network.type = veth
lxc.network.link = br0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx

lxc.network.hwaddrの"xx:xx:xx"の部分は、lxc-createコマンドを実行するとランダムな値に変換して、コンテナの設定ファイル~/.local/share/lxc/<コンテナ名>/configにコピーされます。なお、DHCPサーバにMACアドレスを登録する必要がある場合は、このMACアドレスを登録してください。同一セグメント内で重ならない好きな値に書き換えて、登録することもできます。

この接続方法は、外部のサーバからコンテナに接続することができるので、コンテナ内にサーバを設置する場合などに有効です。ただし、ホストOSの物理NICが無線の場合には使えないことに注意してください。

物理NIC(無線)を仮想ブリッジに接続する方法

仮想ブリッジは、ホストOS上に仮想的なレイヤ2スイッチを構成して、レイヤ2のフレーム交換を行う機能です。しかし、無線LANでつながっている場合、レイヤ2レベルの転送をサポートしていないため、簡単にブリッジ接続を構成することができません。

そこで、tunctluml_utilitiesパッケージ⁠⁠、parproutedparproutedパッケージ⁠⁠、bcrelaypptpdパッケージ)を使って、図3のようにホストOSの物理NIC(無線)に、仮想ブリッジを接続する構成にします。

図3 物理NIC(無線)を仮想ブリッジに接続
図3 物理NIC(無線)を仮想ブリッジに接続

ホストOSでのネットワークの設定は、あらかじめ以下のように設定しておきます。tap0というtapデバイスを作成、仮想ブリッジbr0tap0をブリッジし、br0にDHCPレンジ外のスタティックIPアドレスを付与して起動します。そして、parproutedwlan0に飛んできたARPリクエストをbr0にも転送、bcrelayでブロードキャスト(DHCP情報)をリレーするようにしていますparproutedは、マルチキャストトラフィックに対応していないためです⁠⁠。ネットワーク設定用rcスクリプト/etc/rc.d/rc.inet1.tradnetの適切な場所に入れておくと良いでしょう。

~$ sudo ip link set dev wlan0 up
~$ sudo brctl addbr br0
~$ sudo tunctl -t tap0
~$ sudo brctl addif br0 tap0
~$ sudo ip addr add 192.168.1.100/24 dev br0
~$ sudo ip link set dev br0 up
~$ sudo parprouted wlan0 br0
~$ sudo bcrelay -d -i br0 -o wlan0
~$ sudo wpa_supplicant -B -c /etc/wpa_supplicant.conf -i wlan0
~$ sudo dhclient wlan0

各ユーザのLXCのネットワーク設定は、物理NIC(有線)を仮想ブリッジに接続するときと同じです。

まとめ

今回は、lxc-createコマンドを使ってコンテナを作成するときの、root権限と一般ユーザ権限での動作の違いや、複数ユーザによるコンテナの作成、状況に応じたネットワークのいろいろな構成などを紹介しました。これらは、特にPlamo Linuxに依存した内容ではありませんので、他のディストリビューション上でも応用できると思います。ぜひお試しください。

次回は、コンテナ内でサウンドを取り扱う方法について紹介します。お楽しみに。

おすすめ記事

記事・ニュース一覧