Ubuntu Weekly Recipe

第535回LXD 3.0のネットワーク設定

第521回ではLXD 3.0の基本的な使い方を紹介しました。今回はLXD 2.0から3.0にかけて大幅に改善されたネットワーク周りの設定について紹介しましょう。特に次のような設定方法について解説します。

  • ネットワークインターフェースの追加
  • 固定IPアドレスの指定
  • 標準のインターフェースeth0のカスタマイズ
  • 特定のアドレス・ポートにホストの外からアクセスする方法

LXDにおけるネットワークの設定

現在のLXD 3.0にはネットワークに関する2種類の設定インターフェースが存在します[1]⁠。

ブリッジインターフェースはホスト上でlxd initを実行したときにも設定されます。特に設定を変更していないなら「lxdbr0」という名前のブリッジインターフェースが作られていることでしょう。

$ lxc network show lxdbr0
config:
  ipv4.address: 10.154.195.1/24
  ipv4.nat: "true"
  ipv6.address: fd42:284:93c1:a178::1/64
  ipv6.nat: "true"
description: ""
name: lxdbr0
type: bridge
used_by: []
managed: true
status: Created
locations:
- none

$ ip addr show dev lxdbr0
3: lxdbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether fe:53:81:5f:d5:f3 brd ff:ff:ff:ff:ff:ff
    inet 10.154.195.1/24 scope global lxdbr0
       valid_lft forever preferred_lft forever
    inet6 fd42:284:93c1:a178::1/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::284f:6aff:fef6:c174/64 scope link
       valid_lft forever preferred_lft forever

上記lxdbr0では、IPv4/IPv6のアドレスレンジが設定されており、それが作成されたコンテナに割り当てられます。

Ubuntu 16.04 LTSのLXD 2.0では、LXDが作る唯一のブリッジインターフェースを使うか、別途自分で作ったインターフェースを使うかの二択でした。LXD 3.0(に至るまでの2.xの間)で、lxc networkコマンドを使うことで、LXDが管理する複数のブリッジインターフェースを持てるようになり、コンテナごとにインターフェースの割り当てを変えられるようになったのです。これにより同じホスト上で異なるネットワークセグメントに属する複数のコンテナを構築できます。

個々のコンテナインスタンスにおけるネットワークインターフェースはlxc config deviceコマンドによって追加・削除します。ただし何も設定せずにコンテナを起動した場合は、lxc configは未設定の状態となり、lxc profileの値が継承されます。

たとえば何も設定せずに「container」コンテナを作成・起動してみましょう。

コンテナの作成・起動
$ lxc launch ubuntu:18.04 container

デバイス設定は空の状態
$ lxc config device show container
{}

コンテナの中にはeth0が存在する
$ lxc exec container ip addr show eth0
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:e5:c5:37 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.154.195.67/24 brd 10.154.195.255 scope global dynamic eth0
       valid_lft 3553sec preferred_lft 3553sec
    inet6 fd42:284:93c1:a178:216:3eff:fee5:c537/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 3596sec preferred_lft 3596sec
    inet6 fe80::216:3eff:fee5:c537/64 scope link
       valid_lft forever preferred_lft forever

デバイス設定は空にも関わらずeth0インターフェースが作られています。これは作成したコンテナには自動的にdefaultプロファイルが適用され、そのプロファイルの中にeth0についての設定が書かれているからです。defaultプロファイルの内容を確認してみましょう。

$ lxc profile show default
config: {}
description: ""
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default
used_by: []

上記のプロファイルの設定は、そのプロファイルが適用されているコンテナにも継承されます。ちなみに、lxc config device showではなくlxc config show--expandedオプションを付加して実行すれば、継承されたプロファイルの内容も展開されて表示されます。

LXDにおけるプロファイルの設定はまた別の機会に譲るとして、今回はネットワークの基本設定をいくつか紹介しましょう[2]⁠。

ネットワークインターフェースを追加する

