Contenu connexe Similaire à SDN時代の開発よもやま話 - OpenFlowとTrema (7) Plus de Yasuhito Takamiya (6) SDN時代の開発よもやま話 - OpenFlowとTrema2. FAQ
よくある疑問
12年12月9日日曜日
4. 世界で初めて、
OpenFlow/SDN
開発を扱った本
2013/1/8 発売
12年12月9日日曜日
6. SDN
とは
プログラミング分野の1つ
12年12月9日日曜日
7. SDN
プログラミング分野の1つ
とは
「GUIプログラミング」
とかと同じ!
12年12月9日日曜日
10. OpenFlow とは
SDNのいち実装方式
GtkとかCocoa
とかと同じ!
12年12月9日日曜日
11. SDN
つまり
OpenFlow
12年12月9日日曜日
12. SDN
つまり
OpenFlow
は純粋にソフトウェア開発の話。
12年12月9日日曜日
14. すでにバックボーンは
OpenFlowで作っちゃた
Urs Hölzle
http://itpro.nikkeibp.co.jp/article/NEWS/20120418/391401/
12年12月9日日曜日
16. Googleのチャレンジ
最先
• 平均 PUE = 1.13 端
• 年間アベイラビリティ = 99.984%
• 処理するデータ量/day = 数十ペタ
12年12月9日日曜日
17. Googleのチャレンジ
最先
• 平均 PUE = 1.13 端
• 年間アベイラビリティ = 99.984%
• 処理するデータ量/day = 数十ペタ
次の チャレンジは データセンター
をつなぐネッ トワークだ!
12年12月9日日曜日
18. WAN 回線のコスト
日米間(4.8Tbps、1万km)=320億円
12年12月9日日曜日
19. WAN 回線のコスト
日米間(4.8Tbps、1万km)=320億円
回線を 追加せずに、
既存のを有 効活用したい
12年12月9日日曜日
20. DC どれだけ流せる? DC
DC
12年12月9日日曜日
21. 従来のルーティング
• ルータは自律分散で動く
• 宛先に対して必ず最短路を選択
• 帯域の使用量は加味してくれない
12年12月9日日曜日
22. DC 集中 DC
ヒマ?
DC
12年12月9日日曜日
23. DC 理想 DC
DC
12年12月9日日曜日
24. 帯域は?
DC DC
10Gbps
6Gbps
DC
12年12月9日日曜日
25. DC DC
10Gbps
?
6Gbps
DC
12年12月9日日曜日
26. DC DC
どう分ける?
DC
12年12月9日日曜日
27. TEサーバ
DC DC
DC
12年12月9日日曜日
28. TEサーバ
DC
空き帯域
DC
の収集
DC
12年12月9日日曜日
29. TEサーバ
DC
空き帯域
DC
帯域の
の収集
割り当て
DC
12年12月9日日曜日
30. TEサーバ
DC DC
DC
12年12月9日日曜日
31. TEサーバ
DC DC
DC
12年12月9日日曜日
32. TEサーバ
DC
BGPルータ BGP処理部
コントローラ
OpenFlow
12年12月9日日曜日
スイッチ
33. TEサーバ
DC
BGPルータ BGP処理部
経路情報
コントローラ
OpenFlow
12年12月9日日曜日
スイッチ
34. TEサーバ
DC
BGPルータ BGP処理部
経路情報
コントローラ
トラフィック
情報
OpenFlow
12年12月9日日曜日
スイッチ
35. TEサーバ
DC
BGPルータ BGP処理部
経路情報
コントローラ
トラフィック
情報
OpenFlow
12年12月9日日曜日
スイッチ
36. TEサーバ
経路制御
DC
BGPルータ BGP処理部
経路情報
コントローラ
トラフィック
情報
OpenFlow
12年12月9日日曜日
スイッチ
37. TEサーバ
経路制御
DC
BGPルータ BGP処理部
経路情報
コントローラ
トラフィック
情報
OpenFlow
12年12月9日日曜日
スイッチ
39. TEサーバ
DC
BGPルータ BGP処理部
コントローラ
OpenFlow
12年12月9日日曜日
スイッチ
41. 2分で分かる
OpenFlow
12年12月9日日曜日
42. 従来のスイッチ
制御する パケットの
ソフトウェア 転送ルール
ファーム フロー
ウェア領域 テーブル
12年12月9日日曜日
43. ソフトウェア
コントローラ
OpenFlow
プロトコル
フロー
テーブル
ハードウェア
12年12月9日日曜日
44. コントローラ
フロー
テーブル
転送ルール
を参照
速い
12年12月9日日曜日
45. コントローラ
知らない
パケット
フロー
テーブル
転送ルール
を参照
速い
12年12月9日日曜日
46. コントローラ
フローテーブル
の更新
知らない
パケット
フロー
テーブル
転送ルール
を参照
速い
12年12月9日日曜日
47. コントローラ
フローテーブル
遅い の更新
知らない
パケット
フロー
テーブル
転送ルール
を参照
速い
12年12月9日日曜日
52. OpenFlowのうれしさ
• ソフトウェアによる高い自由度
• システム連携と自動化
• ソフトウェア開発手法をネットワーク
に適用できる
12年12月9日日曜日
53. コントローラ
好きな言語で書ける
OpenFlow Ruby, Python, C++,Java,...
プロトコル
フロー
テーブル
既存のプロトコルに
縛られない
(e.g.,VLANの上限)
12年12月9日日曜日
54. TEサーバ ミドル DB
コントローラ 自由に連携
フロー
テーブル
12年12月9日日曜日
55. データセンター
GFS
Paxos
Chubby
12年12月9日日曜日
56. データセンター
GFS
Paxos コントローラ
Chubby
フロー
テーブル
12年12月9日日曜日
57. データセンター
GFS
Paxos コントローラ
Chubby
フロー
テーブル
12年12月9日日曜日
60. ソフトウェア手法の応用
• ネットワークを段階的に反復構築
(アジャイル)
• ネットワークのテスト
(各種テスト、テストドリブン)
12年12月9日日曜日
61. ソフトウェア手法の応用
• ネットワークを段階的に反復構築
(アジャイル)
• ネットワークのテスト
(各種テスト、テストドリブン)
• バージョン管理や巻き戻し
(git などのバージョン管理ツール)
12年12月9日日曜日
64. スイッチ
コントローラ
パケットの宛先
MACアドレス
→出力ポート
転送
12年12月9日日曜日
66. ルータ
コントローラ
書き換え
転送
送信元・宛先
MACアドレスを
書き換え
12年12月9日日曜日
68. ロードバランサ
バックエンド
コントローラ サーバ
トラフィック
情報
流量
書き換え
転送
トラフィック量に
応じて転送先を調整
12年12月9日日曜日
69. 帯域を目一杯使う
DC DC
転送 転送
転送 転送
転送 転送
12年12月9日日曜日
70. レプリケーション
DC
分岐
DC
DC 分岐
DC
分岐
DC
12年12月9日日曜日
71. パッチパネル
コントローラ
ネットワーク
A
転送
ネットワーク
所属するネットワーク B
をソフトウェアで切り替え
12年12月9日日曜日
72. パッチパネル
コントローラ
ネットワーク
A
転送
ネットワーク
所属するネットワーク B
をソフトウェアで切り替え
12年12月9日日曜日
73. パッチパネル
コントローラ
ネットワーク
A
転送
ネットワーク
所属するネットワーク B
をソフトウェアで切り替え
12年12月9日日曜日
75. 主なOpenFlow
プログラミングフレームワーク
•Trema (Ruby)
• POX (Python)
• NOX (C++)
• Floodlight (Java)
12年12月9日日曜日
78. バージョン対応状況
Trema
POX
NOX
Floodlight
1.0 1.1 1.2 1.3
12年12月9日日曜日
79. バージョン対応状況
Trema
POX
NOX
Floodlight TremaEdge
1.0 1.1 1.2 1.3
12年12月9日日曜日
82. watcher数: 1位
Trema 123
Floodlight 91
POX 68
OpenFaucet 28
NOX 17
12/6/2012 調べ
12年12月9日日曜日
83. サンプルApp数: 1位
Trema 29
NOX 16
POX 5
01101101
Beacon 4
11010101
Floodlight 3 01111101
OpenFaucet 0 01010111
12年12月9日日曜日
85. Wakame-VDC
http://wakame.jp/document/15/
• コンパクトな Ruby 製 IaaS 基盤ソフト
(e.g., OpenStack、Eucalyptus)
• 九州電力、NII など採用事例たくさん
• Rails による GUI
• ネットワークとストレージの仮想化
12年12月9日日曜日
90. パッチパネル
コントローラ
ネットワーク
A
転送
ネットワーク
所属するネットワーク B
をソフトウェアで切り替え
12年12月9日日曜日
93. IP アドレスがかぶると?
ユーザ X ユーザ Y
192.168.0.1 192.168.0.2 192.168.0.1 192.168.0.2
VM VM VM VM
普通のスイッチ
→ユニキャストなら問題なし
12年12月9日日曜日
94. IP アドレスがかぶると?
ユーザ X ユーザ Y
はーい!
192.168.0.2
はーい!
192.168.0.1 192.168.0.1 192.168.0.2
VM VM VM VM
192.168.0.2 の
MACアドレスは? 普通のスイッチ
→ブロードキャストで問題
12年12月9日日曜日
95. エッジスイッチでブロック
ユーザ X ユーザ Y
192.168.0.1
はーい!
192.168.0.2 192.168.0.1 192.168.0.2
VM VM VM VM
192.168.0.2 の
MACアドレスは? エッジスイッチ
12年12月9日日曜日
96. エッジスイッチで書き換え
ホストA ホストB
ユーザ X ユーザ Y ユーザ X ユーザ Y
VM VM VM VM
宛先を
元に戻す
ホストBへ
12年12月9日日曜日
100. Tremaの特長
「今風」なフレームワーク
• Rubyによる簡潔な記述
12年12月9日日曜日
101. Tremaの特長
「今風」なフレームワーク
• Rubyによる簡潔な記述
• フルスタック: ノートPC一台で開発
12年12月9日日曜日
102. Tremaの特長
「今風」なフレームワーク
• Rubyによる簡潔な記述
• フルスタック: ノートPC一台で開発
• GitHub上でのオープンな開発 (GPL2)
12年12月9日日曜日
103. Tremaの哲学
一、短く書けて、
一、すぐに動くべし
と れ ま
取間先生
(C) Miramax Films 2004
12年12月9日日曜日
105. 短ければ短いほど、
• 早くサッと作れて、
• バグの発生率が小さく、
• 後々のメンテも楽だから
12年12月9日日曜日
106. Trema でハブ
class RepeaterHub < Controller
def packet_in datapath_id, message
send_flow_mod_add(
datapath_id,
:match => ExactMatch.from( message ),
:actions => SendOutPort.new( OFPP_FLOOD )
)
send_packet_out(
datapath_id,
:packet_in => message,
:actions => SendOutPort.new( OFPP_FLOOD )
)
end
end
12年12月9日日曜日
108. POX (Python)
from pox.core import core
import pox.openflow.libopenflow_01 as of
class RepeaterHub (object):
def __init__ (self, connection):
self.connection = connection
connection.addListeners(self)
def send_packet (self, buffer_id, raw_data, out_port, in_port):
msg = of.ofp_packet_out()
msg.in_port = in_port
if buffer_id != -1 and buffer_id is not None:
msg.buffer_id = buffer_id
else:
if raw_data is None:
return
msg.data = raw_data
action = of.ofp_action_output(port = out_port)
msg.actions.append(action)
self.connection.send(msg)
def act_like_hub (self, packet, packet_in):
self.send_packet(packet_in.buffer_id, packet_in.data,
of.OFPP_FLOOD, packet_in.in_port)
def _handle_PacketIn (self, event):
packet = event.parsed
if not packet.parsed:
return
packet_in = event.ofp # The actual ofp_packet_in message.
self.act_like_hub(packet, packet_in)
def launch ():
def start_switch (event):
RepeaterHub(event.connection)
core.openflow.addListenerByName("ConnectionUp", start_switch)
12年12月9日日曜日
110. NOX (C++)
#include <boost/bind.hpp>
#include <boost/shared_array.hpp>
#include "assert.hh"
#include "component.hh"
#include "flow.hh"
#include "packet-in.hh"
#include "vlog.hh"
#include "netinet++/ethernet.hh"
namespace {
using namespace vigil;
using namespace vigil::container;
Vlog_module lg("hub");
class Hub
: public Component
{
public:
Hub(const Context* c,
const json_object*)
: Component(c) { }
void configure(const Configuration*) {
}
Disposition handler(const Event& e)
{
const Packet_in_event& pi = assert_cast<const Packet_in_event&>(e);
uint32_t buffer_id = pi.buffer_id;
Flow flow(pi.in_port, *(pi.get_buffer()));
if (flow.dl_type == ethernet::LLDP){
return CONTINUE;
}
ofp_flow_mod* ofm;
size_t size = sizeof *ofm + sizeof(ofp_action_output);
boost::shared_array<char> raw_of(new char[size]);
ofm = (ofp_flow_mod*) raw_of.get();
ofm->header.version = OFP_VERSION;
ofm->header.type = OFPT_FLOW_MOD;
ofm->header.length = htons(size);
ofm->match.wildcards = htonl(0);
ofm->match.in_port = htons(flow.in_port);
ofm->match.dl_vlan = flow.dl_vlan;
ofm->match.dl_vlan_pcp = flow.dl_vlan_pcp;
memcpy(ofm->match.dl_src, flow.dl_src.octet, sizeof ofm->match.dl_src);
memcpy(ofm->match.dl_dst, flow.dl_dst.octet, sizeof ofm->match.dl_dst);
ofm->match.dl_type = flow.dl_type;
ofm->match.nw_src = flow.nw_src;
ofm->match.nw_dst = flow.nw_dst;
ofm->match.nw_proto = flow.nw_proto;
ofm->match.tp_src = flow.tp_src;
ofm->match.tp_dst = flow.tp_dst;
ofm->cookie = htonl(0);
ofm->command = htons(OFPFC_ADD);
ofm->buffer_id = htonl(buffer_id);
ofm->idle_timeout = htons(5);
ofm->hard_timeout = htons(5);
ofm->priority = htons(OFP_DEFAULT_PRIORITY);
ofm->flags = htons(0);
ofp_action_output& action = *((ofp_action_output*)ofm->actions);
memset(&action, 0, sizeof(ofp_action_output));
action.type = htons(OFPAT_OUTPUT);
action.len = htons(sizeof(ofp_action_output));
action.port = htons(OFPP_FLOOD);
action.max_len = htons(0);
send_openflow_command(pi.datapath_id, &ofm->header, true);
free(ofm);
if (buffer_id == UINT32_MAX) {
size_t data_len = pi.get_buffer()->size();
size_t total_len = pi.total_len;
if (total_len == data_len) {
send_openflow_packet(pi.datapath_id, *pi.get_buffer(),
OFPP_FLOOD, pi.in_port, true);
}
}
return CONTINUE;
}
void install()
{
register_handler<Packet_in_event>(boost::bind(&Hub::handler, this, _1));
}
};
REGISTER_COMPONENT(container::Simple_component_factory<Hub>, Hub);
}
12年12月9日日曜日
112. Floodlight (Java)
package net.floodlightcontroller.hub;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.util.U16;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Hub implements IFloodlightModule, IOFMessageListener {
protected static Logger log = LoggerFactory.getLogger(Hub.class);
protected IFloodlightProviderService floodlightProvider;
public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
this.floodlightProvider = floodlightProvider;
}
@Override
public String getName() {
return Hub.class.getPackage().getName();
}
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
OFPacketIn pi = (OFPacketIn) msg;
OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory()
.getMessage(OFType.PACKET_OUT);
po.setBufferId(pi.getBufferId())
.setInPort(pi.getInPort());
OFActionOutput action = new OFActionOutput()
.setPort((short) OFPort.OFPP_FLOOD.getValue());
po.setActions(Collections.singletonList((OFAction)action));
po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
if (pi.getBufferId() == 0xffffffff) {
byte[] packetData = pi.getPacketData();
po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH
+ po.getActionsLength() + packetData.length));
po.setPacketData(packetData);
} else {
po.setLength(U16.t(OFPacketOut.MINIMUM_LENGTH
+ po.getActionsLength()));
}
try {
sw.write(po, cntx);
} catch (IOException e) {
log.error("Failure writing PacketOut", e);
}
return Command.CONTINUE;
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
return false;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
return null;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() {
return null;
}
@Override
public Collection<Class<? extends IFloodlightService>>
getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
floodlightProvider =
context.getServiceImpl(IFloodlightProviderService.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
}
12年12月9日日曜日
113. コード行数
120 111
95
90
60
36
30
14
0
Trema (Ruby) POX (Python) NOX (C++) Floodlight (Java)
12年12月9日日曜日
114. フローテーブルの書き込み
send_flow_mod_add(
Trem
dpid,
:match => ExactMatch.from( message ), a
:buffer_id => message.buffer_id,
:actions => ActionOutput.new( message.in_port + 1 )
)
NOX
inst.install_datapath_flow(
dpid,
extract_flow(packet),
CACHE_TIMEOUT,
openflow.OFP_FLOW_PERMANENT,
[[openflow.OFPAT_OUTPUT, [0, prt[0]]]],
bufid,
openflow.OFP_DEFAULT_PRIORITY,
inport,
buf
)
12年12月9日日曜日
115. Tremaの哲学
一、短く書けて、
一、すぐに動くべし
と れ ま
取間先生
(C) Miramax Films 2004
12年12月9日日曜日
117. SDN開発の面倒さ
"OpenFlow は開発環境の構築が面倒だ"
(ネットワーク設定や VM 構築とか)
プログラマの声
12年12月9日日曜日
118. SDN開発の面倒さ
"OpenFlow は開発環境の構築が面倒だ"
(ネットワーク設定や VM 構築とか)
"OpenFlow スイッチ持ってないよ"
プログラマの声
12年12月9日日曜日
119. SDN開発の面倒さ
"OpenFlow は開発環境の構築が面倒だ"
(ネットワーク設定や VM 構築とか)
"OpenFlow スイッチ持ってないよ"
"そもそも OpenFlow の実験ができる
ネットワーク持ってないし..."
プログラマの声
12年12月9日日曜日
120. Tremaなら簡単!
trema run コントローラ
起動
作成
12年12月9日日曜日
121. Tremaなら簡単!
trema run コントローラ
起動
作成
12年12月9日日曜日
122. コントローラを書いてみよう
# simple-controller.rb
class SimpleController < Controller
end
コンパイルなしですぐに実行
% trema run simple-controller.rb
12年12月9日日曜日
123. スイッチの起動を捕捉
class SimpleController < Controller
def switch_connected dpid
puts “Hello #{ dpid.to_hex }!”
end
end
“スイッチ起動”イベントの
ハンドラを追加
12年12月9日日曜日
124. 仮想スイッチを接続
# network.conf
vswitch { dpid 0xabc }
vswitch { dpid 0xdef }
% trema run simple-controller.rb
-c network.conf
Hello 0xabc!
Hello 0xdef!
12年12月9日日曜日
126. パケットを送る
# network.conf
vswitch { dpid 0xabc }
vswitch { dpid 0xdef }
vhost “host1”
vhost “host2”
link “0xabc”, “host1”
link “0xdef”, “host2”
12年12月9日日曜日
127. パケットを送る
class SimpleController < Controller
def switch_connected dpid
puts “Hello #{ dpid.to_hex }!”
end
def packet_in dpid, message
puts “Unknown packet!”
end
end
12年12月9日日曜日
128. パケットを送る
% trema send_packets --source host1
--dest host2 --n_pkts 10 --pps 10
Unknown packet!
Unknown packet!
Unknown packet!
Unknown packet!
Unknown packet!
...
12年12月9日日曜日
129. SDN
プログラミング分野の1つ
とは
「GUIプログラミング」
とかと同じ!
12年12月9日日曜日
130. 仮想ネットワークDSL
開発マシン上に好きなトポロジを構築して
コントローラを実行できる
• テストパケットを送受信してコントローラを
デバッグできる
• 使える部品は仮想スイッチ、仮想ホスト、
仮想リンク
12年12月9日日曜日
131. スイッチ10台のフルメッシュ接続を
ベタ書き(約80行)
vswitch { dpid "0x1" }
vswitch { dpid "0x2" }
vswitch { dpid "0x3" }
vswitch { dpid "0x4" }
...
link "0x1", "0x2"
link "0x1", "0x3"
link "0x1", "0x4"
link "0x1", "0x5"
link "0x1", "0x6"
link "0x1", "0x7"
link "0x1", "0x8"
link "0x1", "0x9"
link "0x1", "0x10"
link "0x2", "0x3"
link "0x2", "0x4"
...
12年12月9日日曜日
132. 言語内 DSL
$nswitch = 10
1.upto( $nswitch ).each do | sw1 |
vswitch { dpid sw1.to_hex }
1.upto( $nswitch ).each do | sw2 |
if sw1 < sw2
link sw1.to_hex, sw2.to_hex
end
end
end
12年12月9日日曜日
137. SDNでTDD
• RSpecでネットワークを
ユニットテスト
12年12月9日日曜日
138. SDNでTDD
• RSpecでネットワークを
ユニットテスト
• ホスト間でのパケットの送受信数
12年12月9日日曜日
139. SDNでTDD
• RSpecでネットワークを
ユニットテスト
• ホスト間でのパケットの送受信数
• スイッチのフローテーブルの中身
12年12月9日日曜日
140. SDNでTDD
• RSpecでネットワークを
ユニットテスト
• ホスト間でのパケットの送受信数
• スイッチのフローテーブルの中身
• Etc.
12年12月9日日曜日
141. ハブのテストコード
describe RepeaterHub do
around do | example |
network { # ネットワークの定義
vswitch("switch") { datapath_id "0xabc" }
vhost("host1") { promisc "on" } 仮想NWで
vhost("host2") { promisc "on" }
link "switch", "host1" ハブを動かし、
link "switch", "host2"
}.run( RepeaterHub ) {
example.run
}
end
context "when host1 sends one packet to host2" do
describe "switch" do
before { send_packets "host1", "host2" }
subject { switch( "switch" ) }
フローテーブル
it { should have( 1 ).flows }
its( "flows.first.actions" ) { should == "FLOOD" }
の中身をテスト
end
...
12年12月9日日曜日
142. まとめ
• SDNはプログラミング
•やってるとこはやっている
(Google、Trema)
• Tremaで楽に作ろう
12年12月9日日曜日