初一之思

佛经里讲究因缘际会,农历初一前回家,妻子因工作仍需上班,我独坐书房,因不愿常对电脑,书架上找来似乎是当年于北京八大处所领的几本佛经,其中一本为《金刚经●心经》,其中《金刚经》为佛教经典,随手读来,竟然十分有趣,而先前为解自己心理问题,买得《图解正念》一书并读来十分受用,而“正念”概念,恰是来自于从佛教开辟的概念,两者前后忽应,莫不是让我开始了解佛经以求解脱的契机?

前些时常思量此生应如何度过,终得利他度已这样的想法,而如今以《金刚经》中所提布施是积累福报之途径,从这点来看,我给自己定的人生目标与佛经教化更为契合,实为一幸事。

因工作前往杭州,而暂居苏州,苏杭两地,寺院殊多,将会为我之修心带来不少便利,此为地利。

《金刚经》中的核心为四句偈“一切有为法,如梦幻泡影,如露亦如电,应作如是观”,大学时似乎听人讲过,印象深刻,但大多停留于词藻,如今读经而思,方知停留于词藻之肤浅,亦难对无住之意;未来出世入世,需好好思量。

记于猴年初一。

OpenStack网络方案近期状态分析

目前OpenStack网络方案存在有较多的选择,liberty发布已经有大半多时间,mikata版本也快要发布,有必要比较一下近期各种openstack网络方案的状态。

主流方案介绍

当前,比较主流的方案有如下几种:

  • ovs-agent
  • ofagent
  • ovn
  • dragonflow
  • midonet/opencontrail

除此之外,还有一些闭源的商业方案,如nsx、plumgrid,以及一些试验性的开源方案如networking-odl等,本次分析暂不考虑。

现有网络方案特性集及特点

  • ovs-agent
功能点支持情况备注
L2 isolation using overlayY支持vlan/vxlan/gre
arp responderY要求ovs 2.1 + l2pop使能
L3 DVRY要求每台计算节点都需要安装l3-agent,通过netns及linux协议栈实现
ovsdb nativeY独立功能,与openflow native无关,可单独开启,有一定的性能提升
openflow nativeYovs-agent将内置ryu,运行一个of app用来处理与本地ovs的交互,基本逻辑与原ovs-agent相似,通过driver机制实现不同方式的bridge操作,目前仍处于实验阶段
security groupY目前仍基于iptables,随着ovs conntrack功能的完善,可以演进至基于ovs conntrack实现
dhcpY仍采用dhcp agent
north-sourth流量Y仍采用l3agent进行集中式转发,即便在dvr模式下
  • ofagent

如同ofagent在openstack上的wiki所述:

OFAgent is a neutron core-plugin, implemented as ML2 mechanism driver. It aims to support pure OpenFlow1.3 switches.

ofagent关注的更多是指向向设备(如vswitch/pswitch)可移植性,因此基于纯openflow协议进行实现,而纵观业界,基本上没有基于纯openflow的openstack网络方案(目前仍成功商用的则是bigswitch的big fabric,但其采用了深度修改的openflow),因此这种方式可能是过于理想化的方案,可能并不一定能够容易落地。

从ovs的状态来看,ovs 2.5中为支持conntrack,引入了ct_state/ct这样的match/atction,这些nicira扩展落入openflow spec的时间点可能还比较长,而为了实现dv足够多的功能,ovs将会继续引入扩展,如果基于纯openflow实现的话,这些功能都将难以利用。

从wiki上的比较来看,ofagent也不支持dvr,实现上同样基于ryu,而部署模式上与ovs-agent相似,在compute/network节点都需部署。

  • ovn

ovn是由vmware所主导的新项目,其介绍如下所示:

OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. Services such as DHCP are also desirable features. Just like OVS, OVN’s design goal is to have a production-quality implementation that can operate at significant scale.

目前已经实现的功能集如下:

