どんなことをする?
mininetはネットワークエミュレータです。仮想でホストとOpenFlowスイッチを構成し、その箱庭環境の中で色々な動作確認を行うことができます。デフォルトではこんなトポロジーになってます。
コレだとやれることって結構寂しいので、こんなトポロジーは組めないかなと思い、組んでみました。コントローラ側は、前回と同じ「simple_switch.py」を使用します。
トポロジーの組み方
トポロジーを組むには、どうやらPythonと言う言語を用いて、構成を書く必要があるようです。今回、4台のスイッチ、10台のホストを上図のように接続する場合、以下のように記述する必要があるようです。これを~/mininet/custom/topo-4sw-10host.pyとして保存しました。
from mininet.topo import Topo class MyTopo( Topo ): "Simple topology example" def __init__( self ): # Initialize topology Topo.__init__( self ) # Add hosts and switches Host1 = self.addHost( 'h1' , ip='192.168.10.1' , mac='00:00:00:00:00:01' ) Host2 = self.addHost( 'h2' , ip='192.168.10.2' , mac='00:00:00:00:00:02' ) Host3 = self.addHost( 'h3' , ip='192.168.10.3' , mac='00:00:00:00:00:03' ) Host4 = self.addHost( 'h4' , ip='192.168.10.4' , mac='00:00:00:00:00:04' ) Host5 = self.addHost( 'h5' , ip='192.168.10.5' , mac='00:00:00:00:00:05' ) Host6 = self.addHost( 'h6' , ip='192.168.10.6' , mac='00:00:00:00:00:06' ) Host7 = self.addHost( 'h7' , ip='192.168.10.7' , mac='00:00:00:00:00:07' ) Host8 = self.addHost( 'h8' , ip='192.168.10.8' , mac='00:00:00:00:00:08' ) Host9 = self.addHost( 'h9' , ip='192.168.10.9' , mac='00:00:00:00:00:09' ) Host10 = self.addHost( 'h10' , ip='192.168.10.10' , mac='00:00:00:00:00:10' ) Switch1 = self.addSwitch( 's1' , ip='192.168.10.11' , dpid='0000000000000001' ) Switch2 = self.addSwitch( 's2' , ip='192.168.10.12' , dpid='0000000000000002' ) Switch3 = self.addSwitch( 's3' , ip='192.168.10.13' , dpid='0000000000000003' ) Switch4 = self.addSwitch( 's4' , ip='192.168.10.14' , dpid='0000000000000004' ) # Add links self.addLink( Switch1, Switch2 ) self.addLink( Switch2, Switch3 ) self.addLink( Switch3, Switch4 ) self.addLink( Host1, Switch1 ) self.addLink( Host2, Switch1 ) self.addLink( Host3, Switch2 ) self.addLink( Host4, Switch2 ) self.addLink( Host5, Switch2 ) self.addLink( Host6, Switch2 ) self.addLink( Host7, Switch3 ) self.addLink( Host8, Switch3 ) self.addLink( Host9, Switch4 ) self.addLink( Host10, Switch4 ) topos = { 'mytopo': ( lambda: MyTopo() ) }
Pythonと言う言語自体触ったことがなかったので、トライ&エラーで書くだけ書いてみたのですが、以下の点に気をつける必要があるようです。
- C言語で言う{}部分は「インデント」で認識される
- つまり、ある処理系の中身を書く際は、その宣言より1段インデントを奥にずらす必要がある
- 上記テキストでは半角スペースでインデントされていますが、実際にはTabでインデントしてます(それが正しいのかどうかはわからないけど)
その上で、以下のような記述を行っていきます。
- 変数 = self.addHost ( ‘ホスト名称‘ , ‘ip=IPアドレス‘ , ‘mac=macアドレス‘ )
- ホストの定義をする箇所です。
- ホスト名称は任意です。mininetの中でホストを指定する際の識別子になります。
- macアドレスは箱庭でしか稼働させないので、適当な内容で良さそうです。
- mininetの中で定義したIPアドレス/macアドレスを用いて通信することになります。
- 変数 = self.addSwitch ( ‘スイッチ名称‘ , ‘ip=IPアドレス‘ , ‘dpid=任意のID‘ )
- スイッチの定義をする箇所です。
- スイッチ名称は任意です。mininetの中でスイッチを指定する際の識別子になります。
- mininetの中で定義したIPアドレス/IDを用いて通信することになります。
- self.addLink ( 変数 , 変数 )
- スイッチやホスト間のリンクを定義します。
- 変数は先述した「self.addHost」や「self.addSwitch」で定義した変数を使用します。
動かしてみる
早速動かしてみます。まずはryu側を起動します。
[root@OpenFlow ~]# cdryu-manager ryu/app/simple_switch.py loading app ryu/app/simple_switch.py loading app ryu.controller.ofp_handler instantiating app ryu.controller.ofp_handler of OFPHandler instantiating app ryu/app/simple_switch.py of SimpleSwitch
続いて、mininetを起動します。前回とは異なり、トポロジーのpyファイルを指定した上で起動を行います。
root@mininet:~/mininet/custom# mn --controller=remote,ip=192.168.100.138,port=66 33 --custom ~/mininet/custom/topo-4sw-10host.py --topo mytopo
するとこんな感じで出力され、前回とは異なりそれなりに複雑な構成が取れていることがわかります。
*** Creating network *** Adding controller *** Adding hosts: h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 *** Adding switches: s1 s2 s3 s4 *** Adding links: (h1, s1) (h2, s1) (h3, s2) (h4, s2) (h5, s2) (h6, s2) (h7, s3) (h8, s3) (h9, s4) (h10, s4) (s1, s2) (s2, s3) (s3, s4) *** Configuring hosts h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 *** Starting controller c0 *** Starting 4 switches s1 s2 s3 s4 ... *** Starting CLI: mininet>
疎通確認をしてみよう
pingallコマンドで疎通確認をしてみます。
mininet> pingall *** Ping: testing ping reachability h1 -> h2 h3 h4 h5 h6 h7 h8 h9 h10 h2 -> h1 h3 h4 h5 h6 h7 h8 h9 h10 h3 -> h1 h2 h4 h5 h6 h7 h8 h9 h10 h4 -> h1 h2 h3 h5 h6 h7 h8 h9 h10 h5 -> h1 h2 h3 h4 h6 h7 h8 h9 h10 h6 -> h1 h2 h3 h4 h5 h7 h8 h9 h10 h7 -> h1 h2 h3 h4 h5 h6 h8 h9 h10 h8 -> h1 h2 h3 h4 h5 h6 h7 h9 h10 h9 -> h1 h2 h3 h4 h5 h6 h7 h8 h10 h10 -> h1 h2 h3 h4 h5 h6 h7 h8 h9 *** Results: 0% dropped (90/90 received) mininet>
仮想的にHTTP通信なんかをしてみよう
以下のようにすることで、TinyなWebサーバを指定ホストに構成することが可能みたいです。
mininet> h1 python -m SimpleHTTPServer 80&
これでHost1に対してHTTPサーバがたちました。
Host10からダウンロードを試みてみます。
mininet> h10 wget h1 -O h1 --2017-11-07 23:07:52-- http://192.168.10.1/ 192.168.10.1:80 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 418 `192.168.10.1' に保存中 192.168.10.1 0%[ ] 0 --.-KB/s 192.168.10.1 100%[===================>] 418 --.-KB/s in 0s 2017-11-07 23:07:52 (65.2 MB/s) - `192.168.10.1' へ保存完了 [418/418]
ダウンロードできたようです。ファイルとして存在するか、確認してみます。
mininet> h10 ls |grep 192.168.10.1 192.168.10.1 mininet> mininet> h10 cat 192.168.10.1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html> <title>Directory listing for /</title> <body> <h2>Directory listing for /</h2> <hr> <ul> <li><a href="192.168.10.1">192.168.10.1</a> <li><a href="README">README</a> <li><a href="topo-2sw-2host.py">topo-2sw-2host.py</a> <li><a href="topo-3sw-8host.py">topo-3sw-8host.py</a> <li><a href="topo-4sw-10host.py">topo-4sw-10host.py</a> </ul> <hr> </body> </html> mininet>
おお、ダウンロードできてますなぁ。
ホストのプロセスリストを見てみる
Host1のプロセスリストを見てみましょう。以下の24079プロセスがTiny HTTP Serverプロセスですね。
mininet> h1 ps -a Serving HTTP on 0.0.0.0 port 80 ... 192.168.10.10 - - [07/Nov/2017 23:07:52] "GET / HTTP/1.1" 200 - PID TTY TIME CMD 22065 pts/8 00:00:00 sudo 22066 pts/8 00:00:00 su 22067 pts/8 00:00:00 bash 22216 pts/9 00:00:00 sudo 22217 pts/9 00:00:00 su 22218 pts/9 00:00:00 bash 23704 pts/8 00:00:00 mn 24079 pts/11 00:00:00 python 24090 pts/11 00:00:00 ps mininet>
構成情報をダンプする
dumpコマンドを使用します。こんなふうに出ます。
mininet> dump <Host h1: h1-eth0:192.168.10.1 pid=23714> <Host h2: h2-eth0:192.168.10.2 pid=23717> <Host h3: h3-eth0:192.168.10.3 pid=23720> <Host h4: h4-eth0:192.168.10.4 pid=23723> <Host h5: h5-eth0:192.168.10.5 pid=23726> <Host h6: h6-eth0:192.168.10.6 pid=23729> <Host h7: h7-eth0:192.168.10.7 pid=23732> <Host h8: h8-eth0:192.168.10.8 pid=23735> <Host h9: h9-eth0:192.168.10.9 pid=23738> <Host h10: h10-eth0:192.168.10.10 pid=23741> <OVSSwitch s1: lo:127.0.0.1,s1-eth1:None,s1-eth2:None,s1-eth3:None pid=23747> <OVSSwitch s2: lo:127.0.0.1,s2-eth1:None,s2-eth2:None,s2-eth3:None,s2-eth4:None,s2-eth5:None,s2-eth6:None pid=23750> <OVSSwitch s3: lo:127.0.0.1,s3-eth1:None,s3-eth2:None,s3-eth3:None,s3-eth4:None pid=23753> <OVSSwitch s4: lo:127.0.0.1,s4-eth1:None,s4-eth2:None,s4-eth3:None pid=23756> <RemoteController{'ip': '192.168.100.138', 'port': 6633} c0: 192.168.100.138:6633 pid=23708> mininet>
リンク状態の確認
linksコマンドを使用します。どれがどの機器の何のポートに接続されているかが確認できます。
mininet> links h1-eth0<->s1-eth2 (OK OK) h2-eth0<->s1-eth3 (OK OK) h3-eth0<->s2-eth3 (OK OK) h4-eth0<->s2-eth4 (OK OK) h5-eth0<->s2-eth5 (OK OK) h6-eth0<->s2-eth6 (OK OK) h7-eth0<->s3-eth3 (OK OK) h8-eth0<->s3-eth4 (OK OK) h9-eth0<->s4-eth2 (OK OK) h10-eth0<->s4-eth3 (OK OK) s1-eth1<->s2-eth1 (OK OK) s2-eth2<->s3-eth1 (OK OK) s3-eth2<->s4-eth1 (OK OK) mininet>
インタフェースリストの出力
intfsコマンドを使用します。どうやらスイッチに関しては、1ポートは管理用ポートとして、あとは接続されている分だけポートが準備されるようですね。
mininet> intfs h1: h1-eth0 h2: h2-eth0 h3: h3-eth0 h4: h4-eth0 h5: h5-eth0 h6: h6-eth0 h7: h7-eth0 h8: h8-eth0 h9: h9-eth0 h10: h10-eth0 s1: lo,s1-eth1,s1-eth2,s1-eth3 s2: lo,s2-eth1,s2-eth2,s2-eth3,s2-eth4,s2-eth5,s2-eth6 s3: lo,s3-eth1,s3-eth2,s3-eth3,s3-eth4 s4: lo,s4-eth1,s4-eth2,s4-eth3 c0: mininet>
フロー状態のダンプ
dpctl dump-flows コマンドを使用します。各スイッチのフロー状態がダンプされるみたいです。
mininet> dpctl dump-flows *** s1 ------------------------------------------------------------------------ NXST_FLOW reply (xid=0x4): cookie=0x0, duration=305.981s, table=0, n_packets=3, n_bytes=238, idle_age=300, in_port=3,dl_dst=00:00:00:00:00:01 actions=output:2 cookie=0x0, duration=305.979s, table=0, n_packets=2, n_bytes=140, idle_age=300, in_port=2,dl_dst=00:00:00:00:00:02 actions=output:3 cookie=0x0, duration=305.969s, table=0, n_packets=275778, n_bytes=18213819, idle_age=156, in_port=1,dl_dst=00:00:00:00:00:01 actions=output:2 cookie=0x0, duration=305.967s, table=0, n_packets=2, n_bytes=140, idle_age=300, in_port=2,dl_dst=00:00:00:00:00:03 actions=output:1 cookie=0x0, duration=305.955s, table=0, n_packets=2, n_bytes=140, idle_age=300, in_port=2,dl_dst=00:00:00:00:00:04 actions=output:1 cookie=0x0, duration=305.944s, table=0, n_packets=2, n_bytes=140, idle_age=300, in_port=2,dl_dst=00:00:00:00:00:05 actions=output:1 cookie=0x0, duration=305.933s, table=0, n_packets=2, n_bytes=140, idle_age=300, in_port=2,dl_dst=00:00:00:00:00:06 actions=output:1 : : *** s2 ------------------------------------------------------------------------ NXST_FLOW reply (xid=0x4): cookie=0x0, duration=305.974s, table=0, n_packets=3, n_bytes=238, idle_age=300, in_port=3,dl_dst=00:00:00:00:00:01 actions=output:1 cookie=0x0, duration=305.969s, table=0, n_packets=5, n_bytes=378, idle_age=300, in_port=1,dl_dst=00:00:00:00:00:03 actions=output:3 cookie=0x0, duration=305.961s, table=0, n_packets=3, n_bytes=238, idle_age=300, in_port=4,dl_dst=00:00:00:00:00:01 actions=output:1 : :
pingall詳細結果出力
pingallfull コマンドを使用することで、単純な到達性だけではなく、応答時間を測ることができます。
mininet> pingallfull *** Ping: testing ping reachability h1 -> h2 h3 h4 h5 h6 h7 h8 h9 h10 h2 -> h1 h3 h4 h5 h6 h7 h8 h9 h10 h3 -> h1 h2 h4 h5 h6 h7 h8 h9 h10 h4 -> h1 h2 h3 h5 h6 h7 h8 h9 h10 h5 -> h1 h2 h3 h4 h6 h7 h8 h9 h10 h6 -> h1 h2 h3 h4 h5 h7 h8 h9 h10 h7 -> h1 h2 h3 h4 h5 h6 h8 h9 h10 h8 -> h1 h2 h3 h4 h5 h6 h7 h9 h10 h9 -> h1 h2 h3 h4 h5 h6 h7 h8 h10 h10 -> h1 h2 h3 h4 h5 h6 h7 h8 h9 *** Results: h1->h2: 1/1, rtt min/avg/max/mdev 0.401/0.401/0.401/0.000 ms h1->h3: 1/1, rtt min/avg/max/mdev 0.440/0.440/0.440/0.000 ms h1->h4: 1/1, rtt min/avg/max/mdev 0.339/0.339/0.339/0.000 ms h1->h5: 1/1, rtt min/avg/max/mdev 0.313/0.313/0.313/0.000 ms h1->h6: 1/1, rtt min/avg/max/mdev 0.254/0.254/0.254/0.000 ms h1->h7: 1/1, rtt min/avg/max/mdev 0.629/0.629/0.629/0.000 ms h1->h8: 1/1, rtt min/avg/max/mdev 0.274/0.274/0.274/0.000 ms h1->h9: 1/1, rtt min/avg/max/mdev 0.472/0.472/0.472/0.000 ms h1->h10: 1/1, rtt min/avg/max/mdev 0.347/0.347/0.347/0.000 ms h2->h1: 1/1, rtt min/avg/max/mdev 0.027/0.027/0.027/0.000 ms : : h10->h6: 1/1, rtt min/avg/max/mdev 0.027/0.027/0.027/0.000 ms h10->h7: 1/1, rtt min/avg/max/mdev 0.025/0.025/0.025/0.000 ms h10->h8: 1/1, rtt min/avg/max/mdev 0.029/0.029/0.029/0.000 ms h10->h9: 1/1, rtt min/avg/max/mdev 0.024/0.024/0.024/0.000 ms mininet>
性能測定(TCP)
iperf 通信元 通信先 で実行します。TCP帯域の測定ができます。
mininet> iperf h1 h10 *** Iperf: testing TCP bandwidth between h1 and h10 *** Results: ['22.1 Gbits/sec', '22.1 Gbits/sec'] mininet>
性能測定(UDP)
iperfudp 帯域 通信元 通信先 で実行します。UDP帯域の上限を設定できるみたいです。その中でどれほどの帯域を使っているかが計測できるようです。
mininet> iperfudp 1000M h2 h10 *** Iperf: testing UDP bandwidth between h2 and h10 *** Results: ['1000M', '801 Mbits/sec', '801 Mbits/sec'] mininet>
後片付け
プロンプトで exit コマンドを実行することですべてのスイッチ・ホストを畳み、プログラムを終了します。
mininet> exit *** Stopping 1 controllers c0 *** Stopping 13 links ............. *** Stopping 4 switches s1 s2 s3 s4 *** Stopping 10 hosts h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 *** Done completed in 362.308 seconds root@mininet:~/mininet/custom#
コントローラ側でもこんな出力が出ます。
port deleted 2 port deleted 3 port deleted 3 port deleted 4 port deleted 3 port deleted 6 port deleted 5 port deleted 4 port deleted 1 port deleted 1 port deleted 3 port deleted 2 port deleted 1 port deleted 2 port deleted 1 port deleted 2
コントローラ側では、ryu-managerはフォアグラウンドで動いているので、Ctrl+Cで終了します。
まとめ
mininetは使ってみると比較的簡単にネットワークトポロジーを組んで遊べるので、こりゃぁいいなぁ~って感じです。ただ、やっぱりPythonと言う言語をちゃんと理解してないのと、OpenFlowコントローラが各スイッチに異なる役割を与えられるのか、実際の運用としてどう管理するのかはよく分かってません。
まだまだ理解が足りてないなと感じるものの、結構こりゃァ面白いなぁとも感じたりしていて、もう少し触ってみようそうしようってな気分です。
<追記:20171108>
Mininet Python API Referenceなるものを見つけましたヽ(=´▽`=)ノ。これ読んでもう少し理解を深めようそうしよう。
Comments are closed