VirtualBox + Ubuntu 18.04 + LXD 3.0 上のコンテナにLAN側から接続

## 概要

VirtualBox や VMware であれば、ゲストOS のネットワークインターフェイスを「ブリッジアダプター」に設定することで、ゲストOS で動く HTTPサーバーや SSHサーバーに対して LAN側から直接接続できるようになります。

同様に LXC/LXD でも、物理マシンであれば、ネットワークインターフェースに macvlan を設定することで、LXD上のコンテナに LAN側から直接接続できるそうですが、物理マシンが用意できなかったので、VirtualBox で動く Linux上の LXD コンテナに LAN側のマシンから接続できるようにする方法について、「[How to make your LXD containers get IP addresses from your LAN using macvlan](https://blog.simos.info/how-to-make-your-lxd-container-get-ip-addresses-from-your-lan/)」などを参考に確認した時のメモです。

## 確認した構成

* ホストOS: MacOS Mojave
* 仮想化: VirtualBox 5.2 または 6.0
* VirtualBox上の OS: Ubuntu: 18.04
* コンテナ: LXD 3.0
* コンテナ上のOS: CentOS 7

前提としてホストOSは、有線でLANと接続している必要があります。無線LANではうまくいきませんでした。(無線LANで使われている IEEE802.11 の仕様で、複数のMACアドレスが使えないからだそうです。)

## 手順

次の 2つの方法で確認しました。

* macvlan を使うように設定したプロファイルを用意してコンテナを新規作成  
→ ネットワークインターフェイスは macvlan だけ
* bridged を使っている既存のコンテナに macvlan のネットワークインターフェースを追加  
→ ネットワークインターフェイスは、既存の bridged に加えて macvlan の 2個になる

どちらの場合も次に説明するように VirtualBox と Ubuntu の両方でプロミスキャスモードを有効にしておく必要があります。
また、VirtualBox ではブリッジアダプターを使うようにします(コンテナなしで普通に Linux を動して LAN側から接続する場合と同じですね)。

### VirtualBox 設定

VirtualBox で Ubuntu 18.04 のネットワークアダブター割り当てが「ブリッジアダプター」になっているかを確認します。なっていない場合は「ブリッジアダプター」に設定します。
合わせてブリッジアダプターのプロミスキャスモードが「すべて許可」に設定されているか確認します。なっていない場合は「すべて許可」に設定します。

![VirtualBoxNetworkSetting.png](https://qiita-image-store.s3.amazonaws.com/0/102132/f7624be9-6d4b-24ca-f9b2-e780647a6029.png)

### Ubuntu 18.04 でのネットワーク設定

Ubuntu 18.04 が LAN と接続しているインターフェースを確認します。

`ip route show default 0.0.0.0/0`

```
default via 192.168.1.1 dev enp0s3 proto dhcp src 192.168.1.100 metric 100 
```

上記の例では enp0s3 が LANに接続しているネットワークインターフェイスになります。

次に Ubuntu でプロミスキャスモードを有効にします。

`sudo ip link set enp0s3 promisc on`

または

`sudo ifconfig enp0s3 promisc`

Ubuntu 18.04 が起動したら、自動的にプロミスキャスモードになるようにする場合は、rc.local にコマンドを記述するなどしておきます。

`sudo vi /etc/rc.local`

```
#!/bin/sh

/sbin/ip link set enp0s3 promisc on
```
`sudo chmod 755 /etc/rc.local`

### コンテナ設定

次にコンテナ側を設定していきます。

#### 方法1: macvlan を使うように設定したプロファイルを用意してコンテナを新規作成する場合

default のプロファイルでは bridged ネットワークインターフェイスを使うように設定されています。

`lxc profile show default`

```
config: {}
description: Default LXD profile
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default
```

そこで default プロファイルを lanprofile という名前でコピーします。

`lxc profile copy default lanprofile`

lanprofile で使用するネットワークインターフェイスを macvlan に変更します。

`lxc profile device set lanprofile eth0 nictype macvlan`

続けて先ほど ip route コマンドで確認した Ubuntu 18.04 が LANと接続しているネットワークインターフェイス(ここでは enp0s3)と関連付けます。

`lxc profile device set lanprofile eth0 parent enp0s3`

lanprofile の内容を確認します。

`lxc profile show lanprofile`

```
config: {}
description: Default LXD profile
devices:
  eth0:
    name: eth0
    nictype: macvlan
    parent: enp0s3
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: lanprofile
```

nictype が macvlan に、parent が enp0s3 になっています。
このプロファイルを使って CentOS 7 のコンテナを作成します。

`lxc launch -p lanprofile images:centos/7/amd64 centos7`

コンテナ上で動いている CentOS 7 のネットワーク状態を確認します。

`lxc exec centos7 ip a s`

```
6: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:11:22:33 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.101/24 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 86371sec preferred_lft 86371sec
    inet6 fe80::216:3eff:fe11:2233/64 scope link 
       valid_lft forever preferred_lft forever
```

ここでは DHCP サーバから取得した IPアドレスが割り当てられています。
別のマシンからコンテナ上の CentOS 7 に対して ping を打ってみます。

`ping 192.168.1.101`

```
PING 192.168.1.101 (192.168.1.101): 56 data bytes
64 bytes from 192.168.1.101: icmp_seq=0 ttl=64 time=0.603 ms
```

#### 方法2: bridged を使った既存のコンテナに macvlan のネットワークインターフェースを追加する場合

次のようなコマンドを実行すことで default プロファイルを使った CentOS 7 のコンテナを作成済みだとします。
(プロファイルを指定しないと、default プロファイルが使われます。
このコンテナに macvlan のネットワークインターフェイスを追加し、LAN側から接続できるようにしていきます。)

`lxc launch images:centos/7/amd64 centos7-2`

CentOS 7 コンテナのネットワーク状態を表示します。

`lxc exec centos7-2 ip a s`

```
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:11:22:34 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.13.73.225/24 brd 10.13.73.255 scope global dynamic eth0
       valid_lft 3597sec preferred_lft 3597sec
    inet6 fe80::216:3eff:fe11:2233/64 scope link 
       valid_lft forever preferred_lft forever
```

bridged インターフェイスの IPアドレスが使われています。
このままでは LAN側から直接接続することができません。
そこでコンテナOSを止めてから macvlan で enp0s3 と関連付けられた eth1 を CentOS 7 コンテナに追加します。

`lxc stop centos7-2`  
`lxc config device add centos7-2 eth1 nic name=eth1 nictype=macvlan parent=enp0s3`

これだけでは IPアドレスが割り当てられませんので、コンテナOSを起動して、コンテナ内に DHCP サーバから IPアドレスを取得するよう、設定ファイルを作成します。

`lxc start centos7-2`  
`lxc exec centos7-2 vi /etc/sysconfig/network-scripts/ifcfg-eth1`

記述する内容は次のような感じにしました。

```
DEVICE=eth1
BOOTPROTO=dhcp
ONBOOT=yes
HOSTNAME=centos7-2
NM_CONTROLLED=no
TYPE=Ethernet
DHCP_HOSTNAME=`hostname
```

設定ファイル ifcfg-eth1 を作成したら、CentOS 7 コンテナのネットワークをリスタートします。

`lxc exec centos7-2 systemctl restart network`

再度ネットワーク状態を表示します。

`lxc exec centos7-2 ip a s`

```
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:11:22:34 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.13.73.225/24 brd 10.13.73.255 scope global dynamic eth0
       valid_lft 3596sec preferred_lft 3596sec
    inet6 fe80::216:3eff:fea6:90a0/64 scope link 
       valid_lft forever preferred_lft forever
11: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:11:22:35 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.102/24 brd 192.168.1.255 scope global dynamic eth1
       valid_lft 86398sec preferred_lft 86398sec
    inet6 fe80::216:3eff:fe11:2233/64 scope link 
       valid_lft forever preferred_lft forever
```

DHCP サーバーから IPアドレスが取得できていますので、別のマシンからコンテナ上の CentOS 7 に対して ping を打ってみます。

`ping 192.168.1.102`

```
PING 192.168.1.102 (192.168.1.102): 56 data bytes
64 bytes from 192.168.1.102: icmp_seq=0 ttl=64 time=0.347 ms
```