功能点支持情况备注
L2 isolation using overlayYhypervisor间仅支持geneve/stt,与l2gw之间可采用vxlan连接
arp responderY直接基于流表实现
L3 DVRY不依赖netns,基于ovs patch port及纯流表实现,对vrouter网关的icmp请求将由流表进行回复
ovsdb nativeYovsdb目前既做northbound db,又做southbound db,同时又做hypervisor上的本地db,但操作上均是通过c语言访问native接口实现的
openflow nativeY在hypervisor上需运行一个基于c语言实现的local controller,通过native openflow消息操作vswitchd
security groupN基于OVN ACL实现,目前已经提供部分代码,尚未实现
dhcpY暂时仍采用dhcp agent,后续将内置实现
north-sourth流量Y暂仍采用l3 agent,后续将内置实现,并预期支持L3HA

nn从功能点上来说,ovn还有大量的功能有待开发,并且很多功能一方面依赖于ovs,一方面依赖于kernel,尽管邮件列表上计划在mikita版本有试验性的支持,从当前现状来看,达到生产级质量水平的可能性较小。

从架构上来看,尽管ovn也认识到northdb、southdb目前都存在ovsdb-server上存在单点故障并且ovsdb尚不支持clustering,但目前仍关注于feature的完备性,故而还未投入过多精力处理。

  • dragonflow

Dragonflow implements Neutron using a lightweight embedded SDN Controller. Dragonflow is available in two configurations: Distributed and Centralized. Our project mission is to Implement advanced networking services in a manner that is efficient, elegant and resource-nimble

dragonflow由华为以色利团队开发,相比ofagent那种仅为提升性能而采用ryu实现openflow native语义的方式,dagonflow更为激进,采用了ryu app来实现l2转发及l3 dvr,并且从代码上来看,dhcp功能也已经做为ryu app实现了,并且dragonflow强调自己实现了pluggable db的支持,即通过引入外置第三方db来实现数据同步,与openstack rpc解耦,相比ovn采用ovsdb来说,db层面的可靠性及扩展性更好。

目前dragonflow的功能如下:

功能点支持情况备注
L2 isolation using overlayY支持vxlan/gre
arp responderY基于流表实现
L3 DVRY基于流表实现,无需netns
ovsdb nativeY通过ovsdb python idl访问ovsdb
openflow nativeY内置ryu,通过openflow协议处理packet-in及流表
security groupN计划采用ovs conntrack实现,但尚未开发
dhcpY采用运行在ryu里的dhcp app,实现分布式dhcp
xnorth-sourth流量Y仍采用l3agent进行集中式转发
  • midonet

midonet是midokura公司开源的openstack网络方案,核心实现是基于分布式数据库(zookeeper)实现neutron数据分发,并通过ovs kmod实现转发,但在用户态完全舍弃ovs,采用自己通过java+scala实现的本地控制器,其核心转发过程称为simulation,即通过本地控制器所获知到的虚拟网络拓朴,来判断一个报文最终的转发结果,并将结果添加到ovs kmod所维护的内核态流表中。

国外有部署云客户采用该公司的方案,国内据说乐视私有云采用了该方案。

目前midonet的功能如下:

功能点支持情况备注
L2 isolation using overlayY支持vxlan
arp responderY由本地控制器仿真实现
L3 DVRY由本地控制器仿真实现,将下发至内核态流表
security groupY由本地控制器仿真实现
dhcpY由本地控制器实现
north-sourth流量Y支持DNAT/SNAT,并实现多l3时的snat同步,并可以和外部通过bgp进行l3对接
load-balancerY由HA Proxy实现
  • opencontrail

opencontrail为juniper所开发的独立网络虚拟化方案,采用专用的内核模块实现virtual network,不依赖于ovs,并采用大量的私有或公有如bgp协议实现控制平面,与juniper的设备有较好的对接,国外部分公有云如tcpcloud等采用其方案。

从部署上来说该方案与现有方案差异较大,不做过多描述。

选型建议