コンテナに複数のネットワークインターフェースを持たせたいことがあるでしょう。LXD 3.0では次の種類(nictype)のネットワークデバイスを追加可能です。

  • physical:ホスト上の物理デバイスをそのままコンテナの中に見せる方式です。指定したデバイスはホスト上からは見えなくなります。性能的な要件から物理NICを特定のコンテナのみで使用したい場合に便利です。
  • bridged:ホスト上の仮想ブリッジに紐付いた仮想ネットワークデバイスを作成する方式です。コンテナに最初から存在するeth0はこの方式を使っています。
  • macvlan:既存のデバイスに対して異なるMACアドレスを割り当てた仮想ネットワークデバイスを作成する方式です。第533回のようにコンテナをホストの外からも見えるようにしたい場合に便利ですが、ホストとコンテナでの通信はできなくなります。
  • p2p:vethペアを作成し片方をコンテナに割り当てる方式です。bridgedと異なり、特定のインターフェースへの紐付けは必須ではありません。より柔軟に複雑なネットワーク環境を構築したい場合に使うようです。
  • sriov:ホスト上の物理デバイスの仮想機能(Virtual Function)をコンテナに専有させる方式です。物理デバイスがSR-IOVに対応している必要があります。

LXDではネットワークインターフェースを次のように追加します。

$ lxc config device add コンテナ名 設定名 nic nictype=種別 オプション

設定名は追加するデバイス設定を一意に決める値です。nicを含む他のデバイス設定と被らない値を指定する必要があります。設定名がそのままコンテナ内部のネットワークインタフェースデバイスの名前になるため、用途にあわせたわかりやすい名前を付けるといいでしょう。もし設定名とデバイス名を一致させたくない場合はオプションにname=デバイス名を指定してください。

オプションはkey=valueの形で指定します。nictypeによって、指定できるオプションが異なるので注意してください。設定可能なkeyと初期値などはLXDのドキュメントに一覧があります

「とりあえずインターフェースを追加したい」ならbridgedかmacvlanを使うことになるでしょう。このうちlxdbr0に対するbridgedはlxc networkコマンドを使います。その手順は後述の「ブリッジインターフェースで固定IPアドレスを設定する」で紹介することにして、ここではmacvlanインターフェースを追加してみましょう。

たとえば「container」コンテナにmacvlanタイプのeth1を追加したい場合は、次のように設定します。

$ lxc config device add container eth1 nic nictype=macvlan parent=enp0s31f6
デバイス eth1 が container に追加されました

ここでparent=に指定しているのは、実際にフレームを送受信するホスト上のネットワークインターフェースです。macvlanではこのインターフェースのサブインターフェースとしてデバイスを作成します[3]⁠。nictype=bridgedとして作成するならブリッジインターフェースを指定します。

実際にコンテナの中でip addrを実行するとeth1が作成されていることがわかります。

$ lxc exec container ip addr show eth1
16: eth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 00:16:3e:22:10:68 brd ff:ff:ff:ff:ff:ff link-netnsid 0

enp0s31f6が親デバイスではあるものの、MACアドレスは異なります。このため、たとえばdhclientコマンドを使えば、親デバイスとは異なるIPアドレスが割り振られます。

$ lxc exec container dhclient eth1

ためしにeth1あてに外部からアクセスしつつ、enp0s31f6に対してパケットキャプチャしてみれば、eth1宛のフレームがMACアドレスが異なるはずのenp0s31f6を経由していることがわかるでしょう。うまくアドレスが割り当たっているようであれば、第533回のように起動時にeth1に対してDHCPでアドレスを取得するよう、netplanの設定ファイルを変更しておきましょう。

ちなみにLXDで設定したmacvlanのモードは「bridge」になります。よって特に設定しなくても同じホスト上の同じ物理インターフェースを親に持つコンテナ同士なら通信できるのです。

$ lxc exec container -- ip -d link show eth1
16: eth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:22:10:68 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
    macvlan mode bridge addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

MACアドレスには「00:16:3e」で始まるランダムなアドレスが割り当てられます[4]⁠。hwaddr=FOOと設定することで、任意のMACアドレスを設定することも可能です。

もし追加したインターフェースを削除したい場合は、次のように実行します。

$ lxc config device remove container eth1
デバイス eth1 が container から削除されました

ブリッジインターフェースで固定IPアドレスを設定する

