LXDコンテナで固定IPアドレスを使うための設定

2016-08-12 追記

lxd-bridge サービスを再起動せずに固定IPアドレス設定を更新できるようにするための設定方法を LXDのdnsmasqの固定IP設定をSIGHUPで更新する · hnakamur's blog at github に書きました。こちらのほうがお勧めです。

設定まとめ

自分が後から参照することを想定して先に設定方法をまとめます。

LXDコンテナで固定IPアドレスを使うためには以下の設定が必要です。なお、設定にはroot権限が必要です。

まず /etc/dnsmasq.conf に以下のようにコンテナ名に対するIPアドレスを指定します。

dhcp-host=cent01,10.64.177.101
dhcp-host=cent02,10.64.177.102

lxd-bridge サービスを再起動します。

sudo systemctl restart lxd-bridge

IPアドレスを変更したコンテナを再起動します。

lxc restart cent01
lxc restart cent02

調査メモ

以下は調査メモです。

まず Persistent IP for Containers · Issue #1168 · lxc/lxd に固定IPアドレスを使うための情報がありました。LXDで特にサポートはないが、各コンテナでDHCPを使わずに静的IPアドレスを使用するか、あるいはホストのDHCPサーバ側で設定すれば実現できるとのことです。

各コンテナで静的IPアドレスを使う方法も試してみたのですが、ホストのコンテナ内から別のコンテナをコンテナ名で参照しようとすると変更前のIPアドレスで通信しようとしてしまいうまく行きませんでした。

これを実現するにはホストのDHCPサーバに各コンテナのIPアドレスを把握してもらう必要があるので、後者のDHCPサーバ側で設定するほうが良いです。

LXDのブリッジインターフェースとdnsmasqの設定ファイル

LXD networking: lxdbr0 explained | Ubuntu Insightslxcbr0 is being replaced by lxdbr0によると、 LXCでは lxcbr0 というブリッジインターフェースを使っていましたが、LXDでは lxdbr0 と別のインターフェースを使うように変更されたそうです。

これらの記事を見るとLXCの lxcbr0 はインストール時に固定のアドレスネットワークが設定されて環境によっては既存のネットワークと衝突するという問題があったので、LXD の lxdbr0 ではインストール時にはIPv4やIPv6のサブネットは設定せずに sudo lxd init を実行したときに設定するように変更されたということのようです。

sudo lxd init で生成した lxdbr0 用の設定は /etc/default/lxd-bridge に保存されています。 ファイルの先頭に書かれていますが、変更したい場合は直接編集せずに dpkg-reconfigure -p medium lxd を実行するのが良いそうです。

$ cat /etc/default/lxd-bridge
# WARNING: This file is generated by a debconf template!
# It is recommended to update it by using "dpkg-reconfigure -p medium lxd"

# Whether to setup a new bridge or use an existing one
USE_LXD_BRIDGE="true"

# Bridge name
# This is still used even if USE_LXD_BRIDGE is set to false
# set to an empty value to fully disable
LXD_BRIDGE="lxdbr0"

# Update the "default" LXD profile
UPDATE_PROFILE="true"

# Path to an extra dnsmasq configuration file
LXD_CONFILE=""

# DNS domain for the bridge
LXD_DOMAIN="lxd"

# IPv4
## IPv4 address (e.g. 10.0.8.1)
LXD_IPV4_ADDR="10.16.29.1"

## IPv4 netmask (e.g. 255.255.255.0)
LXD_IPV4_NETMASK="255.255.255.0"

## IPv4 network (e.g. 10.0.8.0/24)
LXD_IPV4_NETWORK="10.16.29.1/24"

## IPv4 DHCP range (e.g. 10.0.8.2,10.0.8.254)
LXD_IPV4_DHCP_RANGE="10.16.29.2,10.16.29.254"

## IPv4 DHCP number of hosts (e.g. 250)
LXD_IPV4_DHCP_MAX="252"

## NAT IPv4 traffic
LXD_IPV4_NAT="true"