从上述分析来看,除社区ovsagent外,midonet/opencontrail是较为完善的方案,但前者社区较小(尽管目前部分代码已经托管至openstack官方),且采用小众语言开发,深入定制有一定的难度;如果半年内要升级的话,ovsagent似乎是较为可行的选择,半年后可以关注ovn/dragonflow等相关项目的状态再做选择。

至于转发性能方案,各方案主要还是解决管理上的复杂度,性能上dvr算是一个较大的亮点,转发性能上可以通过后续引入对组网影响较小的vxlan offload nic来提升。

关于网卡VXLAN offload能力的误解

网络技术这个行业里,厂商通常会对一些技术做出高大上的包装,像网卡VXLAN offload这种技术,就是一个典型的例子,然而其底层技术,也许并不像想象中的那么神秘。

做为网络交换机的开发人员,近年来一直从事数据中心网络虚拟化的相关工作,最近读取一篇关于青云SDN 2.0的文章,里面提到了采用网卡VXLAN offload这种技术后overlay情形下的虚拟网络性能得到了较大规模的提升,考虑到后续我亦将加入云计算公司进行网络虚拟化相关的研发,自然也十分想了解这种技术的底层实现如何,以便从理论上分析其性能是否具有较大的价值。

分析之前,稍微VXLAN的封装格式进行简单的说明,本质上VXLAN类似于一种l2vpn技术,即将二层以太报文封装在udp报文里,从而跨越underlay L3网络,实现不同服务器或不同数据中心间的互联,其格式总结如下:

|ETH_HEADER|IP_HEADER|UDP_HEADER|VXLAN_HEADER|INNER_ETH_HEADER|INNER_IP_HEADER|…|

在采用VXLAN技术后,由于vm/docker的报文被封装于外层的UDP报文中予以传输,使得以往的TCP SEGMENT OPTIMIZATION、TCP CHECKSUM OFFLOAD等功能对于内层VM的TCP数据收发失效,较大地影响了VM间通信的性能,给最终用户带来了很差的用户体验。

下面我们来看看不同的方案都是如何实现的:

支持Overlay的交换机

目前较新的万兆交换机大多支持Overlay功能,即支持将以太报文封装在VXLAN报文中予以转发,也即将VXLAN封装功能放在专用的网络设备上,来提供确定性的线速转发性能,但这很明显增加了交换机与服务器之间的耦合,给管理维护带来了一定的复杂度。

NIC VXLAN OFFLOAD

而网卡VXLAN offload则不像Overlay交换机那样要求对组网方案进行较大的变更,而是对于网卡的能力进行了增加,与网卡驱动配合,使得网卡能够知晓VXLAN内部以太报文的位置,从而使得TSO、TCP CHECKSUM OFFLOAD这些技术能够对内部以太报文生效,从而提升TCP性能。

以TSO为例,内核封装出的TCP报文如下:

|ETH_HEADER|IP_HEADER|UDP_HEADER|VXLAN_HEADER|INNER_ETH_HEADER|INNER_IP_HEADER|INNER_TCP_HEADER|INNER_TCP_DATA_NEED_TSO|

该报文的内部TCP数据需要进行TSO处理,即切成满足网卡MTU的报文,内核驱动将报文通过DMA送给网卡后(同时要提供一些元信息,如VXLAN头位置等),网卡负责将内部的TCP报文转换为多个内部TCP报文,封装相同的外层VXLAN头后进行转发,从而减少CPU的干预,大规模提升性能。

小结

个人认为VXLAN OFFLOAD这个名字不如用VXLAN-AWARE OPTIMIZATION比较直观,但厂商为了吸引眼球,自然是需要适当的包装,可以理解。

从实现的角度来说,万兆甚至更高性能的网卡本来就比较贵,这样的优化可能并不一定导致过高的复杂度,但在实现上却能够不依赖于OVERLAY交换机更实现性能的提升,应该是做为优化OVERLAY性能的首选方案。

三十而立

