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