# IPv6
## IPv6 address (e.g. 2001:470:b368:4242::1)
LXD_IPV6_ADDR="fd94:d372:e27f:2987::1"

## IPv6 CIDR mask (e.g. 64)
LXD_IPV6_MASK="64"

## IPv6 network (e.g. 2001:470:b368:4242::/64)
LXD_IPV6_NETWORK="fd94:d372:e27f:2987::1/64"

## NAT IPv6 traffic
LXD_IPV6_NAT="true"

# Run a minimal HTTP PROXY server
LXD_IPV6_PROXY="false"

このブリッジインタフェースを有効にするには lxd-bridge.service を開始します。サービスの定義ファイルは以下のようになっていました。

$ cat /lib/systemd/system/lxd-bridge.service
[Unit]
Description=LXD - network bridge
Documentation=man:lxd(1)
Before=lxd.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/lxd/lxd-bridge.start
ExecStop=/usr/lib/lxd/lxd-bridge stop

ExecStart に指定しているスクリプトの中身は以下のようになっていました。

$ cat /usr/lib/lxd/lxd-bridge.start
#!/bin/sh -e

[ ! -e /etc/default/lxd-bridge ] && exit 0

. /etc/default/lxd-bridge

# Start by bringing up the bridge
/usr/lib/lxd/lxd-bridge start

# Switch LXD in setup mode if needed
if [ "${UPDATE_PROFILE:-true}" = "true" ] && [ -e "/var/lib/lxd" ] && \
    ([ ! -e "/var/lib/lxd-bridge/timestamp" ] || \
     [ "/etc/default/lxd-bridge" -nt "/var/lib/lxd-bridge/timestamp" ]); then

    mkdir -p /var/lib/lxd-bridge
    touch /var/lib/lxd-bridge/timestamp

    touch /var/lib/lxd/.setup_mode
fi

exit 0

ここから呼ばれる /usr/lib/lxd/lxd-bridge を見てみると、initスクリプトになっていて iptables, ip6tables, dnsmasq を実行するようになっていました。また /usr/lib/lxd/lxd-bridge は上記の /etc/default/lxd-bridge を読み込むようになっています。

起動された dnsmasqps で見ると以下のようなコマンドラインになっていました。

$ ps auxww | grep [d]nsmasq lxd 2134 0.0 0.0 49984 388 ? S 09:48 0:00 dnsmasq -s lxd -S /lxd/ -u lxd --strict-order --bind-interfaces --pid-file=/run/lxd-bridge//dnsmasq.pid --dhcp-no-override --except-interface=lo --interface=lxdbr0 --dhcp-leasefile=/var/lib/lxd-bridge//dnsmasq.lxdbr0.leases --dhcp-authoritative --listen-address 10.16.29.1 --dhcp-range 10.16.29.2,10.16.29.254 --dhcp-lease-max=252 --dhcp-range=fd94:d372:e27f:2987::1,ra-only --listen-address fd94:d372:e27f:2987::1

/usr/lib/lxd/lxd-bridge を見ると /etc/default/lxd-bridgeLXD_CONFILE にファイル名を指定しておけば dnsmasq--conf-file オプションを使ってそのファイルを読み込むように書かれています。この方法を使おうかと思ったのですが、一方で /etc/default/lxd-bridge の設定にかかわらず --dhcp-authoritative オプションが常に指定されるように書かれています。

man dnsmasq によると --dhcp-authoritative オプションはネットワーク内に他にDHCPサーバが無く唯一のDHCPになっているときに指定するオプションとのことです。ホストで稼働する dnsmasq が1つという前提であれば、デフォルトの設定ファイル /etc/dnsmasq.conf を作ってそこに設定を書くほうが手っ取り早いので、そうすることにしました。

lxd-bridge.serviceの設定変更はreloadではなくrestartが必要

sudo systemctl reload lxd-bridge を実行してみると以下のようなエラーが出て失敗しました。