今年阴历生日与阳历生日巧妙的重合了,按理说在这样的时间里,我是适合来写篇文章留做纪念的,然而总不能抽出时间给自己思考,或者说自己潜意识里在抗拒着思考,于是拖了半周,终于想起来这个未完的事情,在这寒冷的冬夜里,与自己对对话,写点给未来自己看的话来。

买房装修是一件非常花时间的事情,一五年似乎除了搬进新家,没有太多能够留在记忆里的事情,前些时间加了四五年前在北京时的旧同事,提到一些共事不久的旧同事,我似乎都已经想不起来了,我打上大学时就觉得自己记忆很短暂,比方说那时我就很难记起自己的童年时光,仿佛那些光阴都被时间抹成了空白,或许这样挺好,使我总能够不被过去所累,能够更好的向前前行。

三十岁到来前,我做了很多思考,始终还没能弄清楚自己在这既长又短暂的人生中的使命,子曰四十不惑,或许我修为未到,亦或圣人在年老时回忆过去,也发现自己三十与四十之间,仍有很多的疑问未能够解答吧,我想这思考求索的本身,也许也是使命的一种。

上周末老友来苏,借着周末陪同他逛了苏州的网师园,雨天的园林静美,我对园林中的对联及字画感到十分有兴致,加之近来我回忆起十多年前买书时首次读到的甪直古镇,使得自己定居苏州得到了合理的解释,这似乎是多年心愿的达成,只是我需要更多的时间来解读这个自己过去便种下渴望的城市。

展望未来五年,我对自己的期望是首先能够准备好心态,迎接孩子加入我和妻子的二人世界,让这个家里多些孩子笑声所带来的温暖与快乐;其次是自己能够更多的创造出更多的让自己觉得惊喜的事情,能够让自己体会到创造的乐趣。

未来是有无限可能的,但我想还是要脚踏实地,幻想在二十多岁的年纪是可以的,而三十岁后,应该更多靠智慧与拼搏。

加油,未来。

linux下的session leader及controlling terminal

最近在调试嵌入式下设备启动基于busybox的init程序所执行的rc.sysinit脚本时,发现该脚本执行的后台脚本,会因为没有关联到串口设备(通过ps的TTY一列可以确认) ,导致我们做了tty设备检测的程序无法顺利执行,借此机会,查阅了busybox的init代码,并对linux的session group和controlling terminal有了一定的理解。

在libc的info文档中Job Control一节,有如下几个重要的定义:

  • process group,即进程组,用管道符创建的一堆命令就行成了一个进程组。
  • session,即会话,通常从login shell登陆后的所有进程都属于一个session,即session由login shell创建,注意libc的文档还是从传统的语义来讲的,对于现代的大多数桌面环境而言(DE,如KDE/GNOME等),一个图形会话,往往会通过setsid函数起大量的session),一个session下可以管理多个process group,一个进程可以在同一个session下的不同process group中迁移,但将一个进程移到另外的会话中则只能通过setsid这个函数来创建新的会话实现。
  • session leader,即会话leader,创建会话的进程称为会话leader,从上面的描述可知,login shell一般为会话leader,首次调用setsid的进程也将成为session leader。
  • controlling terminal,即控制终端,进程的重要属性之一,由fork创建的子进程继续父进程的controlling terminal,而从setsid的libc文档中可以看出(或者用man 3 setsid查看),setsid调用后,进程将做为新的会话的会话leader,并且丢失controlling terminal属性,而其后该会话leader打开的首个tty设备,将成为该会话的controlling terminal(见附2说明);shell通常只会将controlling terminal这个属性给予一个进程组,以便由这个进程组通过终端设备获取输入或者输出信息,这组进程将称为前台任务,而未获得输入或输出等权限的进程组,在尝试向controlling terminal读取或写入数据时,将收到SIGTTIN或SIGTTOU信号,默认情况下这个信号将中止相关程序的执行。这点是合理的,因为如果不做此限制,后台程序很可能扰乱终端输出或者输入的处理。

