VirtualBox上のCentOS 6.4でLXCをセットアップするAnsible playbook

## なぜCentOS上でLXCを使うか

ゲストにCentOSを使いたくて、そのときLinuxカーネルもCentOSのを使いたいからです。
LXCではホストとゲストで同じLinuxカーネルを使うので、ホストもCentOSにすればこれが実現できます。

Ubuntuゲストの起動は想定していません。上と同じ理由でUbuntuゲストを使うのはUbuntuホストのLXCにしたほうがよいと私は考えているからです。Ubuntu 12.04のLinuxカーネルは3.xですがCentOS 6.4のカーネルは2.6.xと古いのでなにか不具合が出る可能性もあると思います。

## 使い方

Ansibleのplaybookは
https://github.com/hnakamur/ansible-playbooks/tree/master/roles/lxc
に置いてあります。

### ローカルドメインの設定

https://github.com/hnakamur/ansible-playbooks/blob/master/roles/lxc/vars/lxc.yml
のlxc_local_domainを適宜変更します。

[.local - Wikipedia, the free encyclopedia](http://en.wikipedia.org/wiki/.local)、[RFC 6762 - Multicast DNS](http://tools.ietf.org/html/rfc6762)によると
> Any DNS query for a name ending with ".local." MUST be sent to the
   mDNS IPv4 link-local multicast address 224.0.0.251 (or its IPv6
   equivalent FF02::FB). 

とのことなので、.localを使うのはよくなさそうです。

[Choosing a Internal Top Level Domain](http://www.trainsignal.com/blog/choose-internal-top-level-domain-name)によると自分が所有しているドメインで公開しないサブドメインを設定するのがお勧めのようです。例えば自分が所有しているドメインがexample.comなら、internal.example.comなどとします。このサブドメインは公開DNSには登録しないようにします。

LXCホストのホスト名も同じドメインを設定するようにします。例えばhost01というホスト名なら、以下のコマンドを実行して、FQDNをhost1.internal.example.comと設定します。

```shell-session:
$ sudo hostname host01.internal.example.com
$ sudo sed -i 's/^HOSTNAME=.*/HOSTNAME=host01.internal.example.com/' /etc/sysconfig/network
```

この設定を行なわないとLXCホストでdigでゲスト名の名前解決ができるのにsshでゲスト名を指定しても繋がらないという現象が発生します。例えばcent01で作成したゲストに対してdig cent01では名前解決できるのにssh cent01では繋がらず、dig cent01で表示されたIPアドレスを指定してsshすると繋がるという状態になります。

### LXCホストのセットアップ

事前にAnsibleのinventory file (例: /etc/ansible/hosts)に${your_lxc_hostname}を登録して以下のコマンドでセットアップします。

```shell-session:
$ ansible-playbook -l ${your_lxc_hostname} lxc.yml
```

私が試しているのはOSXでansibleを実行して、LXCホストはVirtualBoxのCentOS VMという構成です。
LXCのバージョンは0.8.0をお勧めします。
最新の0.9.0だとゲスト側からshutdownを実行しても停止されないという問題があるからです。ホストからlxc-stopで止めることは出来ます。ですが、ゲスト側でshutdownやrebootが出来ないと不便なので0.9.0はお勧めではないです。

### ゲストの作成

LXCホスト上で以下のコマンドを実行します。
以下はcent01という名前で作る例で説明します。

```shell-session:
$ sudo lxc-create -n cent01 -f /etc/lxc/default.conf -t centos
```

最初のコンテナを作るのにはマシン環境にもよりますが10分程度かかります。

### ゲストの起動

```shell-session:
$ sudo lxc-start -dn cent01
```
オプション-dを指定してdettachした状態で起動します。オプション-d無しで起動すると即座にシリアルコンソールに繋がるのですが、そこから抜ける方法が分かりません。

CentOSホストでlxc 0.9.0を動かす場合、ゲストでshutdown -h nowを実行してもなぜかシャットダウンされないという不具合があるので常に-dは指定してください。

### ゲストのシリアルコンソールへ接続

```shell-session:
$ sudo lxc-console -n cent01

Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself
```

初期状態ではrootユーザのパスワードがpasswordとなっていますので、それでログインします。
シリアルコンソールから抜けるときはCtrl-a qを押します。

### ゲストの停止

ホストで以下のコマンドを実行します。

```shell-session:
$ sudo lxc-stop -n cent01
```

CentOSホストでlxc 0.9.0を動かす場合、ゲストでsudo shutdown -h nowを実行しても、なぜかシャットダウンされないという不具合があります。

### ゲストの起動状態確認

```shell-session:
$ sudo lxc-info -n cent01
state:   RUNNING
pid:     13543
```

オプション-sも指定すればstateの行のみが出力されます。

```shell-session:
$ sudo lxc-info -sn cent01
state:   RUNNING
```

### ゲストの自動起動／停止設定

/etc/lxc/auto/以下にゲスト名のファイルがあると、ホストの起動／停止時にlxc-autoサービスによりゲストが自動起動／停止されます。

```shell-session:
$ sudo touch /etc/lxc/auto/cent01
```

自動起動／停止設定設定済みのゲストの起動状態一括確認

```shell-session:
$ sudo service lxc-auto status
cent01: RUNNING
cent02: STOPPED
```

## playbookの概要

[さくらのVPSにLXCで仮想環境構築してXtraDB Cluster動かす - apatheia.info](http://apatheia.info/blog/2012/05/13/vps-lxc-xtradb-cluster/)やその他多数のホストを参考にしました。ありがとうございます。

- LXC 0.8.0をソースからビルド
- cgroupをマウント
- lxcbr0というブリッジを作り、ゲストからインターネットにアクセスできるようにiptablesを設定
- dnsmasqのDHCPでゲストコンテナに動的IPアドレスを付与しローカルのDNSに登録する設定
- ホスト起動／停止時にコンテナを自動的に起動／停止するスクリプトを設置

## LXC 0.8.0をソースからビルド

LXC最新版のバージョン0.8.0をソースからビルドします。

## cgroupをマウント

/etc/fstabにエントリを追加して、ホスト起動時にマウントするようにしています。

## lxcbr0というブリッジを作り、ゲストからインターネットにアクセスできるようにiptablesを設定

Ubuntuのlxcパッケージでインストールされるlxc-netというUpstartのスクリプトをベースにして作りました。/etc/init.d/lxc-netにインストールされ、ホスト起動／停止時に実行されます。

service lxc-net startでブリッジ作成とiptablesに設定追加、service lxc-net stopでブリッジ削除とiptablesの設定削除を行います。サービスにしているのはホスト起動／停止時に実行したいだけで、service lxc-net start実行後に何かデーモンが動いているわけではないです。

なお、dnsmasqはホスト起動時にservice dnsmasq startで自動起動されます。Ubuntuのlxcパッケージではlxc-netのスクリプト内から起動していますが、私がCentOS用に作ったlxc-netではdnsmasqは起動せずにdnsmasq自体のサービスのほうで起動するようにしました。

ゲスト稼働中にservice lxc-net stopを実行してもブリッジの削除とiptablesの設定削除は行なわれません。全てのゲストを停止してから実行してください。

lxc-netをstartした後はlxcbr0というブリッジが作成され、iptablesの設定は以下のようになります。

```shell-session:
$ sudo iptables-save
# Generated by iptables-save v1.4.7 on Fri Aug 23 00:26:26 2013
*nat
:PREROUTING ACCEPT [42:7470]
:POSTROUTING ACCEPT [18504:1157052]
:OUTPUT ACCEPT [18504:1157052]
-A POSTROUTING -s 10.0.3.0/24 -o eth0 -j MASQUERADE 
COMMIT
# Completed on Fri Aug 23 00:26:26 2013
# Generated by iptables-save v1.4.7 on Fri Aug 23 00:26:26 2013
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [59585:3690486]
-A INPUT -i lxcbr0 -p udp -m udp --sport 67:68 --dport 67:68 -j ACCEPT 
-A INPUT -i lxcbr0 -p udp -m udp --dport 53 -j ACCEPT 
-A INPUT -i lxcbr0 -p tcp -m tcp --dport 53 -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A INPUT -p icmp -j ACCEPT 
-A INPUT -i lo -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT 
-A INPUT -j REJECT --reject-with icmp-host-prohibited 
-A FORWARD -i eth0 -j ACCEPT 
-A FORWARD -o eth0 -j ACCEPT 
-A FORWARD -j REJECT --reject-with icmp-host-prohibited 
COMMIT
# Completed on Fri Aug 23 00:26:26 2013
```

natのPOSTROUTINGとfilterのFORWARDは、ゲストからインターネットにアクセスできるようにするために[FORWARD and NAT Rules](http://www.centos.org/docs/4/html/rhel-sg-en-4/s1-firewall-ipt-fwd.html)を参考にして追加しました。

udpの67, 68はDHCPのリクエストを通すために[Letting DHCP requests through iptables](http://www.faqs.org/docs/iptables/lettingdhcprequests.html)を参考に追加しました。

udp 53, tcp 53はDNSリクエストを通すために追加しました。

これでとりあえず動いてはいますが、私がネットワーク設定に詳しくないので、設定が無駄だったりセキュリティ的に良くない設定になっているかもしれません。その場合はご指摘いただけるとありがたいです。

## 未解決のエラー

### ゲスト作成時に以下の様なエラーが出ている

```shell-session:
  Installing : initscripts-9.03.38-1.el6.centos.2.x86_64                134/146 
Non-fatal POSTIN scriptlet failure in rpm package initscripts-9.03.38-1.el6.centos.2.x86_64
chmod: cannot access `/var/lib/random-seed': そのようなファイルやディレクトリはありません
警告: %post(initscripts-9.03.38-1.el6.centos.2.x86_64) scriptlet failed, exit status 1
```

これは今後調査予定です。