Ubuntu 16.04: LXD/LXCでコンテナを立ち上げる

LXD/LXCの設定方法とコンテナの立ち上げ方を記載します。

 

1 LXD向けのパッケージ

LXD向けのパッケージとZFS向けのパッケージをインストールします。

$ sudo apt install -y lxd lxd-client lxd-tools criu
$ sudo apt install -y zfsutils-linux
$ sudo modprobe zfs

criuはコンテナのマイグレーションで使用されます。

ZFSはコンテナイメージの格納場所に使用されます。ZFSを使用しない場合はディレクトリ配下に格納されます。ZFSは専用のブロックデバイスがある場合に用いると良いでしょう。

2 LXDの初期化

lxdを対話形式で初期化します。lxd-bridgeも対話形式で初期化します。ZFSをブロックデバイスなしで使用した場合はファイルシステム上にzfs.imgが作成されます。

"Address to bind LXD to"はどのNICにlxdデーモンをListenさせるかを示します(0.0.0.0の場合はすべてのインターフェースに対してListenします)。

$ sudo lxd init
Name of the storage backend to use (dir or zfs): zfs
Create a new ZFS pool (yes/no)? yes
Name of the new ZFS pool: tank
Would you like to use an existing block device (yes/no)? no
Size in GB of the new loop device (1GB minimum): 20
Would you like LXD to be available over the network (yes/no)? yes
Address to bind LXD to (not including port): 0.0.0.0
Port to bind LXD to (8443 recommended): 8443
Trust password for new clients:
Again:
Do you want to configure the LXD bridge (yes/no)? yes
Warning: Stopping lxd.service, but it can still be activated by:
  lxd.socket
LXD has been successfully configured.

–autoオプションを用いれば対話不要で初期化できます。

$ sudo lxd init --auto \
--storage-backend zfs \
--storage-pool tank \
--storage-create-loop 20 \
--network-address 0.0.0.0 \
--network-port 8443 \
--trust-password password

ZFSを使わない場合は以下のとおりです。

$sudo lxd init --auto \
     --storage-backend dir \
     --network-address 0.0.0.0 \
     --network-port 8443 \
     --trust-password password

 lxd-bridgeも対話不要で初期化出来ます。

$ sudo systemctl stop lxd-bridge
$ sudo systemctl --system daemon-reload
$ sudo su -c 'cat <<EOF > /etc/default/lxd-bridge
USE_LXD_BRIDGE="true"
LXD_BRIDGE="lxdbr0"
UPDATE_PROFILE="true"
LXD_CONFILE=""
LXD_DOMAIN="lxd"
LXD_IPV4_ADDR="10.202.80.1"
LXD_IPV4_NETMASK="255.255.255.0"
LXD_IPV4_NETWORK="10.202.80.1/24"
LXD_IPV4_DHCP_RANGE="10.202.80.2,10.202.80.254"
LXD_IPV4_DHCP_MAX="252"
LXD_IPV4_NAT="true"
LXD_IPV6_ADDR=""
LXD_IPV6_MASK=""
LXD_IPV6_NETWORK=""
LXD_IPV6_NAT="false"
LXD_IPV6_PROXY="false"
EOF
'
$ sudo systemctl enable lxd-bridge
$ sudo systemctl start lxd-bridge

3 コンテナの作成

lxc launchでコンテナを作成します。コンテナの名前は.や_を用いることができません。

$ # lxc launch <image> <countainer>
$ lxc launch ubuntu:16.04 ubuntu-16-04

イメージのダウンロードはLinux Containersのページのものを使うこともできます。私の環境だとダウンロード速度はLinux Containersのページの方が圧倒的に速いです。

images:<Distribution>/<Release>/<Architecture>

例えば、Debian 8なら以下のとおりです。

images:debian/jessie/amd64

リモートサーバのイメージ一覧は以下のコマンドで取得できます。

$ lxc image list <remote>:

デフォルトでアクセス可能なリモートサーバはimages, ubuntu, ubuntu-dailyです。イメージのサイズは100MByte程度です。

$ lxc image list images:
$ lxc image list ubuntu:
$ lxc image list ubuntu-daily:

3.1 Fedora 22/23のdnfで'Inappropriate ioctl for device'(回避策あり)

gpgcheckが上手く動作しません。

# dnf install -y <pkg>
gpgme.GpgmeError: (7, 32870, 'Inappropriate ioctl for device')

–nogpgcheckオプションを使うことでインストールは完了できます。

# dnf install --nogpgcheck -y <pkg>

RedHatのbugzillaに一度teeを噛ませると直るという情報があります。イメージ作成方法が正しくないのか、dnfが作成方法の情報を記録しているのが悪いのか、今後の改善に期待です。