经过上述讲述,以代码形式给出上述概念的演示。

#include "stdlib.h"
#include "errno.h"
#include "unistd.h"
#include "fcntl.h"

int main()
{
	int err, fd;
	pid_t pid;
	char *pts_name;

    pid = fork();
    if (pid != 0) {
        exit(0); // 主进程退出。
	}

    pid = setsid(); // 在子进程中创建新的会话

    // 打印从父进程继续而来的tty,注意,该tty已经不是我们的controlling terminal
	printf("before we prepare the new stdio, the child inherit its parent's stdio %s\n", ttyname(0));

    // 如下一段代码用于创建一个pseudo-terminal,将作为tty设备被新的会话leader打开,成为该会话的controlling terminal
    fd = getpt();
    pts_name = ptsname(fd);
    printf("allocated a new pts is %s\n", pts_name); 
    grantpt(fd);
    unlockpt(fd);

    // 首次open的tty设备将成为controlling terminal
	fd = open(ptsname(fd), O_RDWR);

    // 将该fd做为标准输入,在本例中意义不大
	close(STDIN_FILENO);
	dup2(fd, STDIN_FILENO);

    // 停留2分,以便我们可以通过ps检验是否获得了新的session及controlling terminal
	sleep(120);
}

编译并执行上述程序后,执行后,立即用如下命令,可以查看到新进程确实拥有了新的controlling terminal了。

ps  -u fortitude -o pid,args,tt,sess,ppid

PID COMMAND                     TT        SESS  PPID
8086 ./sessionleader             pts/5     8086     1

参考文档:

  1. libc info,可在shell下info libc,或在emacs下info libc查看
  2. http://uw714doc.sco.com/en/SDK_sysprog/_The_Controlling-Terminal_and_Pr.htmlWhen a session-leader without a controlling-terminal opens a terminal-device-file and the flag O_NOCTTY is clear on open, that terminal becomes the controlling-terminal assigned to the session-leader if the terminal is not already assigned to some session (see open(2)). When any process other than a session-leader opens a terminal-device-file, or the flag O_NOCTTY is set on open, that terminal does not become the controlling-terminal assigned to the calling-process.

shell中通过set builtin操作positional parameter

最近在调试ubuntu 14.04上的openvswitch服务时,发现openvswitch在upstart里的脚本实现频繁用到了set这样一个shell builtin,代码写的不是很好理解,后来经过查询shell相关文档,终于理解了通过set这个builtin来操作位置变量确实是一个很不错的办法。

bash的info文档中专门有一章节来介绍The Set Builtin,并且由于builtin is so complicated that it deserves its own section,但从本文的角度来看,我们认为set就是用来操作位置变量的,以下用一个简单的示例,就可以很容易的给出set的妙用了。

set -- # --这个特殊的用法来清空位置变量,即用$@来访问位置变量将得到空,除了--,set还有其他很多用法,具体可以查看shell的info文档
set "$@" ls # 用ls来初始化位置变量,ls将被赋值给$1
set "$@" -l # 将$@展开后,与-l一起重新赋值给位置变量,使得ls被赋值给$1,-l给赋值给$@
$@ # 展开位置变量,并执行,等同于执行ls -l命令

从上面的代码示例可以看出,set可以将其后的一串参数赋值给位置变量,从而方便对命令行扩充,支持更好的扩展性,并且由于@这个位置变量全集的通用性,使得在脚本嵌套时,可以方便在被执行的脚本中,对原调用脚本传过来的参数进行进一步的编辑,从而实现更为复杂的应用,比如上述openvswitch的upstart脚本,就在这点上大做了文章。

从这点感觉,bash设计的还是很精妙的,工作中大多数采用C编程,对于shell脚本的编程实践较少,后续需要继续掌握这些细节和强大之处:)

Linux普通时钟与高精度时钟

对于X86 PC来说,主板上会有一个用电池驱动的里存在的以较小hz(如200)这样的interrupt(即tick interrupt),形成以1/hz为单位的jiffes,精度约在ms上的时钟,无法满足高精度计时(ns级)的需要(计时、ntp同步)等 。

