pipeworkを使わずにDockerコンテナに任意の固定IPアドレスを割り当てる

# はじめに

Dockerコンテナが他のコンピュータやその上にあるコンテナと通信するために、任意の固定IPアドレスを割り当てるのに用いられるツールとして[pipework](https://github.com/jpetazzo/pipework)が知られている。
使い方： http://blog.hansode.org/archives/52634473.html

pipeworkには不満はないのだが、ただ1つREADME.mdに「Pipework should become obsolete」と書かれていることが気掛かりである。

そこで今のうちに任意の固定IPアドレスを割り当てるための別の手段を確保しておくことにした。

探してみると（0.8までデフォルトコンテナエンジンとして使われていた）LXCをコンテナエンジンとして使用すれば任意の固定IPアドレスを割り当てることができるようだ。

それでは以下に手順を記述する。
[前回](http://qiita.com/yunano/items/9637ee21a71eba197345)と同じくホスト側の操作は非rootの管理者ユーザで行っているのでプロンプトは「$」、コンテナ内での操作はプロンプトが「#」である。

# 作業手順

ホストにはCentOS 7.0.1406を使用しており、これはVirtualBox 4.3.12上で動かしている。
ホストのIPアドレスは「10.0.2.35」とし、コンテナには同じサブネットの「10.0.2.36」を割り当てるものとする。

```
$ ip r
default via 10.0.2.1 dev enp0s3  proto static  metric 1024
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.35
```

さて、まずはホストとコンテナを同じサブネットにするためにブリッジを作ることにしよう。

まず、VirtualBoxでブリッジを利用できるようにするためにはVirtualBox側の仮想マシンの設定でネットワークのアダプターのプロミスキャスモードを「すべて許可」にしておく必要がある。

以下のコマンドでブリッジを作りホスト側のIPアドレスをそのブリッジに付け替える。

```
$ sudo nmcli connection add type bridge con-name br0 ifname br0 stp no
$ sudo nmcli connection modify br0 ipv4.method manual ipv4.addresses "10.0.2.35/24 10.0.2.1" ipv4.dns 8.8.8.8
$ sudo nmcli connection add type bridge-slave ifname enp0s3 master br0
$ sudo nmcli connection modify enp0s3 connection.autoconnect no
$ sudo systemctl restart network
```

これで「10.0.2.35」のIPアドレスはブリッジのものとなっている。

```
$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.08002799ba75       no              enp0s3

$ ip r
default via 10.0.2.1 dev br0  proto static  metric 1024
10.0.2.0/24 dev br0  proto kernel  scope link  src 10.0.2.35
```

dockerとlxcをインストールする。
lxcはEPELにある。

```
$ sudo yum install docker
$ sudo yum install http://dl.fedoraproject.org/pub/epel/beta/7/x86_64/epel-release-7-0.2.noarch.rpm
$ sudo yum install lxc
```

dockerサービスの起動オプションとしてコンテナエンジンをLXCにする(-e lxc)。
ついでに、もうブリッジは作ったのでdockerにはブリッジを作らせないようにする(-b none)。
これは/etc/sysconfig/dockerにother_args=...として記述すれば良い。

```
$ echo 'other_args="-e lxc -b none"' | sudo tee -a /etc/sysconfig/docker
```

……はずなのだが、docker.serviceのユニットファイル /usr/lib/systemd/system/docker.service を見ると"ExecStart="行がother_argsを読んでくれていない。

なのでこちらで追加する。/usr/lib/systemd/system/以下は利用者が触るべきではないらしいので/etc/systemd/system/にユニットファイルをコピーして修正。
同名のユニットファイルが/usr/lib/systemd/system/以下と/etc/systemd/system/以下の両方にある場合は/etc/systemd/system/以下のものが使用される。

```
$ sudo cp /usr/lib/systemd/system/docker.service /etc/systemd/system/
$ sudo sed -ri 's/(^ExecStart=.*$)/\1 $other_args/' /etc/systemd/system/docker.service
```

ユニットファイルの書き換えを行ったので`systemctl daemon-reload`を実行してからサービスを起動する。

```
$ sudo systemctl daemon-reload
$ sudo systemctl enable docker
$ sudo systemctl start docker
```

pipeworkを使う場合はdocker runしてからpipework実行だが、LXCを使用する方法ではdocker runの際に--lxc-confオプションでIPアドレスなどを設定する。

* lxc.network.ipv4 : 割り当てるIPアドレス
* lxc.network.ipv4.gateway : ゲートウェイ
* lxc.network.link : ブリッジ。先程作ったものを指定する
* lxc.network.name : コンテナ内でのNIC名

"lxc.network.type=veth"と"lxc.network.flags=up"は変更なしで。
また、--net=noneを付けておくと--lxc-confオプションで指定した以外のNICが作られないようになるので付けておく。

```
$ sudo docker run --net=none --lxc-conf="lxc.network.type=veth" --lxc-conf="lxc.network.ipv4=10.0.2.36/24" --lxc-conf="lxc.network.ipv4.gateway=10.0.2.1" --lxc-conf="lxc.network.link=br0" --lxc-conf="lxc.network.name=eth0" --lxc-conf="lxc.network.flags=up" -it --name ip1 centos:centos7 /bin/bash
```

コンテナの「eth0」のIPアドレスが「10.0.2.36」になっていることを確認する。

```
# ip r
default via 10.0.2.1 dev eth0
10.0.2.0/24 dev eth0  proto kernel  scope link  src 10.0.2.36
```

DNSサーバはホストと同じになる。
（「# Generated by NetworkManager」行の"#"はプロンプトではなく出力）

```
# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
```

デフォルトゲートウェイにpingでも飛ばしてみる。

```
# ping 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
64 bytes from 10.0.2.1: icmp_seq=1 ttl=255 time=0.133 ms
64 bytes from 10.0.2.1: icmp_seq=2 ttl=255 time=0.191 ms
64 bytes from 10.0.2.1: icmp_seq=3 ttl=255 time=0.151 ms
^C
--- 10.0.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 0.133/0.158/0.191/0.026 ms
```

問題なく接続されている。

# さいごに

まあ、pipeworkが使えるうちは特にこちらを使うつもりはないが、一応pipeworkと比べての利点として、一度docker stopしてもう一度docker startした時にpipeworkはもう一度実行し直す必要があるが、こちらは何もしなくても設定した固定IPアドレスが割り当てられている、というのはある。

# 参考資料

* https://goldmann.pl/blog/2014/01/21/connecting-docker-containers-on-multiple-hosts/