LXC 1.0: 二つ目のコンテナー [2/10]

## この文書について

この文書は、連載記事「[LXC 1.0: Blog post series](https://www.stgraber.org/2013/12/20/lxc-1-0-blog-post-series/)」の一つである以下の記事を翻訳したものです。連載の目次や注意点は[こちら](http://qiita.com/m-shibata/items/ee9b295a2596824da819)を参照してください。

* https://www.stgraber.org/2013/12/21/lxc-1-0-your-second-container/

この文書のライセンスは原文と同じく、[Creative Commons BY-NC-SA 2.5](http://creativecommons.org/licenses/by-nc-sa/2.5/)のもとに提供されています。

![CC-BY-NC-SA 2.5](http://i.creativecommons.org/l/by-nc-sa/2.5/88x31.png)


## テンプレートについて

これまでの作業で、「p1」という名前の実際に動作するUbuntuコンテナーができました。このコンテナーは「ubuntu」というシンプルだけで必要十分なテンプレートを使用しています。

しかしながら、LXCは標準的なUbuntu以外もサポートしています。実際、[現在のアップストリーム](https://github.com/lxc/lxc/tree/master/templates)（やデイリーPPA）では、Alpine LinuxやAlt Linux、Arch Linux、busybox、CentOS、Cirros、Debian、Fedora、OpenMandriva、OpenSUSE、Oracle、Plamo、sshd、Ubuntu Cloud、それにUbuntuをサポートしているのです。

これらのすべては、通常は `/usr/share/lxc/templates` で見つけることができます。これらのテンプレートには高度なオプションが存在し、そのオプションは「 `lxc-create` 」の後ろ（ `lxc-create` 自身のオプションからテンプレートのオプションを分離するために `--` が必要です）に、 `--help` を渡すことで確認できます。

テンプレートを拡張することはそれほど難しくはありません。テンプレートは基本的に、標準的な引数のセットを受け取り、指定されたパスに作業用のrootfsを生成する実行可能なファイルです（すべてシェルスクリプトですが、シェルスクリプトである必要はありません）。

すべてのディストリビューション上で、すべてのディストリビューションをブートストラップできるツールが揃っているわけではない、ということに気づくでしょう。通常、もっとも良いのは実際に試してみることです。私たちは常に、より多くのディストリビューションのテンプレートの作成に関心をいだいています。たとえそれが（Fedoraのテンプレートで行なっているような）奇妙なトリックを使う必要があったとしてもです。現時点で動作しない特定の組み合わせがあるなら、パッチはいつでも歓迎します。

さて、十分に話しましたので、次に進んで32bit版のOracle Linuxコンテナーを作成してみましょう。

```bash
sudo lxc-create -t oracle -n p2 -- -a i386
```

たいていのシステムでは最初は失敗し、ブートストラップに必要な「rpm」パッケージをまずインストールするよう通知されることでしょう。そこでそれと、「yum」パッケージをインストールして再び試してみます。

RPMファイルのダウンロード後、コンテナーが作成されます。その後、次のコマンドを実行します：

```bash
sudo lxc-start -n p2
```

そうするとOracle Linuxのログインプロンプトが表示されるはずです（アカウントはroot/root）。

今回は「 `lxc-start` 」に「-d」オプションは渡さずに起動したので、シェルに戻るためにはコンテナーをシャットダウンする必要があります（バックグラウンドで起動しなかったコンテナーを中からデタッチすることはできません）。

Ubuntuが二つのテンプレートを持っていることを不思議に思うかもしれません。これまで使用したUbuntuテンプレートは「 `debootstrap` 」を用いてローカルでブートストラップし、スクラッチからコンテナーを作成していました。それに対してUbuntu Cloudテンプレート（ `ubuntu-cloud` ）は、事前に作成済みのcloudイメージ（EC2や他のクラウドサービスで使用しているイメージと同じもの）をダウンロードし、それを起動します。このイメージには、cloud-initも含まれており、標準的なクラウドメタデータをサポートしています。

これらは個人的に好きな方を選べば良いでしょう。私個人は、ローカルに（訳注：おそらくリポジトリの）ミラーを持っているため、「 `ubuntu` 」テンプレートの方が高速ですし、私の目の前にあるアーカイブからすべてがダウンロードされ、マシン上にローカルに構築されているとわかるので、そちらのテンプレートを信用しています。

テンプレートについて最後に一つ。ほとんどはローカルキャッシュを使います。特定のアーキテクチャに対する、最初のコンテナーのブートストラップは遅くなりますが、次からはキャッシュからのローカルコピーになるのでもう少し早くなるでしょう。


## 自動起動

起動時にコンテナーを自動的に起動したい場合はどうすればいいのでしょうか？

Ubuntuでは随分前からサポートしており、他のディストリビューションでも/etcにあるいくつかのinitスクリプトをシンボリックリンクで実現できました。しかしながら、最近（二日前）になって、この機能はアップストリームできれいな形で実装されたのです。

現在のコンテナーの自動起動の方法は次のとおりです：

知ってのとおり、それぞれのコンテナーには `/var/lib/lxc/<コンテナー名>/config` のような設定ファイルが存在します。

このファイルは「キー = 値」という形で、[lxc.conf(5)](http://qa.linuxcontainers.org/master/current/doc/man/lxc.conf.5.html)にあるキーのリストをもとに設定しています。

起動に関しては、次の設定を利用できます：

* lxc.start.auto = 0 （無効） もしくは 1 （有効）
* lxc.start.delay = 0 （コンテナーの起動したあとに（訳注：次のコンテナーを起動するまでに）待つ秒数）
* lxc.start.order = 0 （コンテナーの優先度、大きな値ほどより先に起動される）
* lxc.group = group1,group2,group3,… （このコンテナーがメンバーになっているグループ）

マシンが起動した時、initスクリプトは指定したグループ（標準ではどこにも属していないすべてのコンテナー）のコンテナーを正しい順番で起動するために「 `lxc-autostart` 」を呼び出し、起動の間指定した時間だけ待つことになるでしょう。

説明のために、 `/var/lib/lxc/p1/config` に次の行を追加してみましょう：

    lxc.start.auto = 1
    lxc.group = ubuntu

また `/var/lib/lxc/p2/config` にも以下の行を追加します：

    lxc.start.auto = 1
    lxc.start.delay = 5
    lxc.start.order = 100

これらの設定は、起動時はp2コンテナーのみが起動すること（標準ではグループに属していないもののみ起動するので）、起動するのは一つだけなのでオーダーは関係ないこと、initスクリプトは次の処理の移るまで5秒待つことを意味します。

どのコンテナーが自動起動するかは「 `lxc-ls` 」コマンドで確認できます：

```bash
stgraber@castiana:~$ sudo lxc-ls --fancy
NAME    STATE    IPV4        IPV6                                    AUTOSTART
---------------------------------------------------------------------------------
p1      RUNNING  10.0.3.128  2607:f2c0:f00f:2751:216:3eff:feb1:4c7f  YES (ubuntu)
p2      RUNNING  10.0.3.165  2607:f2c0:f00f:2751:216:3eff:fe3a:f1c1  YES
```

また、「 `lxc-autostart` 」コマンドを使って、 `lxc.start.auto=1` が設定されているすべてのコンテナーを、手動で起動、停止、強制終了、再起動することも可能です。

例えば、以下のように実行します：

```bash
sudo lxc-autostart -a
```

これにより、 `lxc.start.auto=1` であるすべてのコンテナーが（ `lxc.group` の設定に関係なく）起動します。今回の場合、まずp2が起動し（ `order = 100` であるため）、その後5秒待って（ `delay = 5` ）、p1が起動したあとに戻ってきます。

現時点で、「ubuntu」グループに属するすべてのコンテナーを再起動したい場合は、次のコマンドを実行します：

```bash
sudo lxc-autostart -r -g ubuntu
```

「-L」オプションを渡すと、このコマンドによって影響を受けるコンテナーと遅延が単純に表示されて、実際には何もしません（他のスクリプトを実装する場合に便利でしょう）。


## コンテナーの凍結

あるとき、コンテナーがシャットダウンや再起動に時間のかかるデーモンを実行しているにも関わらず、現時点ではそれを積極的に使っていないためにコンテナーを実行したくない時があるかもしれません。

その場合は「 `sudo lxc-freeze -n <コンテナー名>` 」を使えます。これはコンテナー内のすべてのプロセスを凍結するという非常に単純なもので、これを使えばスケジューラーによる時間の割り当てが行われなくなります。しかしながら、プロセス自体は存在し続け、使用しているメモリ領域も残ったままになります。

再びサービスが必要になったら、「 `sudo lxc-unfreeze -n <コンテナー名>` 」を呼び出してください。すべてのプロセスが再開します。


## ネットワーク

自動起動の設定で設定ファイルを編集した時に気がついたかもしれませんが、LXCには比較的柔軟なネットワーク設定が存在します。Ubuntuの初期設定ですと、コンテナー毎に一つの「 `veth` 」デバイスが存在し、それらが最小のdnsmasq dhcpサーバーが動作しているホスト上の「 `lxcbr0` 」ブリッジに接続されています。

ほとんどの人にとってはこれで十分でしょう。しかしながら、複数のネットワークインターフェースを作成したり、ホストの物理ネットワークインターフェースにパススルーしたりといった、より複雑な設定をしたい場合があるかもしれません。それらのすべての設定は[lxc.conf(5)](http://qa.linuxcontainers.org/master/current/doc/man/lxc.conf.5.html)に詳細な記述があります。ここでそれを繰り返し説明したくはないので、わかりやすい例を一つ紹介しましょう。

```
lxc.network.type = veth
lxc.network.hwaddr = 00:16:3e:3a:f1:c1
lxc.network.flags = up
lxc.network.link = lxcbr0
lxc.network.name = eth0

lxc.network.type = veth
lxc.network.link = virbr0
lxc.network.name = virt0

lxc.network.type = phys
lxc.network.link = eth2
lxc.network.name = eth1
```

このコンテナーは三つのインターフェースを持っています。eth0はlxcbr0にブリッジされるvethデバイスです。eth1はホストのeth2をコンテナーの中に移動したものです（よってこのコンテナーが起動中は、このインターフェースはホストから見えなくなります）。virt0はホストのvirbr0にブリッジされたもう一つのvethデバイスです。

後者の二つのインターフェースはMACアドレスやネットワークフラグの設定が行われていないので、起動時にランダムな（固定的でない）MACアドレスが付与され、リンクアップしたときにup状態になります。


## アタッチ

十分に新しいカーネル、つまり3.8以上のカーネルを使っている場合、「 `lxc-attatch` 」ツールを使用できます。このツールの基本的な機能は、実行中のコンテナーの中で標準的なシェル環境を提供することです：

```
sudo lxc-attach -n p1
```

スクリプトから、コンテナーの中で何かの動作を実行させることもできます。例えば次のような感じです：

```
sudo lxc-attach -n p1 -- restart ssh
```

さらにもっと強力な機能も有しています。例えば次のコマンドを実行してみましょう：

```
sudo lxc-attach -n p1 -e -s 'NETWORK|UTSNAME'
```

この時、「 `root@p1` 」のシェルに移行します（ `UTSNAME` の効果です）。さらに、そこで「 `ifconfig -a` 」を実行すると、コンテナーのネットワークインターフェースがリストアップされるでしょう。ただし、それ以外のすべてのものはホストと同じ状態になります（訳注：例えばカレントディレクトリは「ホストの」カレントディレクトリのままになる、など）。「 `-e` 」オプションは、cgroupやapparmorなどの制約を、このシェルから起動したプロセスには適用しないことを意味しています。

この機能は、ホストの存在するソフトウェアを、コンテナーのネットワークやPID名前空間で実行したい時に便利でしょう。


## 実行中のコンテナーにデバイスを渡す

コンテナーへ自由自在に出入りできることはわかりました。しかし、ホスト上の任意のデバイスへのアクセスについてはどうなのでしょうか？

LXCの初期設定では、デバイスcgroupと呼ばれるフィルタリング機構を使って、そのようなアクセスを防止しています。コンテナーの設定を変更し、再起動すれば、デバイスにアクセスできるようにすることが可能です。

しかし、その場限りの設定で良ければ、「 `lxc-device` 」というとても便利なツールが使えます。次のように実行してみましょう：

```
sudo lxc-device add -n p1 /dev/ttyUSB0 /dev/ttyS0
```

これにより、 `/dev/ttyUSB0` と同じtype、major、minorな `/dev/ttyS0` をコンテナーの中に追加（ `mknod` ）され、コンテナーからアクセスできるようにcgroupエントリーが追加されます。

これと同じツールを使って、ホストからコンテナーの中へネットワークインターフェースを移動することができます。