コンテナがUbuntu 18.04 LTSの場合、何も設定しなければCloud-Init経由でeth0がDHCPv4を利用してIPv4アドレスを、RAを利用してIPv6を取得する設定になっています[5]⁠。具体的にはLXD自身がdnsmasqを起動し、lxdbr0に紐付けます。このdnsmasqがDHCPv4およびステートレスDHCPv6/SLAACを利用してIPアドレスを設定するわけです。

たとえばIPv4のlease情報は次のように確認できます。

$ lxc exec container -- sh -c 'cat /run/systemd/netif/leases/*'
# This is private data. Do not parse.
ADDRESS=10.154.195.28
NETMASK=255.255.255.0
ROUTER=10.154.195.1
SERVER_ADDRESS=10.154.195.1
NEXT_SERVER=10.154.195.1
BROADCAST=10.154.195.255
T1=1800
T2=3150
LIFETIME=3600
DNS=10.154.195.1
DOMAINNAME=lxd
HOSTNAME=container
CLIENTID=ff8210b60000020000ab11cd753f09779ead4b

単にホストからコンテナのIPアドレスを知りたければlxc listコマンドを使うと良いでしょう。

$ lxc list container
+-----------+---------+----------------------+----------------------------------------------+------------+-----------+
|   NAME    |  STATE  |         IPV4         |                     IPV6                     |    TYPE    | SNAPSHOTS |
+-----------+---------+----------------------+----------------------------------------------+------------+-----------+
| container | RUNNING | 10.154.195.28 (eth0) | fd42:284:93c1:a178:216:3eff:fea7:d454 (eth0) | PERSISTENT | 0         |
+-----------+---------+----------------------+----------------------------------------------+------------+-----------+

JSONなど機械読み取り可能なフォーマットが良い場合は
「--format」オプションを指定する
$ lxc list --format=json container
(略)

コンテナの用途によっては静的にIPアドレスを割り当てたい場合もあるでしょう。次のようにbridgedなネットワークインターフェースデバイスを作成し、⁠ipv4.address」キーをセットすれば、LXDがコンテナ起動時に静的なIPv4アドレスを割り当ててくれます。

まずbridgedなネットワークインターフェースデバイスを作成します。LXDには標準でlxdbr0ネットワークが作成されていますので、これを親に持つインターフェース「eth1」を作成しましょう。

$ lxc network attach lxdbr0 container eth1

次にこのインターフェースに対してIPv4アドレス「10.154.195.100」を割り当てます。

アドレスの割り当て
$ lxc config device set container eth1 ipv4.address 10.154.195.100

デバイス情報の確認
$ lxc config device show container
eth1:
  ipv4.address: 10.154.195.100
  nictype: bridged
  parent: lxdbr0
  type: nic

「ipv4.address」による設定では、実際にこのIPv4アドレスをコンテナに割り当てるのはLXDが実行しているdnsmasqの役割です。つまりコンテナから見るとDHCP経由で固定アドレスを取得することになります。

IPアドレスの割り当て
$ lxc exec container dhclient eth1

コンテナの状態の確認
$ lxc list container
+-----------+---------+-----------------------+----------------------------------------------+------------+-----------+
|   NAME    |  STATE  |         IPV4          |                     IPV6                     |    TYPE    | SNAPSHOTS |
+-----------+---------+-----------------------+----------------------------------------------+------------+-----------+
| container | RUNNING | 10.154.195.28 (eth0)  | fd42:284:93c1:a178:216:3eff:fea7:d454 (eth0) | PERSISTENT | 0         |
|           |         | 10.154.195.100 (eth1) | fd42:284:93c1:a178:216:3eff:fe37:425f (eth1) |            |           |
+-----------+---------+-----------------------+----------------------------------------------+------------+-----------+

うまくアドレスが割り当たっているようであれば、第533回のように起動時にeth1に対してDHCPでアドレスを取得するよう、netplanの設定ファイルを変更しておきましょう。繰り返しになりますが、コンテナからはDHCP経由でアドレスを取得します。よってnetplanの設定も「dhcp4: true」としてください。LXDがleaseしているアドレスのリストはlxc network list-leasesで確認できます。

$ lxc network list-leases lxdbr0
+-----------+-------------------+----------------+---------+
| HOSTNAME  |    MAC ADDRESS    |   IP ADDRESS   |  TYPE   |
+-----------+-------------------+----------------+---------+
| container | 00:16:3e:37:42:5f | 10.154.195.100 | DYNAMIC |
+-----------+-------------------+----------------+---------+