于是,通过其他芯片来触发高精度中断,成为linux os提供高精度时间源的一个可能的选择。

对于linux系统,可能过如下命令查看可用的时间源以及当前活跃的时间源:

cat /sys/devices/system/clocksource/clocksource0/available_clocksource
cat /sys/devices/system/clocksource/clocksource0/current_clocksource 

目前在x86架构上使用较多的为tsc这个由x86 cpu内部以cycle计算提供的一个计数器(TSC)来实现的由中断触发的时间源,可以以clock_event_device的行式来触发linux中断执行,从而驱动linux的高精度计时功能。

谈谈Linux下的时间机制

最近同事在解决一个时区问题时花费了较多时间,但仍是一头雾水,通过查询Linux相关的文档,我给出了他一些修改建议,经过验证后问题得以修复,事后我总结来看,主要是该同事对于Linux下的时间机制的理解不够深入,因此这里我将相关的知识给记录下来,以解释相关的疑惑。

Linux下目前有三种主要的时钟,其名称及作用如下:

硬件时钟

此时钟一般由主板上的单独电子器件提供,它与CPU核心是独立的运行的,往往在电脑关机时仍能继续运行(想想我们的PC在装机重启后,时间仍然还保持着正确性就是因为这么个东西存在)。

此时钟还有一些其他名称,如RTC(实时时钟)、BIOS时钟或CMOS时钟,此时钟可以通过hwclock这样的小工具来设置。

需要注意的是,硬件时钟中是不感知时区的,其硬件实现记录了个初始值(如我司设备经常采用的1970.1.1等),然后用户可以通过hwclock写入一个时间来更新它,我们可以认为它是一个有初始值的计数器,用户可以更新当前的值,之后它将按照其计时精度来更新其值,供系统通过硬件接口读取。

系统时钟

此时钟是由Linux内核所维护的一个软件时钟,由时钟中断触发更新,其时间是相对于1970.1.1-00:00:00 UTC这个时间的秒数(UTC与GMT即格林威治时间意义相同,为世界标准时间),为方便这里的讲述,我们可以假定我们在格林威治这个地方讨论时间,这样我们可以先把时区放到一边不管了。

在Linux启动后,系统启动脚本(不管是sysvinit还是systemd都将有这样的处理)将通过hwclock读取硬件时钟,并将时间设置到内核中,此后用户获取时间均通过内核获取。

单调时钟

在Linux系统上,用户可以通过date命令更新系统时间,从而导致系统时钟出现跳变,这对于一些涉及到对调度单元(如协程)运行时间来记时以便切出去让其他进程执行的程序来说则会产生计时不准的问题,为此,Linux上支持了一个所谓的单调时钟的接口,此接口除支持单调时钟外,还支持对于系统时钟的获取,此接口属于较新的POSIX标准,如果Linux版本较老的话,可能将无法支持

此接口的定义如下:

int clock_gettime(clockid_t clk_id, struct timespec *tp); // 采用CLOCK_MONOTONIC这个clk id可以获取到单调时钟

上面讨论时,我刻意互略了关于时区的处理,为简单起见,我们这里可以认为Linux内核是不感知时区的(实际上由于种种原因比如VFAT文件系统的时间戳处理就需要内核需要感知到时区)。

在Linux下,由于大量程序均需要依赖于glibc,因此glibc会读取TZ这个环境变量来设置时区,下面是一些C库函数的简单介绍:

struct tm *gmtime(const time_t *timep); // 获取UTC时间,也就是说怎么改TZ变量或者通过tzset更改时区都不会影响此值
struct tm *localtime(const time_t *timep); // 获取本地时间,更改TZ变量或者通过tzset更改时区都会影响此值

