Edited at

Dockerとホストのネットワーク on EC2

More than 1 year has passed since last update.

全然理解していなかったのでちゃんと勉強してみました。


試した環境

AMIは Amazon Linux AMI amzn-ami-hvm-2017.03.1 (ami-3bd3c45c) を使用しています。

インスタンスタイプは t2.micro です。

ポートは :22 (ssh) だけ開けました。

予め、docker をインストールします。

$ sudo yum install -y docker

$ sudo service docker start

ブリッジを操作するコマンド brctl をインストールします。

$ sudo yum install -y bridge-utils

本投稿では sudo は省略 (alias) しています。

$ vi ~/.bash_profile


# これを追加
alias docker='sudo docker'
alias ip='sudo ip'
alias brctl='sudo brctl'

$ source ~/.bash_profile


コンテナが2つある時のネットワーク


図にするとこんな感じです

default.png


まずはコンテナ2つを立ち上げる

Container 1, Continer 2 を立ち上げます。実行するプロセスは sleep 1000000

$ docker run -d --name container1 alpine sleep 1000000

$ docker run -d --name container2 alpine sleep 1000000

ちゃんと動いてるか確認します。

$ docker ps


CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac2ece448844 alpine "sleep 1000000" 5 seconds ago Up 4 seconds container2
e1147e5ec006 alpine "sleep 1000000" 14 seconds ago Up 13 seconds container1


ホストのネットワークを見てみる

ホスト側のネットワークを見てみます。↑図と大体同じような感じになっています。

$ ifconfig


# "veth0000001", "veth0000002" のブリッジ
docker0 Link encap:Ethernet HWaddr 02:42:DE:AF:0E:4C
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:deff:feaf:e4c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:28 errors:0 dropped:0 overruns:0 frame:0
TX packets:14 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1744 (1.7 KiB) TX bytes:1068 (1.0 KiB)

eth0 Link encap:Ethernet HWaddr 06:AC:37:4A:1A:AD
inet addr:172.30.1.211 Bcast:172.30.1.255 Mask:255.255.255.0
inet6 addr: fe80::4ac:37ff:fe4a:1aad/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:45727 errors:0 dropped:0 overruns:0 frame:0
TX packets:22649 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:63486801 (60.5 MiB) TX bytes:1659723 (1.5 MiB)

...

# 図中の "veth0000001" に該当
veth2b8058a Link encap:Ethernet HWaddr F2:BD:7F:6A:C2:88
inet6 addr: fe80::f0bd:7fff:fe6a:c288/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14 errors:0 dropped:0 overruns:0 frame:0
TX packets:42 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1068 (1.0 KiB) TX bytes:3204 (3.1 KiB)

# 図中の "veth0000002" に該当
vethd2804f4 Link encap:Ethernet HWaddr B6:2C:76:4A:0B:DD
inet6 addr: fe80::b42c:76ff:fe4a:bdd/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14 errors:0 dropped:0 overruns:0 frame:0
TX packets:28 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1068 (1.0 KiB) TX bytes:2048 (2.0 KiB)

ホストの routing table はこう↓です。

$ route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.30.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.169.254 0.0.0.0 255.255.255.255 UH 0 0 0 eth0 # これはEC2 metadata用.
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.30.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

ホストのブリッジ情報を表示してみます。docker0 ブリッジにvethが2つ接続されています。それぞれのvethの先に Container 1, Container 2 のネットワーク名前空間があります。

$ brctl show


bridge name bridge id STP enabled interfaces
docker0 8000.0242deaf0e4c no veth2b8058a
vethd2804f4


コンテナの名前空間を見てみる

Dockerはプロセスの実行環境をホストOSや他コンテナと分離をするサービスです。Linuxの ネットワーク名前空間 / マウント名前空間 / PID名前空間 / etc... 機能を利用して実現しています。

各コンテナの名前空間を探す為に、まずホスト側からコンテナプロセスのPIDを探します。 PID 3100, 3211 がそれですね。

$ ps auxf


USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
root 2810 0.0 4.1 435916 42528 pts/0 Sl 03:31 0:01 /usr/bin/dockerd --default-ulimit nofile=1024:4096
root 2816 0.0 0.9 228760 9912 ? Ssl 03:31 0:00 \_ docker-containerd -l unix:///var/run/docker/libcontainerd/...
root 3084 0.0 0.2 135792 2568 ? Sl 03:40 0:00 \_ docker-containerd-shim ... /var/run/docker/libcontainerd/...
root 3100 0.0 0.0 1524 4 ? Ss 03:40 0:00 | \_ sleep 1000000
root 3188 0.0 0.2 201328 2504 ? Sl 03:40 0:00 \_ docker-containerd-shim ... /var/run/docker/libcontainerd/...
root 3211 0.0 0.0 1524 4 ? Ss 03:40 0:00 \_ sleep 1000000

この PID=3100 sleep 1000000 プロセスが実行されている名前空間は↓で確認できます。このうち net がネットワーク名前空間になります。

$ sudo ls -l /proc/3100/ns/


total 0
lrwxrwxrwx 1 root root 0 Jul 23 04:08 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Jul 23 04:08 ipc -> ipc:[4026532159]
lrwxrwxrwx 1 root root 0 Jul 23 04:08 mnt -> mnt:[4026532157]
lrwxrwxrwx 1 root root 0 Jul 23 03:40 net -> net:[4026532162]
lrwxrwxrwx 1 root root 0 Jul 23 04:08 pid -> pid:[4026532160]
lrwxrwxrwx 1 root root 0 Jul 23 04:08 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul 23 04:08 uts -> uts:[4026532158]


コンテナのネットワークを見てみる

次に ip コマンドを使って、コンテナのネットワーク名前空間上で bash プロセスを立ち上げてみます。