以下を実行すればlxc stopやUbuntu 16.04の再起動後でも–nogpgcheckオプションを使わなくてもエラーは発生しなくなります。

$ lxc exec images-fedora-23-amd64 -- dnf reinstall -y gzip | tee

あるいはコンテナで以下を実行します。

$ lxc exec images-fedora-23-amd64 -- /bin/bash
# dnf reinstall -y gzip | tee

3.2 Debian jessieのpolicykit-1のインストールでフリーズ(回避策なし)

lxcとDebian jessieのsystemdの相性が悪いようです。Debian stretchのsystemdをインストールすれば一時的に問題を回避できますが、依存関係が少し壊れるのでおすすめできません。lxcかDebian jessie側で問題が解消されるまで待つしかないようです。

# apt install -y policykit-1
<snip>
Setting up policykit-1 (0.105-8) ...
<hung>

4 ネットワークの設定

コンテナのネットワークにはデフォルトでdefaultプロファイルのbridgedが使われます。

$ lxc profile list
default
docker
$ lxc profile show default
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: lxdbr0
    type: nic

4.1 defaultプロファイル(非推奨)

defaultプロファイルを用いた場合は内部ネットワークを形成します。lxd-bridgeでNATを実現しており、lxd-bridgeはdnsmasqを使用しています。複数の用途が異なるnameserverを用いてる場合は名前解決で問題が発生する場合があります。

4.1.1 複数のnameserverを使い分けてると名前解決できない

以下の/etc/resolv.confは192.168.11.2で内部ネットワークの名前解決をし、192.168.11.1でインターネットの名前解決を実行しています。

$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by
# resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE
# OVERWRITTEN
nameserver 192.168.11.2
nameserver 192.168.11.1
search hiroom2.com

dnsmasqでも/etc/resolv.confを用いて名前解決を試みますが、一つのnameserverが名前解決ができないというREFUSEを返すと、そこで処理が停止しています。

lxd-bridgeではdnsmasqを–strict-orderで使用しており、一つ目のnameserverがREFUSEを実行すると以降のnameserverに問い合わせません。–strict-orderを用いない場合は複数のnameserverへ同時に問い合わせ、ひとつでもREFUSEを返すと名前解決は失敗します。

この問題を回避する為に、serverディレクティブとresolv-fileディレクティブを使用する必要があります。

serverディレクティブにてhiroom2.comの名前解決は192.168.11.2で実行し、新規に作成したresolv.confを読み込むdnsmasq.confを新規に作成します。

$ cat /var/lib/lxd-bridge/dnsmasq.conf
server=/hiroom2.com/192.168.11.2
resolv-file=/var/lib/lxd-bridge/resolv.conf

resolv.confにはインターネット向けのnameserverを記載します。

$ cat /var/lib/lxd-bridge/resolv.conf
nameserver 192.168.11.1

このdnsmasq.confをLXD_CONFILEで読み込みます。

$ diff -uprN /etc/default/lxd-bridge{.org,}
--- /etc/default/lxd-bridge.org 2016-06-18 09:16:05.533082034 +0900
+++ /etc/default/lxd-bridge     2016-06-18 09:16:18.037062209 +0900
@@ -1,7 +1,7 @@
 USE_LXD_BRIDGE="true"
 LXD_BRIDGE="lxdbr0"
 UPDATE_PROFILE="true"
-LXD_CONFILE=""
+LXD_CONFILE="/var/lib/lxd-bridge/dnsmasq.conf"
 LXD_DOMAIN="lxd"
 LXD_IPV4_ADDR="10.202.80.1"
 LXD_IPV4_NETMASK="255.255.255.0"

しかし、DHCPサーバから配布された情報を元に/etc/resolv.confを作成している場合は、DHCPサーバの配布情報が変わる度に上記のファイルを作り直す必要が出てきます。

4.1.2 動的にLXD_CONFILE向けのファイルを作成するパッチを適用

ほぼテストしていませんが、パッチを作成しました。lxd initの後で以下の手順を実行してdebファイルを作成してください。

lxdをビルドするためのパッケージをインストールします。

$ sudo apt install -y devscripts
$ sudo apt-get build-dep -y lxd

lxdのソースコードをダウンロードしてパッチを当てます。lxdのソースコードのダウンロードができない場合は/etc/apt/sources.listのdeb-srcのコメントアウトを全て外してください。

$ mkdir lxd.ubuntu-16.04
$ cd lxd.ubuntu-16.04/
$ apt source lxd
$ cd lxd-2.0.2

パッチを適用します。

