こんにちは、@hico_horiuchiです。
DockerコンテナにDockerホストのNIC 2つをブリッジ接続する機会があったので、その方法をまとめておきます。
複数セグメントに疎通が必要な場合や、DHCPdなどL2接続が必要な場合に役に立つと思います。
最終的な構成
最終的にDockerコンテナ〜Dockerホスト〜ネットワークの構成は下記のようになります。
Dockerのネットワークについては Docker の基本学習 ~ Docker のネットワーク - Qiita を参照。
Dockerホストのセットアップ
ネットワークの設定
eth0
と eth1
の2つのNICを持つVMを用意し、Ubuntu Server 16.04をインストールしました。
NICの命名が ensXX
になっている場合は、下記を参考に /etc/default/grub
を修正します。
$ sudo sed -i 's/^GRUB_CMDLINE_LINUX=""$/GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"/g' /etc/default/grub
$ sudo update-grub
$ sudo reboot
ブリッジの作成
br0
は eth0
に、br1
は eth1
にブリッジするように /etc/network/interfaces
を修正します。
(ネットワークの変更を適用するために再起動が必要です、注意してください。)
auto eth0
iface eth0 inet manual
auto eth1
iface eth1 inet manual
auto br0
iface br0 inet static
address 10.1.1.2
netmask 255.255.255.0
up route add default gw 10.1.1.1
bridge_ports eth0
bridge_maxwait 0
bridge_fd 0
bridge_stp off
auto br1
iface br1 inet static
address 172.16.1.2
netmask 255.255.255.0
bridge_ports eth1
bridge_maxwait 0
bridge_fd 0
bridge_stp off
ブリッジの状態確認などで brctl
コマンドを使うので、bridge-utils
を入れておくと便利です、
$ sudo apt-get install -y bridge-utils
Dockerのインストール
Dockerは個人用のAnsible Playbookを使って最新版をインストールしています。
手動でインストールする場合は Get Docker CE for Ubuntu | Docker Documentation を参照。
$ git clone git@github.com:hico-horiuchi/hiconyan-com-ansible.git
$ cd hiconyan-com-ansible
$ virtualenv venv
$ source venv/bin/activate
$ pip install -r requirements.txt
$ source venv/bin/activate
$ ansible-playbook -i hosts -t common_apt,common_ssh,docker site.yml
[conoha]
10.1.1.2 ansible_python_interpreter=/usr/bin/python3
Dockerネットワークの作成
br0
に接続する br0_network
と、br1
に接続する br1_network
という2つのDockerネットワークを作成します。
-
gateway
:br0
など、ブリッジのIPアドレスを指定します。 -
com.docker.network.bridge.enable_ip_masquerade
: NAPTさせないようにfalse
を指定します。 -
om.docker.network.bridge.name
:br0
など、使用するブリッジ名を指定します。
その他のオプションについては network create — Docker-docs-ja 1.13.RC ドキュメント を参照。
$ docker network create \
-d bridge \
--subnet 10.1.1.0/24 \
--gateway 10.1.1.2 \
-o 'com.docker.network.bridge.enable_icc=true' \
-o 'com.docker.network.bridge.enable_ip_masquerade=false' \
-o 'com.docker.network.bridge.host_binding_ipv4=0.0.0.0' \
-o 'com.docker.network.bridge.name=br0' \
-o 'com.docker.network.driver.mtu=1500' \
br0_network
$ docker network create \
-d bridge \
--subnet 172.16.1.0/24 \
--gateway 172.16.1.2 \
-o 'com.docker.network.bridge.enable_icc=true' \
-o 'com.docker.network.bridge.enable_ip_masquerade=false' \
-o 'com.docker.network.bridge.host_binding_ipv4=0.0.0.0' \
-o 'com.docker.network.bridge.name=br1' \
-o 'com.docker.network.driver.mtu=1500' \
br1_network
Dockerコンテナへのネットワーク接続
docker run
の --network
オプションでは接続するネットワークを1つしか指定できません。
そこで、一度 create
コンテナを作成し、ネットワークを2つ接続してから、start
で起動します。
$ docker create -t --name ubuntu ubuntu:16.04 /bin/bash
$ docker network connect --ip 10.1.1.3 br0_network ubuntu
$ docker network connect --ip 172.16.1.3 br1_network ubuntu
$ docker start -a ubuntu
注意事項
プロミスキャスモードについて
スイッチ側でARPスプーフィングの対策として、コンテナからのパケットがドロップされることがあります。
(コンテナのNICと、ブリッジしたNICのMACアドレスが異なるため、フィルタリングされる。)
今回はESXi上にVMを構築したのですが、「無差別モード」と「偽装転送」が拒否されておりハマってました…。
(いわゆるプロミスキャスモードと呼ばれる機能が有効になっている必要があるようです。)
docker0ネットワークについて
イメージビルド時にはdocker0が使われますが、パッケージダウンロードなどでインターネット疎通が必要ですよね。
しかし、br0_network
を作成すると、Dockerのネットワーク分離機能により、docker0
と br0
は疎通できなくなります。
br1
についても同様で、その結果インターネットに抜けられなくなってしまうので、注意してください。
(イメージビルド時にはbr0_networkとbr1_networkを作成しない、ビルドサーバを分ける、など。)
$ sudo iptables -vL | grep -A4 'Chain DOCKER-ISOLATION'
Chain DOCKER-ISOLATION (1 references)
pkts bytes target prot opt in out source destination
0 0 DROP all -- docker0 br0 anywhere anywhere
0 0 DROP all -- br0 docker0 anywhere anywhere
0 0 RETURN all -- any any anywhere anywhere