当然当于时区还需要补充一点的是,环境变量这个东西只影响所属的进程,在子进程中修改并不会影响父进程,libc.so甚至提供了一个全局变量用来保存当前进程的时区设置(注意,虽然libc.so为动态库,但其在mmap映射是private的,所以会导致其中定义的全局变量每个进程都复制了一份),如果修改的话,需要通过signal类似的IPC机制通知相关进程予以刷新,这也是我同事改时区反复出问题的一个原因。

从一个编程者的角度来看,如果是涉及到对调度单元(如协程)运行时间记时的话,要尽可能采用单调时钟,下面是syslog-ng项目所采用的异步io库ivykis关于时间处理的代码片段,可以看出,它将尽可能去使用单调时钟或者clock_gettime接口,在最后才尝试使用gettimeofday来获取时间。

#ifdef HAVE_CLOCK_GETTIME
static int clock_source;
#endif

void iv_time_get(struct timespec *time)
{
struct timeval tv;

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC_FAST)
if (clock_source < 1) {
if (clock_gettime(CLOCK_MONOTONIC_FAST, time) >= 0)
return;
clock_source = 1;
}
#endif

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
if (clock_source < 2) {
if (clock_gettime(CLOCK_MONOTONIC, time) >= 0)
return;
clock_source = 2;
}
#endif

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_REALTIME)
if (clock_source < 3) {
if (clock_gettime(CLOCK_REALTIME, time) >= 0)
return;
clock_source = 3;
}
#endif

gettimeofday(&tv, NULL);
time->tv_sec = tv.tv_sec;
time->tv_nsec = 1000L * tv.tv_usec;
}

精神自由

昨晚晚饭时喝了一杯咖啡,于是在夜半时一如往常一样的失眠了,而关于自己此生到底要追求什么这样一个问题,其实在我心里已经盘旋了很久,于是借着昨晚的时间,我好好思考了这个问题,而结果既出于意料之外,又在意料之中,仔细推敲来,我其实一直所追求的是一种自由,但这种自由并非是消费的自由,也并非旅行的自由,虽然这种自由也建构在金钱的基础之上,但这种自由则是思考的自由,工作方式的自由,睡眠的自由,读书的自由,写作的自由等等,而如果要从这些这些自由中抽象出共性出来,那就是精神的自由,或者说是精神层面的自由。

今年是二零一五年,也是我进入三十岁奔向四十岁的关键一年,对我来说,工作、结婚、生子、逐渐年迈的父母、偿还房贷等等事务,都会成为我要面对的现实问题,而我能拿出来安慰自己的,则是我相信自己还能够抽出额外的时间,进行思考、阅读甚至这里的写作这样片刻属于我的时间,而我相信我所追求的,就是我能够在这些事务与自己的内心世界之间寻找到一种平衡,我能够坚守这这块属于我的土地,但却又能维持好前述种种。

从目前来看,实现这种自由的前提很世俗,就是资金,但相比财务自由来说,我的要求却低的很,或许是因为成长于农村的原因,我生活并不奢华,也不想追求奢华,于我而言,我只需要简单而基本的生活,于是我有理由相信自己实现这个过程较为简单,它应该是两部分,首先我可以拥有一个覆盖日常生活开支的固定收入来源,比方说一套用于出租的房产或者其他较为稳健的理财产品,之后是我将从事相对灵活自由的工作,像我这样的职业,在互联网这样的时代,想做自由职业者应该容易的多;通过这两部分的结合,我希望再经过十年,也即我四十岁的时候,我能够实现自己这里所谈到的精神自由的资金基础。

想清楚自己追求之后,对于我而言,最大的作用是能够理性正视生活中所遇到的事情,哪些事情我需要隐忍,哪些事情我需要放手一博,哪些事情我可以做,哪些事情我不可以做,也有助于我更好的做好人生规划,不偏离人生方向的持续前行。

我很开心在自己即将在而立之年能想通这样一件事,于是就记录在网络上的这片属于自己的地方,期待将来我回过头来再来看看。

附:

当我发布这篇文章到网站上后,才发现原来我网站的标题就是通往成熟与自由的旅程,看来这并不是巧合,而是我潜意识中故有的所在了。

