LXC のデフォルトの設定だと、DHCP でコンテナに IP アドレスが割り振られるため、コンテナ同士で Riak クラスタを組んでみる、みたいなことがやりづらい。 IP アドレスを固定しようと思ったら普通に /etc/network/interfaces
にそれっぽい設定を書けばいいんだけど、コンテナを作るたびに設定するのは面倒臭いし、間違えて同じ IP アドレスを異なるコンテナに割り当ててしまう事故も起こりやすい。また、IP アドレスを固定するのであれば、ついでに DNS にも登録して名前から IP アドレスを引けるようにしたい。
ということで、次のような設定をしたらすごく便利になった:
- ホストの
/etc/hosts
に全てのコンテナの名前と IP アドレスを書くことにした。- 一つのファイルに全てのコンテナの IP アドレスが集約されるので、どの IP アドレスを使ってないのかすぐにわかる。
- LXC の Ubuntu テンプレートを改造して、
/etc/hosts
から自分の IP アドレスを引いてきて、適切な$rootfs/etc/network/interfaces
を吐き出すようにした。 - dnsmasq を立てて、
/etc/hosts
の内容を DNS 経由で引けるようにした。 - 簡単にコンテナを作れるように、補助シェルスクリプトを書いた。
1. /etc/hosts
こんな感じに、コンテナの IP アドレスを書いておく。
127.0.0.1 localhost
127.0.1.1 dev-3
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# Local containers
10.0.3.1 web-1
10.0.3.2 web-2
10.0.3.3 db-1
(以下略)
2. LXC の Ubuntu テンプレートの改造
LXC はコンテナを作るときにテンプレートと呼ばれるスクリプトを実行する。Ubuntu のコンテナを作成するときは、/usr/share/lxc/templates/lxc-ubuntu
が実行される。これを開いてみると中身はただの bash スクリプトになっていて、lxc-create
で与えられた引数がこのスクリプトに渡ってくるようになっている。今回はこのスクリプトを改造して、/etc/hosts
に与えられたコンテナ名に対応する IP アドレスが登録されている場合は、$rootfs/etc/network/interfaces
をそれっぽく出力するようにする。
まず、lxc-ubuntu
をコピーして lxc-ubuntu-nojima
を作る。(ファイル名から lxc-
を削った文字列がテンプレート名になる)
sudo cp /usr/share/lxc/templates/lxc-ubuntu{,-nojima}
次にテンプレート内の configure_ubuntu()
の中身を書き換える。diff はこんな感じ:
--- /usr/share/lxc/templates/lxc-ubuntu 2014-04-15 00:49:58.000000000 +0900
+++ /usr/share/lxc/templates/lxc-ubuntu-nojima 2014-06-22 18:03:15.209620112 +0900
@@ -53,21 +53,41 @@
release=$3
user=$4
password=$5
# configure the network using the dhcp
- cat <<EOF > $rootfs/etc/network/interfaces
+ ipaddr=$(sed -e 's/#.*$//' /etc/hosts | awk "\$2~/^${hostname}\$/ { print \$1 }")
+ if [ -z "$ipaddr" ]; then
+ cat <<EOF > $rootfs/etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
EOF
+ else
+ echo "Set the IP address of $hostname to $ipaddr"
+ cat <<EOF > $rootfs/etc/network/interfaces
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+ address $ipaddr
+ netmask 255.255.0.0
+ gateway 10.0.0.1
+ dns-nameservers 10.0.0.6
+EOF
+ fi
# set the hostname
cat <<EOF > $rootfs/etc/hostname
$hostname
EOF
一応解説しておくと、
ipaddr=$(sed -e 's/#.*$//' /etc/hosts | awk "\$2~/^${hostname}\$/ { print \$1 }")
の部分で、/etc/hosts
から IP アドレスを取得している。 sed -e 's/#.*$//' /etc/hosts
の部分が /etc/hosts
のコメント以外の部分を主力していて、それを awk "\$2~/^${hostname}\$/ { print \$1 }"
に突っ込んで、「スペースで区切られた 2 番目の部分が ${hostname}
に一致していたら、1 番目の部分を出力」している。
そして
if [ -z "$ipaddr" ]; then
cat <<EOF > $rootfs/etc/network/interfaces
...
$ipaddr
が空文字列、つまり IP アドレスが取得できなかった場合は、デフォルトの interfaces を出力し、そうでない場合は、
else
echo "Set the IP address of $hostname to $ipaddr"
cat <<EOF > $rootfs/etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address $ipaddr
netmask 255.255.0.0
gateway 10.0.0.1
dns-nameservers 10.0.0.6
EOF
fi
として、eth0 に固定 IP アドレスを割り当てている。 netmask
, gateway
に何を設定すべきかは環境によって異なるので、このテンプレートを流用する場合は環境に合わせてこの部分を書き換えないといけないことに注意。 dns-nameservers
に関しては、今回ホストに DNS サーバを立てるので、ホストの IP アドレスにしている。
これで、ubuntu-nojima
という名前のテンプレートを使って lxc-create
することができるようになった。
例えば、web-1
というコンテナを作るときはこんな感じで指定する。いろいろくっついているオプションはお好みで。
sudo lxc-create -t ubuntu-nojima -n web-1 -- --user=nojima --password=secret --auth-key=/home/nojima/.ssh/id_rsa.pub --mirror="http://ftp.jaist.ac.jp/ubuntu"
3. dnsmasq
dnsmasq はお手軽 DNS サーバ兼 DHCP サーバで、デフォルトで /etc/hosts
からホスト名と IP アドレスの対応を読んで勝手に DNS リクエストに答えたり、いい感じに上流の DNS にフォワードしてくれたりする。上流の DNS サーバは /etc/network/interfaces
の dns-nameservers
の部分に書いておくと自動的に認識してくれる模様。
なので、何の設定もしなくても apt-get して起動するだけで DNS サーバとしてそれっぽく動いてくれる。
(普通だと、/etc/network/interfaces
の dns-nameservers
に書くと、自動的に /etc/resolv.conf
に書きだされてしまうと思うのだが、dnsmasq が動いている場合は謎のフックにより /etc/resolv.conf
ではなく /var/run/dnsmasq/resolv.conf
の方に書きだされ、/etc/resolv.conf
の方は nameserver 127.0.0.1
が入るという挙動をしていた。ちゃんと動作を追っていないので詳細は不明。)
今回は
- ホストでは、デフォルトの設定で dnsmasq を動かし、
- コンテナでは、
/etc/network/interfaces
のdns-nameservers
にホストの IP アドレスを指定する
ことで、コンテナからもコンテナ名を DNS で引けるようにした。
4. シェルスクリプト化
2 で書いたコンテナ作成コマンドはあまりにもタイプするのが面倒くさいのと、コンテナを作成したらコンテナの起動と dnsmasq のリロードを行わないといけないので、手順を簡単にするためにシェルスクリプトを書いた。
#!/bin/sh
if [ $# -ne 1 ]; then
echo "Usage: create-container NAME" 1>&2
exit 1
fi
name=$1
ipaddr=$(sed -e 's/#.*$//' /etc/hosts | awk "\$2~/^${name}\$/ { print \$1 }")
if [ -z "$ipaddr" ]; then
echo "ERROR: IP address of ${name} was not found in /etc/hosts." 1>&2
exit 1
fi
set -xe
lxc-create -t ubuntu-nojima -n $name -- --user=nojima --password=secret --auth-key=/home/nojima/.ssh/id_rsa.pub --mirror="http://ftp.jaist.ac.jp/ubuntu"
service dnsmasq force-reload
lxc-start -d -n $name
これで、以下の手順で固定 IP アドレスを持つコンテナを作れるようになった。便利!!
sudo vim /etc/hosts # ホスト名と IP アドレスの対応を追加する。
sudo create-container $NAME # コンテナ作成 + 起動
その他
今回は IP アドレスの固定を行ったが、Ubuntu テンプレートの改造は他にもいろいろできる。とりあえず↓のようなことをやってみた。
-
--user
で指定したユーザに sudo 権限を付与するようにした。 -
--password
に空を指定することで、パスワードを disable したユーザを作成できるようにした。- つまりどんなパスワードを入れても認証に失敗するユーザになる。
-
/var/lib/lxc/$container_name/fstab
にと書いてホストと/data data none bind,create=dir
/data
を共有する。
とにかく汎用性は高いので、いじってみると楽しい。
ToDo
- コンテナを作った後の設定をするのが面倒臭いので、Chef とかで自動化したい。
- ファイルシステムを btrfs にしたい。