こんにちは、サイオステクノロジー技術部 武井です。Dockerのネットワークが気になったのでちょっと調べてみました。
以下のコマンドで簡単なコンテナを起動して、内部のネットワークを調べてみました。PHPが動作するWebサーバーを立ち上げて、-pオプションで80番をポートフォワードします。
# docker run -d -p 80:80 --name php70-apache php:7.0-apache
こんな構成になっているようです。
仮想ブリッジが内部にあり、veth(Virtual Ethernet Pair)が仮想ブリッジにつながっていて、そのペアにコンテナ側のインターフェースがいます。 vethは仮想的なネットワークインターフェースで、異なるnamespaceにあるインターフェース同士をつなぐことが出来ます。
つまり、仮想ブリッジを介して、コンテナ同士がL2のレイヤーでつながっています。
試しに見てみました。
# ip addr show ... 4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:68:de:b6:2a 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 inet6 fe80::42:68ff:fede:b62a/64 scope link valid_lft forever preferred_lft forever 6: vethca38e86@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP link/ether 22:35:bb:aa:3d:15 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::2035:bbff:feaa:3d15/64 scope link valid_lft forever preferred_lft forever ...
docker0という仮想ブリッジがあるのが見えました。仮想ブリッジは172.17.0.1というIPアドレスを持っていました。
また、vethca38e86というのが、vethのインターフェースで、コンテナ側のネットワークインターフェースとケーブルで繋がっているようなイメージです。
こちらも以下のコマンドで見てみました。
# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024268deb62a no vethca38e86
仮想ブリッジに先程のvethca38e86のインターフェースが接続されているのがわかります。
物理NICと仮想ブリッジは通信は、OSの機能でルーティングされます。以下のコマンドで見てみましたが、172.17.0.0/16宛の通信がdocker0(仮想ブリッジのインターフェース)にルーティングされているのがわかります。
# ip route default via 10.0.2.2 dev enp0s3 proto static metric 100 10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 192.168.56.0/24 dev enp0s8 proto kernel scope link src 192.168.56.38 metric 100
iptablesの設定も見てみました。
# Generated by iptables-save v1.4.21 on Mon Sep 10 22:38:05 2018 *nat :PREROUTING ACCEPT [11:1858] :INPUT ACCEPT [9:706] :OUTPUT ACCEPT [115:8648] :POSTROUTING ACCEPT [115:8648] :DOCKER - [0:0] -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE -A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE -A DOCKER -i docker0 -j RETURN -A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80 COMMIT # Completed on Mon Sep 10 22:38:05 2018 # Generated by iptables-save v1.4.21 on Mon Sep 10 22:38:05 2018 *filter :INPUT ACCEPT [1127:114395] :FORWARD DROP [0:0] :OUTPUT ACCEPT [701:70242] :DOCKER - [0:0] :DOCKER-ISOLATION-STAGE-1 - [0:0] :DOCKER-ISOLATION-STAGE-2 - [0:0] :DOCKER-USER - [0:0] -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION-STAGE-1 -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A FORWARD -o docker0 -j DOCKER -A FORWARD -i docker0 ! -o docker0 -j ACCEPT -A FORWARD -i docker0 -o docker0 -j ACCEPT -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2 -A DOCKER-ISOLATION-STAGE-1 -j RETURN -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP -A DOCKER-ISOLATION-STAGE-2 -j RETURN -A DOCKER-USER -j RETURN COMMIT # Completed on Mon Sep 10 22:38:05 2018
以下のPREROUTINGチェインで、物理NICの80番ポート宛への通信の宛先を、コンテナのIPアドレスの80番あてへとDNATしているようです。
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A DOCKER -i docker0 -j RETURN -A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
ローカルのインターフェース宛に届いたパケットは、独自定義のチェインDOCKERに飛んで、2番めのチェインでは、送信元のインターフェースがdoker0(仮想ブリッジのインターフェース)ではないので、そのまま3番目のチェインに飛んで、そこで宛先が80/tcpの通信は、コンテナのIPアドレスの80番にDNATしているという感じだと思います、多分。
こんな感じで、コンテナまでパケットが届くんだということがわかりました。
物理的に以下のようなネットワークを、仮想ブリッジやiptablesなどの機能でエミュレートしているのだと思います、多分。
以上です。今回は備忘録というかメモ的な感じですみません(´・ω・`)