Midonet聪明的Tunnel Key分配策略

拥抱开源如今成了一种潮流,既Juniper家的OpenContrail成为开源一揽子(之所以称为一揽子是用于区别现有OpenStack中由社区所维护的基于分散组件的松耦合方式)OpenStack网络虚拟化方案的首发明星后,Midukura这家公司也将自家的OpenStack网络虚拟化方案以开源方式来运作了,相比于OpenContrail的工程师的设备商研发背景,Midukura更具有IT运维背景(这点可以从其技术堆栈如scala编程语言、zookeeper/cassandra数据库、分布式架构推测出来,关于更细节的内容,后面我会陆续补充相关的分析),因此我个人还是更看好Midokura的方案,而从自己实际部署及验证的情况来看,Midokura要靠谱的多一点,当然这只是一家之言,也并非本文的重点,就不再展开。

在本文中我想谈的是,Midonet Tunnel Key的使用策略,与我们通常在基于OVS的方案中看到Tunnel Key(不论是NVGRE还是VXLAN)一般用做租户标识(即Virtual Network Identifier)不同,Midonet Tunnel Key则可以理解为按VIF分配的(实际上是基于Midonet的外部虚拟端口分配的,这里为了简化理解,可以简单认为就是按VIF分配的),而前者其实也是IETF相关标准文档所描述的用法。

Midonet之所以这样做的原因,与其架构实现有较大的关系,Midonet的架构总结起来,有如下的这几种特色:

  • 全分布式架构,每个节点上运行一个Midolman进程(跑在JVM上),用于负责从分布式数据库(zookeeper)同步并发布虚拟网络配置信息,因此实现了去中心化,每个节点可以独立计算出转发结果
  • 虚拟拓朴仿真机制,Midolman从VIF上收到报文后,通过拓朴转发仿真(与我们常规的Bridge->Router->Router->Bridge转发类似),就可以计算出目的VIF,从而得知目的虚拟机所在的主机,并在通过Underlay网络的IP以NVGRE/VXLAN的方式将报文Overlay传送给目的主机前就将报文编辑好
  • 内核转发采用OVS datapath,即采用exact match的flow转发(megaflow暂不支持),以实现次包(即首包之后的包)的内核转发,提高转发性能

结合上面的介绍,Midonet采用基于VIF分配的原因就比较清楚的:

  • 即然源端可以直接仿真出目的VIF且完成报文编辑,因此到对端唯一需要做的就是知道对端的VIF对应的OVS datapath接口是什么,NVGRE/VXLAN封装中的Tunnel Key则提供了一个简单方便的编码点,于是midonet就这么用了,另外一个Host上通过Zookeeper分配的Tunnel Key空间有10位数(非标准限制,而是Zookeeper的限制),对于一个cpu有限、memory有限的主机来说,10位数的VM已经远超能力极限的(咱们又不是天河一号,天河一号估计也不行),已经足足够用了
  • Midonet采用与OVS相似的机制,转件层面采用wildcard match流表,内核采用exact match流表,因此在主机上的Midolman扫描到VIF对应的TAP接口时,Midolman就可以为这个VIF生成一条匹配域为该VIF在该主机上所申请到的Tunnel Key为匹配项的wildcard match流表,其出接口即为该VIF所对应的OVS datapath接口,当收到含有该Tunnel Key的报文时,可以直接命中该wildcard match流表,提取报文字段生成exact match流表出接口继承该wildcard流表向OVS datapath下发即可,Wildcard match流表查询及exact match流表下发会非常迅速,也弥补了Java/Scala代码在未被JIT编译器优化时的性能损失,是一个非常优雅的方案

以上便是我个人对于Midonet Tunnel Key分配的理解,在软件定义网络这股风潮日近的今天,来自于互联网IT企业所带来的新思维,新用法,也许会继续改变设备商的开发模式,设备商要想办法适应并拥抱这种变化了。