はじめに
今回は、Dockerのネットワークの仕組みについて解説します。対象読者はL2, L3の知識がある方となっております。
Dockerのネットワークの仕組み
先に結論を出すと、以下のような構成になっています。(公式ドキュメントより)
Ref: https://docs.docker.com/engine/tutorials/networkingcontainers
こんなのはググればそこら辺に転がっていますが、今回は実際にコマンドを叩いて確認します。
Docker起動
今回はUbuntu上にDockerを起動させました。
Dockerは起動時に以下のことを行います。実際に見ていきましょう。
- ルーティングを作成
- 仮想ブリッジであるdocker0を作成
- コンテナのIPからeth0のIPに変換するのNAPTルールを作成
ルーティングの確認
新しいルーティングが追加されていますね。
$ ip route
default via 172.31.32.1 dev eth0 proto dhcp src 172.31.33.239 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.31.0.2 via 172.31.32.1 dev eth0 proto dhcp src 172.31.33.239 metric 100
172.31.32.0/20 dev eth0 proto kernel scope link src 172.31.33.239 metric 100
172.31.32.1 dev eth0 proto dhcp scope link src 172.31.33.239 metric 100
送信元IPが172.17.0.1で送信先IPが172.17.0.0/16の場合はdocker0にパケットを送信します。
仮想ブリッジのdocker0を確認
仮想ブリッジ 1 であるdocker0が作成されていることが分かります。
$ ip address show
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:e1:36:00:e8 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
L2スイッチの役割を果たすのでMACアドレステーブルもあるということです。brctlコマンドをインストールします。
$ sudo apt-get install bridge-utils -y
まだコンテナが立ち上がってないのでMACアドレステーブルには何も表示はされていないです。
$ brctl showmacs docker0
port no mac addr is local? ageing timer
NATテーブルを確認
NATテーブルを確認することでルールが追加されているのが確認できます。
$ sudo iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
送信元IPが172.17.0.0/16(Dockerのネットワーク)かつ送信先IPが0.0.0.0/0なら、送信元IPを物理NIC(eth0)のIPに変換し、外部のネットワークにアクセスできるようになります。
コンテナ起動
Nginxのコンテナを立ち上げてみます。
docker run --name nginx -d -p 80:80 nginx
コンテナが起動すると以下のことを行います。実際に見ていきましょう。
- コンテナ専用のNetwork Namespaceとdocker0をveth2にて接続
- 外部からのポートフォワードルールの作成
Virtual Ethernet Pairの確認
コンテナを立ち上げると、新しいインターフェースが作成されています。vethは2つのインターフェースがペアで作成され、インターフェース間で通信が行うことができます。つまり、トンネリングです。
$ ip address show
7: veth6c4af0c@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether aa:72:12:12:e8:c7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::a872:12ff:fe12:e8c7/64 scope link
valid_lft forever preferred_lft forever
$ brctl show docker0
bridge name bridge id STP enabled interfaces
docker0 8000.0242e13600e8 no veth6c4af0c
コンテナ側のvethも確認していきましょう。Nginxコンテナに入ります。
docker exec -it nginx bash
ipコマンドが入っていなかったので以下のコマンドでインストールします。
apt-get update && apt-get install iproute2 -y
コンテナは自動的にIPアドレスが割り当てられており、コンテナからはeth0として見えていることが分かります。
$ ip address show
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
NATテーブルを確認
NATテーブルを確認することでルールが追加されているのが確認できます。
$ sudo iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:80
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
PREROUTINGにてDOCKERチェーンが呼ばれており、ホストの80番の通信をDNATしていることが分かります。
さいごに
L2スイッチを触ったことがあるものの、仮想ネットワークに関してはあまり詳しくなかったので勉強になりました。これからDockerについてさらに深ぼって行く予定です。
参考文献