全然理解していなかったのでちゃんと勉強してみました。
試した環境
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つある時のネットワーク
図にするとこんな感じです
まずはコンテナ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を手動で追加してみる
図にするとこんな感じです
準備
まず、 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-con
→ eth1
に変えてやります。
$ 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 をインストールしてみます。
apk は Alpine 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&ei=3UN0WduOBrGL8QfG9JGIBQ">here</A>.
</BODY></HTML>