ちなみに「ipv6.address」を使えば、静的なIPv6アドレスを指定可能です。ただしこの設定を使うためには、LXDのステートフルDHCPv6を有効化する必要があります[6]⁠。

なお、dnsmasqによるIPアドレスレンジは、lxd init時に設定しています。特に指定せずにすべて初期値どおりに設定していたとしたら、CIDR(Classless Inter-Domain Routing)形式でランダムなアドレスレンジが指定されているはずです。任意の値に変更したい場合は、次のように実行してください。

$ lxc config set lxdbr0 ipv4.dhcp.ranges \
  10.154.195.10-10.154.195.10.20,10.154.195.110-10.154.195.10.120

連続する範囲をハイフンで、不連続な範囲をカンマで区切って指定できます。

標準のインターフェースeth0をカスタマイズする

eth0はdefaultプロファイルから継承されたデバイスです。このためlxc config deviceによる設定の変更が動作しない場合もあります。おおよそ「defaultプロファイルを継承しているすべてのコンテナで適用可能とは言えない設定」は、lxc config deviceが使えないと考えればいいでしょう。たとえばIPv4アドレスを指定するipv4.addressはプロファイルで設定できませんので、プロファイルから継承したデバイスに対するlxc config deviceは使えません。

もし、eth0をより柔軟に設定変更したいのであれば、次のいずれかの方法でeth0を明示的に設定する必要があります。

  1. defaultプロファイルをカスタマイズする
  2. default以外のプロファイルを作成し、それを適用する
  3. コンテナに対してeth0設定を上書きする

1.のdefaultプロファイルをカスタマイズする方法は、そのホスト上のすべてのコンテナに対して同じ設定を適用したい場合に便利です。ただしIPアドレスのようなコンテナ固有の設定は共有できません。2.の別のプロファイルを作る方法は、特定のコンテナ群のみdefault以外のプロファイルを適用する場合、もしくはdefaultプロファイルにいくつかの設定を上書きする場合に便利です。しかしながらIPアドレス云々の制約は1.と同じです。このため、コンテナ固有の設定を行いたいなら、3.の方法が現実的でしょう。

3.の方法において、eth0をlxdbr0に属するbridgedなインターフェースとして作るのであれば、固定IPアドレスを割り当てた時と手順は同じです。ただしattachする際にあらかじめコンテナを停止しておいたほうが良いでしょう。

$ lxc stop container
$ lxc network attach lxdbr0 container eth0
$ lxc config device set container eth0 ipv4.address 10.154.195.100
$ lxc start container

macvlanなインターフェースをeth0として作るのであれば、単純に「lxc config device add」をするだけです。明示的に「name=eth0」を指定しないと、eth1が作られてしまうので注意してください。

$ lxc config device add container eth1 nic nictype=macvlan parent=enp0s31f6
$ lxc exec container dhclient eth0

特定のアドレス・ポートを公開する

コンテナでbridgedインターフェースを使っている場合、コンテナからインターネット(ホストの外)へのアクセスは可能ではあるものの、インターネット(ホストの外)からアクセスはできません。これはコンテナで動かしているサービスを外部に公開したい場合に不便です。

LXD 2.0の頃はリバースプロキシーを用意したり、iptablesを使ってポートフォワーディングなどを手動で設定する必要がありましたが、LXD 3.0までに「proxy」デバイスが追加され、これを設定することでlxcコマンドから各種転送設定を行えるようになりました。

たとえばホストの80番ポート宛のアクセスを、すべてcontainerコンテナの8080番ポートに転送するには次のように設定します。

$ lxc config device add container port80 \
  proxy listen=tcp:0.0.0.0:80 connect=tcp:localhost:8080

「port80」は任意の設定名です。LXD自身はlistenで指定したアドレスとポート番号でbind()listen()を行い、ネットワークプロキシーとして振る舞い、connectで指定したアドレス・ポート番号に転送します。

proxyデバイスは次のコマンドで削除できます。

$ lxc config device remove container port80

なお、より新しいLXD 3.x系ではTCPだけでなく、UDPやUNIXソケットなどもサポートしているようです。

おすすめ記事

記事・ニュース一覧