物理のトランクリンクをOpenvSwitchで受けて各Dockerコンテナに渡す
ご無沙汰しております(3年ぶり)
仕事?
もちろんしてますよ。あと山登りしてます。
それはそうと、仕事でネットワーク関連の検証をやっていて、ただのPing箱がたくさん欲しくなったとき、こんな感じの構成を作りたかった。
今までただPing返してくれれば良かったので、
この絵で言うコンテナ部分をnetns(Linux Network Namespace)でやっていた。
もちろんnetnsはネットワークの隔離であって、ファイルシステムやプロセス空間までは分けてくれない。
なので、すでにホストOS上でApacheだったりsshdだったりが動いていると、起動スクリプトをいじくったりしないといけない。
ここがコンテナになると、一度にネットワークもファイルシステムもプロセス空間も分離できる。
さらにDockerならDockerfile少し書いたり、出来合いのイメージ拾ってくるだけでサーバとかも一瞬で建てられる。
というわけでやってみよう。
Ubuntu16.04でやっていきます。
openvswitchとdockerはaptで導入済み前提でいきます。
まずはNICの確認。
今回は4portのNICがついたサーバを使います。
流石に物理NICのMACアドレスは隠させてください、許してください何でもしますから(なんでもするとは言ってない)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
root@Capella:~/docker# ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:5a:e9:2a:0a
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:5aff:fee9:2a0a/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:55 errors:0 dropped:0 overruns:0 frame:0
TX packets:46 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:3668 (3.6 KB) TX bytes:5490 (5.4 KB)
enp1s0 Link encap:Ethernet HWaddr 00:3d:2c:**:**:**
inet addr:172.16.0.100 Bcast:172.16.0.255 Mask:255.255.255.0
inet6 addr: fe80::23d:2cff:fe15:2454/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:86530 errors:0 dropped:0 overruns:0 frame:0
TX packets:44187 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:111934508 (111.9 MB) TX bytes:4146802 (4.1 MB)
Interrupt:16 Memory:d0900000-d0920000
enp2s0 Link encap:Ethernet HWaddr 00:3d:2c:**:**:**
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:23146 errors:0 dropped:0 overruns:0 frame:0
TX packets:941 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1767504 (1.7 MB) TX bytes:168530 (168.5 KB)
Interrupt:17 Memory:d0800000-d0820000
enp3s0 Link encap:Ethernet HWaddr 00:3d:2c:**:**:**
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)
Interrupt:18 Memory:d0700000-d0720000
enp4s0 Link encap:Ethernet HWaddr 00:3d:2c:**:**:**
inet6 addr: fe80::c9d8:61ff:aeed:9640/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:1035 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:64 (64.0 B) TX bytes:174150 (174.1 KB)
Interrupt:19 Memory:d0600000-d0620000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:244 errors:0 dropped:0 overruns:0 frame:0
TX packets:244 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:18559 (18.5 KB) TX bytes:18559 (18.5 KB)
virbr0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:192.168.122.1 Bcast:192.168.122.255 Mask:255.255.255.0
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)
wlxdc85de653e48 Link encap:Ethernet HWaddr dc:85:de:**:**:**
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)
|
続いてOpenvSwitchのブリッジを作成。
1
2
3
4
5
6
7
8
|
root@Capella:~/docker# ovs-vsctl add-br ovs-docker
root@Capella:~/docker# ovs-vsctl show
2d21e954-f52f-45fd-a4fb-86e5108b8ee4
Bridge ovs-docker
Port ovs-docker
Interface ovs-docker
type: internal
ovs_version: "2.5.2"
|
そして、物理NICの一つをOpenvSwitchにブリッジさせます。
1
2
3
4
5
6
7
8
9
10
|
root@Capella:~/docker# ovs-vsctl add-port ovs-docker enp2s0
root@Capella:~/docker# ovs-vsctl show
2d21e954-f52f-45fd-a4fb-86e5108b8ee4
Bridge ovs-docker
Port ovs-docker
Interface ovs-docker
type: internal
Port "enp2s0"
Interface "enp2s0"
ovs_version: "2.5.2"
|
続いてdockerのコンテナを立てます。
今回はPingくらいしかやらないので軽量なAlpine Linuxを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
root@Capella:~/docker# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@Capella:~/docker# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine1 latest f64255f97787 4 weeks ago 4.811 MB
alpine 3.4 f64255f97787 4 weeks ago 4.811 MB
root@Capella:~/docker# docker run -itd --privileged f64255f97787
97df6099caf7302bdad351cbd5c4894c93aaf700ed516a639380d5e18d58da78
root@Capella:~/docker# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
97df6099caf7 f64255f97787 "/bin/sh" 2 seconds ago Up 1 seconds boring_blackwell
|
ネットワーク周りを確認。
デフォルトではホストOS側で見えるvethがdocker0につながっていて、docker0のセグメントから適当なアドレスがついているはずです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
root@Capella:~/docker# docker exec 97df6099caf7 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%32696/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:22 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2732 (2.6 KiB) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1%32696/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 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:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
root@Capella:~/docker# brctl show docker0
bridge name bridge id STP enabled interfaces
docker0 8000.02425ae92a0a no veth912d17c
|
今回はdocker0は使用しないので、vethをdocker0からひっぺがします。
1
2
3
4
|
root@Capella:~/docker# brctl delif docker0 veth912d17c
root@Capella:~/docker# brctl show docker0
bridge name bridge id STP enabled interfaces
docker0 8000.02425ae92a0a no
|
そしてひっぺがしたvethを先程作ったOpenvSwitchのブリッジにくっつけます。
今回はOpenvSwitchでVLAN振り分けるので、VLAN31のタグがついたトラフィックだけを通すようにタグ指定します。
タグ付きでコンテナに渡したければ、タグ指定せずに書けばいるかも?(未検証)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
root@Capella:~/docker# ovs-vsctl add-port ovs-docker veth912d17c tag=31
root@Capella:~/docker# ovs-vsctl show
2d21e954-f52f-45fd-a4fb-86e5108b8ee4
Bridge ovs-docker
Port ovs-docker
Interface ovs-docker
type: internal
Port "enp2s0"
Interface "enp2s0"
Port "veth912d17c"
tag: 31
Interface "veth912d17c"
ovs_version: "2.5.2"
|
最後に、コンテナ内のNICのアドレスを変更して完了です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
root@Capella:~/docker# docker exec 97df6099caf7 ifconfig eth0 192.168.31.100
root@Capella:~/docker# docker exec 97df6099caf7 ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:192.168.31.100 Bcast:192.168.31.255 Mask:255.255.255.0
inet6 addr: fe80::42:acff:fe11:2%32634/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:55 errors:0 dropped:0 overruns:0 frame:0
TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:8245 (8.0 KiB) TX bytes:774 (774.0 B)
root@Capella:~/docker# docker attach 97df6099caf7
/ # ping 192.168.31.1
PING 192.168.31.1 (192.168.31.1): 56 data bytes
64 bytes from 192.168.31.1: seq=0 ttl=255 time=2.279 ms
64 bytes from 192.168.31.1: seq=1 ttl=255 time=0.751 ms
64 bytes from 192.168.31.1: seq=2 ttl=255 time=1.100 ms
64 bytes from 192.168.31.1: seq=3 ttl=255 time=1.101 ms
64 bytes from 192.168.31.1: seq=4 ttl=255 time=1.013 ms
^C
--- 192.168.31.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.751/1.248/2.279 ms
|
お疲れ様でした。
非常に簡単にできて便利ですね。
最初からopenvswitchにadd-portもできると思うんですが、まだやってないです。
Qiitaでそれっぽい記事を見つけましたが、
LXCドライバを使用すると、起動時のオプションとovsにport-addするスクリプトを少し書けばいけそうです。
Dockerコンテナを直接OpenvSwitchに接続するには(lxcドライバ使用) – Qiita
こんな感じの構成を一度作ると、Ping確認のために端末たくさん用意したり、少ない端末でケーブル頻繁に繋ぎ変えたりが必要なくなります。
適当な多ポートサーバ一台(USB NICとかでも良い)とL2SWを用意して、
あとはL2SWにケーブルプスプス挿してVLAN切ってコンテナ立てていくだけでいくらでもホスト増やせるので便利です。
しかも、Dockerなので公式イメージ拾ってくるだけでサーバー用途のホストだって一瞬で立ちます。
以下、雑記です。
dockerのネットワークは、ホストOSに見えるvethとコンテナ内のeth*がip linkの関係にあり、これを介して通信をします。
ホストOS側のvethから入った通信はコンテナ内のethに飛んでいくイメージです。仮想LANケーブルと言ったほうが良いでしょうか。
どのvethとethがペアになっているのか関係を調べるのは以下の方法が便利そうです。
Relationship between interface
コンテナ内のethのiflinkと、ホストOSのvethのifindexが同一の値になっているとのことです。
1
2
3
4
5
|
root@Capella:~/docker# docker exec 97df6099caf7 cat /sys/class/net/eth0/iflink
31
root@Capella:~/docker# cat /sys/class/net/veth912d17c/ifindex
31
|
なので、単純にホスト側はifindexで引っ張ることができそうです。
ifindexで引っ張る場合、if_indextoname()が利用できそうです。
1
2
3
4
5
6
7
8
9
10
11
|
#include<stdio.h>
#include <net/if.h>
int main(void){
int ifindex = 31;
char ifname[IF_NAMESIZE];
if(if_indextoname(ifindex, ifname) != NULL){
printf("ifindex = %d , ifname = %s\n", ifindex, ifname);
}
return(0);
}
|
コンパイル&実行
1
2
3
|
root@Capella:~/docker# gcc -o ifindexname ifindexname.c
root@Capella:~/docker# ./ifindexname
ifindex = 31 , ifname = veth912d17c
|
できた。
この辺上手く使うと、ワンラインで試験用ホスト立てたりが簡単にできそうですね(やるとはいってない)
Comments