ip コマンドは /var/run/netns/ 以下のネットワーク名前空間しか扱えないので、まずはシンボリックリンクを貼ってやります。

$ sudo mkdir -p /var/run/netns/

$ sudo ln -s /proc/3100/ns/net /var/run/netns/container1

ネットワーク名前空間 container1 上で bash プロセスを立ち上げます。

$ ip netns exec container1 bash


root#


"$ ip netns exec {ネットワーク名前空間名} {実行したいコマンド}"


コンテナ側のネットワークを盗み見る事ができました。

root# ifconfig

eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:45 errors:0 dropped:0 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3414 (3.3 KiB) TX bytes:1138 (1.1 KiB)

...

コンテナの routing table も見てみます。

root# route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0


コンテナにNICを手動で追加してみる


図にするとこんな感じです

2nic.png


準備

まず、 Container 2 は邪魔なので落としておきます

$ docker stop container2

$ docker rm container2

Container 2 のNICへのブリッジも自動で削除されています。

$ brctl show


bridge name bridge id STP enabled interfaces
docker0 8000.0242deaf0e4c no veth2b8058a


veth (Virtual Ethernet) を追加する

veth (1ケーブルで接続済の仮想NICのペア) を追加します。ホスト側のブリッジ docker0 と接続する方を veth-host, コンテナ側を veth-con とします。

$ ip link add name veth-host type veth peer name veth-con

$ ip link set veth-host up

ホスト側NIC veth-host をブリッジ docker0 に接続します。

$ brctl addif docker0 veth-host

$ brctl show

bridge name bridge id STP enabled interfaces
docker0 8000.0242deaf0e4c no veth2b8058a
veth-host

この時はまだ veth-host, veth-con 共にホスト側のネットワーク名前空間に属している為、ホスト側から両方とも見えます。

$ ifconfig veth-host


veth-host Link encap:Ethernet HWaddr 3A:D3:41:66:58:F8
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

$ ifconfig veth-con

veth-con Link encap:Ethernet HWaddr 5E:98:45:07:48:00
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

コンテナ側NIC veth-con のネットワーク名前空間を container1 に変更します。

$ ip link set veth-con netns container1

ホスト側からは見えなくなります。

$ ifconfig veth-con


veth-con: error fetching interface information: Device not found

逆にコンテナ側は見える様になります。

$ ip netns exec container1 ifconfig veth-con


veth-con Link encap:Ethernet HWaddr B2:45:A8:96:1B:AA
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)


コンテナ側NICの設定

まずはNIC名を veth-coneth1 に変えてやります。

$ ip netns exec container1 ip link set veth-con name eth1


"$ ip netns exec container1 ..." → コンテナ側 (netns) でコマンド実行している


NIC eth1 にIP 172.17.0.99/24 を割り当てます。

$ ip netns exec container1 ip addr add 172.17.0.99/24 dev eth1

$ ip netns exec container1 ip link set eth1 up

コンテナ側からIPが見れる様になります。

$ ip netns exec container1 ifconfig eth1


eth1 Link encap:Ethernet HWaddr B2:45:A8:96:1B:AA
inet addr:172.17.0.99 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::b045:a8ff:fe96:1baa/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5714 errors:0 dropped:0 overruns:0 frame:0
TX packets:4448 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:121496019 (115.8 MiB) TX bytes:309958 (302.6 KiB)


コンテナ側 routing table の変更

せっかく追加したNIC eth1 が全く使われていないので、routing table を変更してやります。

$ ip netns exec container1 ip route delete default

$ ip netns exec container1 ip route add default via 172.17.0.1 dev eth1

この通り、外部アクセスが新規追加したNIC eth1 経由となりました。

$ ip netns exec container1 route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth1
172.17.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0


eth0 で入って来たのが eth1 から出てしまいますが、今回は無視します



コンテナに侵入して確認してみる

実際にコンテナに侵入して、意図したとおりに動いているか見てみます。

$ docker exec -it container1 sh


/ # ps aux
PID USER TIME COMMAND
1 root 0:00 sleep 1000000
5 root 0:00 sh
10 root 0:00 ps aux

元からあった eth0 (172.17.0.2) と、追加した eth1 (172.17.0.99) が見えます。

# ifconfig


eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2040 errors:0 dropped:0 overruns:0 frame:0
TX packets:1320 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:48591073 (46.3 MiB) TX bytes:97477 (95.1 KiB)

eth1 Link encap:Ethernet HWaddr B2:45:A8:96:1B:AA
inet addr:172.17.0.99 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::b045:a8ff:fe96:1baa/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5714 errors:0 dropped:0 overruns:0 frame:0
TX packets:4448 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:121496019 (115.8 MiB) TX bytes:309958 (302.6 KiB)
...

routing table も先程設定したとおりです。

# route -n


Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth1
172.17.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0

curl をインストールしてみます。


apkAlpine Linux のパッケージ管理システム


/ # apk update


fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/community/x86_64/APKINDEX.tar.gz
v3.6.2-40-gf1c202674f [http://dl-cdn.alpinelinux.org/alpine/v3.6/main]
v3.6.2-32-g6f53cfcccd [http://dl-cdn.alpinelinux.org/alpine/v3.6/community]
OK: 8436 distinct packages available

/ # apk add curl

(1/3) Installing libssh2 (1.8.0-r1)
(2/3) Installing libcurl (7.54.0-r0)
(3/3) Installing curl (7.54.0-r0)
Executing busybox-1.26.2-r5.trigger
OK: 231 MiB in 41 packages

google.com にアクセスしてみます。

/ # curl google.com


<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.jp/?gfe_rd=cr&amp;ei=3UN0WduOBrGL8QfG9JGIBQ">here</A>.
</BODY></HTML>