简介:一套基于SDN架构的负载均衡Python实现源码,面向计算机网络、人工智能方向的学生与开发者,用于学习和实现SDN控制层与数据转发层分离下的流量动态调度策略。压缩包共31个文件,大小约1004KB,包含Python源代码、Shell配置脚本、网络拓扑文件、PNG流程截图、README说明文档及备份文件等,目录结构清晰,便于按模块对照学习。已有78人学习浏览该项目。源码经测试运行正常,文档不仅阐述SDN负载均衡的工作原理、系统架构设计,还重点介绍了如何通过控制器动态调整网络流量分配,以及关键实现步骤与适用场景;配套的拓扑文件和演示图片可辅助搭建实验环境,直观验证负载均衡算法效果。另提供额外工具与示例配置,方便二次开发和项目演示。既适合作为课程设计、毕业设计或项目初期演示参考,也适合希望结合代码深入理解SDN与网络优化的读者。
1. 基于SDN的负载均衡Python项目到底在做什么:一个能拿高分也能落地的选题
课程设计或毕业设计里出现“基于SDN的负载均衡Python源码文档流程演示”这个标题,通常意味着你要交付的是一整套可运行的工程,而不只是一段算法。实际场景往往是这样的:一台OVS交换机,三个后端服务器,一个客户端,流量进来后希望自动分流,谁空闲谁多接,谁过载谁少接。传统做法是配置文件里写死轮询或IP哈希,改了得重启服务;换成SDN以后,控制平面用一个Python控制器动态算,数据平面的流表项随时可换,整个转发逻辑就活起来了。这套方案能解决两个问题:一是让你在没有物理交换机的条件下,用Mininet和Ryu把实验跑通;二是让“负载均衡策略”这件事从黑匣子变成可观测、可改、可答辩的代码。适合正在做课程设计、毕业设计,或者想快速验证SDN想法的一线开发者。先把下面的选型和流程看完,再动手,你会少踩至少一半的坑。
2. 理论先立住:控制器、OpenFlow 1.3流表与三类调度算法的取舍
2.1 控制器选型:Ryu为什么是较稳的第一选择
做SDN负载均衡,代码主体是控制器应用,控制器本身就像一个操作系统。市面上常见的开源控制器有Ryu、POX、Floodlight、ONOS。POX维护周期长,完整支持OpenFlow 1.0为主,用来做老实验可以,但你想用set_field改IP这类动作时,会发现支持度跟不上。Floodlight是Java生态,模块多、依赖重,调试一个负载均衡应用要比调业务代码花更多时间。ONOS确实生产级,但Java构建加ONOS原生应用框架,学习曲线非常陡,拿它做课程级别的演示,大部分时间耗在环境准备上。
Ryu是Python写的,和Mininet、和你这个标题里“Python源码”的诉求天然匹配。它支持OpenFlow 1.0到1.5,最常用的1.3写起来顺手,事件驱动模型也容易理解:交换机上送一个packet-in,控制器里对应一个回调函数。很多开源课程设计里的SDN负载均衡样例,基本都跑在Ryu上,这让你找参考代码、对照排错都更容易。选择Ryu不是因为它性能最好,而是因为在这个项目规模下,它的投入产出比最合适。
下表是四个控制器的快速对比。
| 控制器 | 语言 | OpenFlow支持 | 学习成本 | 对这个项目的建议 |
|---|---|---|---|---|
| Ryu | Python | 1.0到1.5 | 低 | 主选,和Python源码整体风格一致 |
| POX | Python | 1.0为主 | 低 | 旧项目里还能见到,新开发不推荐 |
| Floodlight | Java | 1.0/1.3 | 中 | 团队强制用Java时再考虑 |
| ONOS | Java | 1.3以上 | 高 | 多控制器集群或生产部署再说 |
需要注意,控制器选型还决定了你后面的网络仿真工具怎么接。Ryu和Mininet同属Python生态,调试时可以直接看同一个逻辑错误堆栈,这点在日常开发里比性能参数更重要。
2.2 三类负载均衡策略:轮询、最小连接、带宽反馈
负载均衡算法才是控制器应用的核心。第一类是轮询,把新请求轮流分配给几台服务器,代码最简单,但完全不看后端实际状态。比如三台服务器里有一台因为磁盘IO卡住,处理一个请求要三秒,轮询还是会继续把新连接塞过去。第二类是最小连接,控制器记录每台后端当前活跃连接数,新请求优先发给连接数最少的服务器。这个策略在课程项目里最容易讲清楚,也最容易演示,因为你能在日志里直观看到计数变化。第三类是带宽反馈,控制器周期性读取交换机端口统计,计算每秒实际流量,再换算成服务器权重。
我用这张表帮你快速决定主线方案。
| 策略 | 核心逻辑 | 实现难度 | 讲解时的侧重点 |
|---|---|---|---|
| 轮询 | 取模分配,无状态 | 低 | 展示SDN流表下发机制 |
| 最小连接 | 全连接计数,新连接去最少者 | 中 | 展示动态决策和状态维护 |
| 带宽反馈 | 周期读端口统计再计算权值 | 高 | 展示控制器的全局视野 |
我一般建议主线做最小连接,轮询留作对照组,带宽反馈作为加分扩展。答辩时先讲轮询的问题,再展示最小连接如何弥补,最后说一句“如果后端性能差异大,还可以扩展成基于端口流量的加权调度”,整个项目的技术深度就有了。另有一条支线是“等开销负载均衡”,也就是常说的ECMP,它针对的不是服务器而是多条链路,控制器把不同五元组哈希到不同出口。这个更适合作为扩展章节写进文档,而不是放到主流程里。
2.3 OpenFlow 1.3流表项里跟负载均衡相关的字段
SDN负载均衡最终落在流表项上。一条流表项由优先级、匹配域、指令、超时时间、cookie等构成。匹配域决定什么流量命中这条规则,指令决定命中后怎么处理。负载均衡里最高频的动作是set_field修改IP地址,再用output把包送向指定物理端口。另一个高频动作是OFPPacketOut,用于把packet-in中的报文原路送出去,比如第四章要写的ARP代答。
要理解这类项目,必须先接受一个观点:控制器给交换机下发的不是简单转发规则,而是一条带优先级的决策路径。table-miss默认优先级为0,负责兜底,凡是没匹配到精确规则的报文都上送控制器。VIP相关的通配规则优先级设置在10到20之间,五元组精确匹配规则优先级要给到40以上。精确匹配放在前面,通用匹配放后面,控制器才能保证新连接先被计算,旧连接直接被数据平面转发。
流表里有两个参数经常被讨论:idle_timeout和hard_timeout。前者表示流表项空闲多久后删除,后者表示无论有没有流量,到时间强制删除。负载均衡里的TCP长连接,建议只用idle_timeout,把hard_timeout设为0,否则你会看到连接被莫名其妙切断。后面第5章会再次提到这个坑,这里先把概念立住。
2.4 SDN负载均衡的边界:它跟传统负载均衡差在哪
写文档和答辩时,这个区别是必答点。传统负载均衡器是串在链路上的专用设备,所有流量都经过它,它自己也需要高可用方案;而SDN负载均衡把决策逻辑放在控制器里,转发动作分散在每台OpenFlow交换机上。你可以在任意交换机上改写目的IP,不存在“网关单点转发”的概念。代价是控制器必须可靠,控制器挂了,新连接没人决策,交换机里的旧流表还能撑一阵,但最终会逐渐老化。所以课程项目里常说“SDN把负载均衡变成应用”,这也是你文档里能展示价值的地方。
3. 可复现的演示环境:Mininet拓扑代码、Ryu骨架和启动顺序
3.1 先写目录结构:源码、文档、流程演示三件套
标题既然叫“源码文档流程演示高分项目”,你交付时最好把工程分成三个可见的部分。常见做法是建一个顶层目录,里面分别放控制器源码、拓扑脚本、文档和演示脚本。目录结构可以是下面这样。
sdn-load-balance/ ├── app/ │ └── lb_controller.py ├── topo/ │ └── lb_topo.py ├── docs/ │ └── report.md └── scripts/ └── bench.sh源码放app和topo,文档放docs,流程演示脚本放scripts。这个结构不是硬性要求,但它能让你在答辩时很自然地讲“这是控制面、这是数据面、这是验证脚本”。很多人的项目里所有代码挤在单个文件里,功能能跑,但“高分”差在工程组织上。既然标题带了文档和流程,你至少要把README写清:怎么装依赖、怎么启动控制器、怎么启动拓扑、怎么验证负载均衡效果。第四章代码写完以后,再回来看这一步你会觉得理所当然。
3.2 先写拓扑:一台OVS加三台服务器加一台客户端
Mininet是这个项目最顺手的实验床。我建议自写Python脚本而不是直接用mn --topo,因为自定义主机名、IP和链路参数会更直观。
#!/usr/bin/env python3 # topo/lb_topo.py from mininet.topo import Topo from mininet.net import Mininet from mininet.node import RemoteController from mininet.cli import CLI from mininet.log import setLogLevel class LbTopo(Topo): """1台OVS交换机 + 3台后端服务器 + 1台客户端""" def build(self): s1 = self.addSwitch('s1', protocols='OpenFlow13') srv1 = self.addHost('srv1', ip='10.0.0.1/24') srv2 = self.addHost('srv2', ip='10.0.0.2/24') srv3 = self.addHost('srv3', ip='10.0.0.3/24') client = self.addHost('client', ip='10.0.0.10/24') self.addLink(client, s1, bw=10, delay='0.5ms') self.addLink(srv1, s1, bw=10, delay='0.5ms') self.addLink(srv2, s1, bw=10, delay='0.5ms') self.addLink(srv3, s1, bw=10, delay='0.5ms') def main(): setLogLevel('info') net = Mininet(topo=LbTopo(), controller=None) net.addController('c0', RemoteController, ip='127.0.0.1', port=6653) net.start() CLI(net) net.stop() if __name__ == '__main__': main()这个脚本里要注意三个参数。第一个是protocols='OpenFlow13',如果不写,Mininet里的OVS可能用OpenFlow10与Ryu握手,而OpenFlow10对set_field这类动作支持不完整,后面你会看到奇怪的丢包。第二个是bw=10,把端口限速成10Mbps,演示时才能看出流量被切分到不同端口的效果。第三个是delay='0.5ms',加一点链路时延,避免所有流量到达时间过于集中,干扰你观察调度顺序。IP网段用/24就够了,VIP单独预留一个,比如10.0.0.100,不要跟任何主机网卡绑定。
这个VIP是整个项目的逻辑入口。客户端访问的是10.0.0.100,但这个地址不归任何一台实体主机所有,而是由控制器通过ARP代答和流表转发来“虚拟”出来。答辩时如果能讲清楚“VIP不绑定物理网卡”,说明你真的理解了SDN解耦的思路。
3.3 控制器端最小骨架:table-miss和packet-in怎么配合
控制器端先做骨架,再填负载均衡逻辑。第一步是收到交换机的SwitchFeatures事件后,下发一条优先级为0的table-miss规则,让所有未知报文都上送控制器。
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER, set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet, ethernet class LbController(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.mac_to_port = {} @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): dp = ev.msg.datapath ofp = dp.ofproto parser = dp.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)] inst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(dp, priority=0, match=match, instructions=inst) dp.send_msg(mod) self.logger.info("table-miss installed on %s", dp.id)这段代码里,priority=0是底线,保证其他任何匹配规则都能压过它。OFPCML_NO_BUFFER的意思是交换机不缓存报文数据,完整上送给控制器。这样控制器能拿到ARP或TCP的完整内容。如果没有这条table-miss,OVS收到未知报文时会按自己的默认行为处理,客户端发出的ARP请求可能直接被丢弃,整个网络立即不通。
骨架阶段不需要写转发逻辑,看到控制器日志输出“table-miss installed”就说明连接建立成功。后面第四章会在packet-in的回调里补上真实的调度逻辑。
3.4 启动顺序:先Ryu再Mininet,监听端口一定要对齐
实际排错时,启动顺序和端口对齐是最大门槛。我习惯先开控制器,再开Mininet。
ryu-manager --ofp-tcp-listen-port 6653 app/lb_controller.py --observe-links--ofp-tcp-listen-port 6653非常容易漏。Ryu的老版本默认监听6633,而很多教程和Mininet默认控制器端口写的是6653。两者不一致时,OVS会反复重试建连,日志里出现“connection refused”,拓扑虽然启动了,但交换机永远没上线。建议固定写成6653,拓扑脚本里也写6653,全程只用这个端口。
然后另开终端启动拓扑:
sudo python3 topo/lb_topo.py顺序不能反。先启动Mininet的话,OVS会在Ryu起来之前反复尝试连接空端口,虽然之后Ryu启动能连上,但中间积压的ARP等报文已经丢了。演示时你会看到启动后前几秒ping不通,过一会又恢复,故事就讲得不干净。遵循“先控制器,后拓扑”的顺序,状态最干净。
4. 负载均衡核心逻辑:ARP代答、双向流表重写与连接计数
4.1 先说清VIP转发的完整路径
整个数据面流程是这样。客户端发出目的IP为VIP的TCP SYN报文,交换机table-miss后把它上送给控制器。控制器发现这是一个新连接,就在服务器池里挑一台当前连接数最少的,然后下发两条流表:一条把目的IP从VIP改写成真实服务器IP,输出到对应端口;另一条把回程包的源IP从真实服务器IP改回VIP,再输出到客户端所在端口。后续同一TCP连接的报文完全走数据平面的流表,不再经过控制器。
这个设计的优秀之处在于,控制器只处理新连接建立这个事件,而不是转发每一个包。很多第一次写SDN负载均衡的人把逻辑写反了,让控制器处理所有的数据包,结果控制通道成为瓶颈,流量一大就开始丢包。记住一句话:控制器负责决策,交换机负责转发。这条原则在答辩时也可以直接讲。
4.2 ARP代答:控制器必须回应VIP的ARP请求
客户端访问VIP之前,先要做ARP解析。问题在于VIP没有绑定任何物理网卡,交换机上查不到这个IP对应的MAC。因此控制器必须在packet-in处理里先拦截ARP请求,代替VIP回一个ARP应答。
def _handle_arp(self, dp, in_port, pkt, eth): arp_pkt = pkt.get_protocol(arp.arp) if arp_pkt is None: return False if arp_pkt.opcode == arp.ARP_REQUEST and arp_pkt.dst_ip == self.VIP: parser = dp.ofproto_parser ofp = dp.ofproto src_mac = self.VIP_MAC dst_mac = eth.src e = ethernet.ethernet(dst=dst_mac, src=src_mac, ethertype=0x0806) a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=arp.ARP_REPLY, src_mac=src_mac, src_ip=self.VIP, dst_mac=dst_mac, dst_ip=arp_pkt.src_ip) p = packet.Packet() p.add_protocol(e) p.add_protocol(a) p.serialize() actions = [parser.OFPActionOutput(in_port)] out = parser.OFPPacketOut(datapath=dp, buffer_id=ofp.OFP_NO_BUFFER, in_port=in_port, actions=actions, data=p.data) dp.send_msg(out) return True return False这段代码里有两个细节容易错。第一是ARP应答的目的MAC要填请求方的源MAC,目的IP要填请求方的源IP,不能简单广播。第二是手动构造的报文必须调用p.serialize(),把协议对象转成字节流,否则发出去的包是空的。OFPPacketOut里的buffer_id写成OFP_NO_BUFFER,表示控制器直接携带完整数据发出,因为这是控制器自己构造出的报文,OVS里没有缓存副本。
如果不做ARP代答,客户端的ARP请求会一直被table-miss上送控制器,控制器又只处理IP层,结果就是客户端永远解析不到VIP。排错时看到“Destination host unreachable”,第一反应就应该是检查ARP分支是否在IPv4分支之前执行。
4.3 最小连接计数与双向流表重写
连接计数是状态机核心。你要区分新连接和旧连接,最简单的标志是TCP SYN标志位。Ryu里读取TCP协议对象后,用tcp_pkt.flags & 0x02判断SYN位置是否置1。SYN出现时,从服务器池中选出当前连接数最少的一台,再写入“客户端到服务器”方向的流表。
def _select_server(self): idx = 0 for i in range(1, len(self.servers)): if self.conn_count[i] < self.conn_count[idx]: idx = i return idx def _add_client_to_server_flow(self, dp, server, client_ip, in_port): parser = dp.ofproto_parser ofp = dp.ofproto match = parser.OFPMatch(eth_type=0x0800, ipv4_src=client_ip, ipv4_dst=self.VIP) actions = [parser.OFPActionSetField(ipv4_dst=server['ip']), parser.OFPActionOutput(server['port'])] inst = [parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod( dp, priority=40, match=match, instructions=inst, idle_timeout=20, hard_timeout=0, cookie=server['id']) dp.send_msg(mod)这段代码有四个参数要记牢。priority=40保证它优先于其他通配规则。idle_timeout=20表示流表项20秒没有流量就自动清理。hard_timeout=0是不启用强制删除,连接只要一直在通信就不会被莫名切断。cookie可以标识这条流表属于哪台后端,后面统计每台服务器的实际流量时,可以按cookie分组。
这里还有一个非常关键的隐藏细节:匹配条件里带了ipv4_src=client_ip。如果不带源IP,所有去VIP的流量都会命中这一条流表,后续新连接也走同一个后端,负载均衡就失效了。一定要把源IP限定住,每条流表只对当前这个客户端IP生效。代价是流表数量会随着客户端数量增长,但在这个演示规模下完全可控。
和它对称的是回程规则。回程流表匹配源IP为服务器真实IP的包,动作为把源IP改回VIP,然后输出到客户端所在端口。这两个方向合起来才是完整NAT。如果只做了前向重写而忘记回程,客户端会收到来自真实服务器IP的响应,而它本来发往的是VIP,TCP层看到源地址不对,直接把包丢弃,表现为连接超时。这一点在答辩时常被追问,写进文档里能证明你考虑过双向状态。
4.4 周期减计数:光加不减会让一台后端永远休息
连接计数只加不减是另一个典型缺陷。一条TCP连接结束后,它的计数还挂在服务器上,时间一长,所有新连接都被分去计数更少的服务器,原先那台被“挤爆”,新的那台也慢慢过载。所以要用一个监控协程周期性地检查连接状态,把已经结束或超时的连接计数减掉。
def _monitor(self): while True: for i in range(len(self.servers)): self.logger.info("server %s conn=%s", self.servers[i]['name'], self.conn_count[i]) hub.sleep(10)这个协程每10秒打印一次三台服务器的连接分布。Ryu的事件循环是asyncio单线程模型,千万不要在packet_in_handler里直接sleep,那会阻塞所有后续事件。正确做法是用hub.spawn启动独立协程,让它在后台跑。打印日志还有一个额外好处:演示时直接看Ryu控制台就能说明调度过程,不需要再开额外工具。
真正的减计数逻辑可以把时间戳和连接绑定在一起。比如用字典记录每个服务器上次调度的时间,周期检查如果某个服务器超过一定时间没有新连接,就认为它的连接数该递减。因为Mininet里的TCP关闭消息不会主动通知控制器,你只能靠超时近似,不需要追求精确的引用计数。
5. 排查与注意:SDN负载均衡最容易翻车的5个坑
5.1 现象:控制器日志疯狂刷packet-in,业务却不通
控制台启动后就开始刷屏,全是来自OVS的packet-in事件,看起来控制器很忙,但pingall一条都不通。这个现象通常是你在packet-in处理里对每个报文都做了OFPPacketOut,却没有安装任何持久流表。等于每个帧都从交换机上送控制器,控制器处理完又原路送回,形成了集中式交换。拓扑里只要有一点环路,这种模式立刻让控制通道拥塞,丢包率飙升。
解决方法是先检查代码是否在第一个包上安装了流表。table-miss只应承担“新连接第一个包”的上送任务,之后的流量必须走数据平面。如果调试时需要临时用packet-in转发,一定要同时安装一条短idle_timeout的流表,让后续报文走快路径。
5.2 现象:ARP一直不通,控制器却收到了ARP包
控制器日志里能看到ARP请求,但客户端一直解析不到VIP的MAC。常见原因有两个。第一个是ARP代答分支写在IPv4分支之后,控制器先按IP处理,把ARP包当成未知流量丢掉。第二个是构造ARP回复时MAC地址填反,源填成了客户端MAC,交换机一看源MAC是自己,丢弃报文。
解决办法是把_handle_arp放在packet-in处理函数最前面,遇到ARP请求先return。检查构造回复时目的MAC是否填eth.src,再确认调用过p.serialize()。如果还是不通,用tcpdump在客户端网卡看是否有ARP回复。Mininet环境下一般不会涉及VLAN,但如果你改了OVS端口属性加了VLAN tag,ARP报文会带802.1Q头,控制器解包时也要对应处理,否则匹配不到端口。
5.3 现象:流表每过一段时间自己消失,负载分布跳变
流表下发后,业务刚开始通,过十几秒就断,重发一次又通。这通常是hard_timeout设置的问题。如果你把流表参数写成hard_timeout=10,就意味着这条流表无论有没有流量,10秒后一律被删除。TCP长连接因此每隔10秒断一次,表现就是“整个服务像电梯一样周期性停运”。
解决方法是把hard_timeout设为0,只保留idle_timeout。idle_timeout是空闲才回收,连接只要还在持续转发数据,流表就一直在。对于HTTP短连接场景,idle_timeout设5到10秒就够用,不需要强超时。调试时可以用ovs-ofctl dump-flows s1 -O OpenFlow13查看每条规则的timeout字段,确认参数真实生效。
5.4 现象:计数器看着正常,实际分配结果和计数对不上
连接计数模块已经打印出每台服务器的连接数,但新连接没有发给计数最少的服务器。这里常见原因是SYN重传也被算成了新连接。Mininet里如果链路delay设得比较大,TCP的SYN重传很常见,同一个连接可能连续发两三次SYN,计数被重复累加。减计数时又只减一次,分配决策自然偏移。
解决方法是维护一个去重集合,用四元组(源IP、源端口、目的IP、目的端口)判断这个连接是否已经见过。新元组才走_select_server并加计数,老元组直接跳过。放到分布式环境里,这个集合要换成共享存储,但在单控制器演示中,Python的set结构就够了。真正生产时还会用连接跟踪表,那已经是另一个量级的工程。
5.5 现象:Mininet里一切正常,换到eve-ng的vNet节点就连不上控制器
有人为了可视化,把仿真环境从Mininet换成eve-ng里的vNet或OVS节点。这时会碰到一个时间差问题:eve-ng节点启动很慢,Ryu控制器已经监听了端口,vNet还处于初始化状态,导致连接建立时序错位。现象是控制器日志没有任何报文,vNet却反复报连接失败。
解决办法是先在eve-ng里启动Ryu或控制器节点,再启动网络节点,必要时给vNet加2到3秒预启动延时。另一个容易忽略的是地址差异,eve-ng的vNet管理接口IP跟Mininet的本地回环不一样,控制器连接地址要写对管理网IP,而不是127.0.0.1。这个坑不常被提起,但它能解释为什么“明明Mininet好端端,一换环境就不行”。
6. 演示和答辩前必做的验证:把负载均衡变成看得见的证据
代码能跑只是第一步,演示要拿证据说话。我习惯在答辩前做两件事:一是让客户端循环建连,二是把控制器日志里的分配记录留成可查的文本。最简单的循环可以用下面这段脚本。
for i in {1..30}; do curl -s -m 2 http://10.0.0.100/ > /dev/null done前提是三台服务器主机上都起了HTTP服务。运行这个循环后,Ryu控制台会连续打出服务器连接分布,你能看到三台都被分配到请求。加-m 2是为了避免curl复用同一个连接,因为连接复用时后续请求不会触发新SYN,控制器反而不参与决策。演示时把这个细节讲出来,说明你理解TCP连接复用和负载均衡粒度之间的关系。
更硬核的验证是看OVS真实流表统计。
sudo ovs-ofctl dump-flows s1 -O OpenFlow13用grep cookie分组查看每台服务器对应的流表命中次数。看到三组cookie的n_packets都在增长,就说明流量确实被分到了不同端口。我习惯用watch -n 1把这个命令挂起来,流表项在屏幕上跳动,评委能直接看到效果。再把控制器日志和流表输出截到文档里,整个“源码文档流程演示”就形成完整闭环。
最后一条经验:不要把VIP直接绑在某台主机上。很多项目为了省事,把三台服务器中的一台IP当作VIP,结果是看似通了,实际上所有流量都去了那一台,负载均衡根本没发生。把VIP独立出来,用ARP代答加流表重写实现,才是SDN方案的正路。准备一份清晰的项目说明,写清楚VIP地址、代答流程、双向重写和验证步骤,这份文档本身就能拉开分数差距。希望帮到你。
本文还有配套的精品资源,点击获取