$ sudo systemctl reload lxd-bridge
Failed to reload lxd-bridge.service: Job type reload is not applicable for unit lxd-bridge.service.
See system logs and 'systemctl status lxd-bridge.service' for details.
$ sudo systemctl status lxd-bridge
● lxd-bridge.service - LXD - network bridge
   Loaded: loaded (/lib/systemd/system/lxd-bridge.service; static; vendor preset: enabled)
   Active: active (exited) since 土 2016-05-07 13:06:06 JST; 6h ago
     Docs: man:lxd(1)
  Process: 3704 ExecStop=/usr/lib/lxd/lxd-bridge stop (code=exited, status=0/SUCCESS)
  Process: 3723 ExecStart=/usr/lib/lxd/lxd-bridge.start (code=exited, status=0/SUCCESS)
 Main PID: 3723 (code=exited, status=0/SUCCESS)
    Tasks: 1 (limit: 512)
   Memory: 412.0K
      CPU: 1.293s
   CGroup: /system.slice/lxd-bridge.service
           └─3755 dnsmasq -s lxd -S /lxd/ -u lxd --strict-order --bind-interfaces --pid-file=/run/lxd-bridge//dnsmasq.pid --dhcp-no-override --except-interface=lo --interface=lxdbr0 --dhcp-leasefile=/var/lib/lxd-bridge//dnsmasq.lxdbr0.leases --dhcp-authoritative --lis
 5月 07 18:56:28 express dnsmasq-dhcp[3755]: not giving name cent02 to the DHCP lease of 10.155.92.102 because the name exists in /etc/hosts with address 10.155.92.201
 5月 07 19:06:21 express dnsmasq-dhcp[3755]: DHCPREQUEST(lxdbr0) 10.155.92.102 00:16:3e:59:21:d7
 5月 07 19:06:21 express dnsmasq-dhcp[3755]: DHCPACK(lxdbr0) 10.155.92.102 00:16:3e:59:21:d7 cent02
 5月 07 19:06:21 express dnsmasq-dhcp[3755]: not giving name cent02 to the DHCP lease of 10.155.92.102 because the name exists in /etc/hosts with address 10.155.92.201
 5月 07 19:23:24 express dnsmasq-dhcp[3755]: DHCPREQUEST(lxdbr0) 10.155.92.101 00:16:3e:5f:01:7e
 5月 07 19:23:24 express dnsmasq-dhcp[3755]: DHCPACK(lxdbr0) 10.155.92.101 00:16:3e:5f:01:7e cent01
 5月 07 19:23:24 express dnsmasq-dhcp[3755]: not giving name cent02 to the DHCP lease of 10.155.92.102 because the name exists in /etc/hosts with address 10.155.92.201
 5月 07 19:33:08 express dnsmasq-dhcp[3755]: DHCPREQUEST(lxdbr0) 10.155.92.102 00:16:3e:59:21:d7
 5月 07 19:33:08 express dnsmasq-dhcp[3755]: DHCPACK(lxdbr0) 10.155.92.102 00:16:3e:59:21:d7 cent02
 5月 07 19:33:08 express dnsmasq-dhcp[3755]: not giving name cent02 to the DHCP lease of 10.155.92.102 because the name exists in /etc/hosts with address 10.155.92.201

ということで、設定ファイルを変更反映するにも sudo systemctl restart lxd-bridge のように再起動する必要があります。

dnsmasqの再起動後、コンテナの再起動が必要

dnsmasqを再起動しただけではコンテナのIPアドレスは変わらないのでネットワークを再起動する必要がありました。

lxc exec cent01 systemctl restart network
lxc exec cent02 systemctl restart network

コンテナごと再起動でも良いと思います。

lxc restart cent01
lxc restart cent02

何回か試してみたところ、作っただけで特に何もしてないコンテナだとネットワーク再起動よりコンテナ自体を再起動するほうが速かったです。

$ time lxc exec cent02 systemctl restart network

real    0m2.878s
user    0m0.008s
sys     0m0.000s
$ time lxc restart cent02

real    0m1.236s
user    0m0.004s
sys     0m0.004s