$ GITHUB=https://github.com/hiroom2/lxd-pkg-ubuntu/commit
$ wget ${GITHUB}/f9c429c8c7177ad1484cb2f6b3a17e5eeacf2442.patch
$ patch -p1 < f9c429c8c7177ad1484cb2f6b3a17e5eeacf2442.patch
$ echo "a.patch" | EDITOR=true dpkg-source --commit

lxdをビルドしてインストールします。

$ dpkg-buildpackage -us -uc
$ sudo dpkg -i ../*.deb

/etc/default/lxd-bridgeでLXD_CONFILEを設定せずにLXD_GEN_CONFILE="true"を設定することでlxd-bridge起動時に/var/run/lxd-bridge/dnsmasq.confと/var/run/lxd-bridge/resolv.confが生成され、lxd-bridgeで使用されます。

$ sudo sed -i 's/^LXD_CONFILE/#LXD_CONFILE/g' /etc/default/lxd-bridge
$ sudo su -c 'echo LXD_GEN_CONFILE="true" >> /etc/default/lxd-bridge'
$ sudo systemctl restart lxd-bridge

4.2 macvlan(非推奨)

macvlanを用いることで外からアクセスできるようになります。ただしホストマシンとコンテナはアクセスできません。

$ lxc profile create vlan
$ cat <<EOF | lxc profile edit vlan
name: vlan
config: {}
description: VLAN profile
devices:
  eth0:
    name: eth0
    nictype: macvlan
    parent: ens3
    type: nic
EOF

ubuntu-16-04というコンテナにこのプロファイルを適用します。

$ lxc profile apply ubuntu-16-04 vlan

MACアドレスはlxc config showで確認できます。

$ lxc config show ubuntu-16-04 | \
grep volatile.eth0.hwaddr: | \
awk '{ print $2 }'
00:16:3e:0e:f6:38

192.168.11.0/24のネットワークで動作するDHCPサーバからMACアドレス00:16:3e:0e:f6:38へIPアドレスを配布できました。

$ lxc exec ubuntu-16-04 -- ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:16:3e:0e:f6:38
          inet addr:192.168.11.86  Bcast:192.168.11.255 Mask:255.255.255.0
          inet6 addr: fe80::216:3eff:fe0e:f638/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:70 errors:0 dropped:0 overruns:0 frame:0
          TX packets:77 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:7192 (7.1 KB)  TX bytes:9830 (9.8 KB)

4.3 bridged(推奨)

予めbridgeインターフェースを作成しておきます。これによりホストマシンからも別のマシンからもアクセスできるようになります。

br0を使うプロファイルを作成します。

$ lxc profile create bridge
$ cat <<EOF | lxc profile edit bridge
name: bridge
config: {}
description: Bridge profile
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: br0
    type: nic
EOF

5 コンテナをイメージにする

コンテナを停止した後、イメージにすることができます。

$ lxc stop ubuntu-16-04
$ lxc publish ubuntu-16-04 --alias ubuntu-16-04
Container published with fingerprint:
fdd934c336bbd506c2f4f068a872ccd7504564b50d27d3a168a4835b6cfe4e96

イメージはexportすることができます。fdd934c336bbd506c2f4f068a872ccd7504564b50d27d3a168a4835b6cfe4e96.tar.gzというファイルが作成されます。

$ lxc image export fdd934c336bbd506c2f4f068a872ccd7504564b50d27d3a168a4835b6cfe4e96

exportしたイメージを他のマシンでimportすることができます。

$ lxc import fdd934c336bbd506c2f4f068a872ccd7504564b50d27d3a168a4835b6cfe4e96.tar.gz

6 リモートサーバの設定

sudo lxc initでLXDデーモンがListenするNICとポートを設定することで、他のマシンからLXDへアクセスできるようになります。以下のコマンドでNICとポートを変更できます。

$ lxc config set core.https_address 0.0.0.0:8443

以下のコマンドでパスワードを変更できます。

$ lxc config set core.trust_password password

他のマシンのLXDから以下のコマンドを実行します。

$ lxc remote add myimages 192.168.11.77:8443
Certificate fingerprint:
c2e967e5d788db7fbbd19e1f22b789b74313083094bf5585334fe3d15192ab6a
ok (y/n)? y
Admin password for myimages: # type password
Client certificate stored at server:  myimages

6.1 イメージのダウンロード

images, ubuntu, ubuntu-dailyのようにダウンロードでます。

$ lxc image list myimages:
<snip>
...  b39919dbaf79 ...
<snip>
$ lxc launch myimages:b39919dbaf79 container-from-myimages
Creating container-from-myimages

6.2 コンテナのマイグレーション

ローカルマシンとリモートサーバにLXDの設定を含め、criuをインストールしておいてください。

$ lxc move images-centos-7-amd64 myimages:
$ lxc list myimages:
<snip>
... images-centos-7-amd64 ...
<snip>