Network
AWS
docker
alpine

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>