物理のトランクリンクを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