前言Weave作为Docker(一个开源的应用容器引擎)的跨主机集群网络解决方案,可以用来连接部署在多台主机上的Docker容器。使用网络的应用程序不需要配置端口映射、链接等信息。此外,Weave的通信支持加密,用户可以从不受信任的网络连接到主机。Weave在控制层类似于Calico,在数据层通过UDP封装实现L2overlay。Weave在1.2版本之前是通过usersapce实现的。Weave-1.2之后,Weave结合内核OpenvSwitch模块实现了OpenvSwitchdatapath(ODP)功能。结合内核的vxlan特性,网络性能有了很大的提升。.由于ODP功能与内核相关模块紧密结合,在实际使用中可能会遇到一些内核相关的“pitch”。本文描述的两个问题都与内核有关。坑一:使用WeaveFastDb导致虚拟机网络中断1、问题描述Weave1.2版本后,考虑到原有sleeve模式网络性能不佳,增加了FastDb模式,也成为了Weave启动时的默认模式。在FastDb模式下,使用内核中的OpenvSwitch模块,使用vxlan协议进行数据包封装。在使用qemu-kvm创建的云主机上,如果安装了centos7.0,内核版本为kernel-3.10.123,则启动Weave,使用FastDb模式时,virtio_net虚拟网卡无法发送数据,会导致整个虚拟机网络中断。问题分析网络断开的原??因是触发了内核中的一个bug。内核bug的commit链接地址为:https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=8a0cafc9a8131cc545dc9924aed38f7176ee4ad7(网址太长,可以进入http://t.cn/Ro53BsH查看)触发这个bug主要是因为Weave在初始化的时候会发送一个60000字节的UDP包用于PMTU检测,而Weave发送的socket是rawsocket,导致virtio_net使用的内存被污染。具体表现为无法通知host上的vhost获取数据,界面上看到的发包数永远不会增加。这个问题不仅仅由Weave触发。使用普通应用创建socket时,使用rawsocket,发送的数据大于接口的MTU值,并开启接口的UFO功能。中断。(图:FastDb模式的数据流转原理)二、解决方案1)升级内核,保证内核版本大于等于3.13;2)禁用虚拟机网卡的ufo特性;3)修复了centos7.1的kernel-3.10.229内核,问题解决。(图:guest通知vhost读取数据流)坑2:Weave不能使用FastDb模式1.问题描述在内核版本CentOSLinux(3.10.0-327.10.1.el7.x86_64)7(Core),andWeave版本大于1.2,如果云主机的MTU值为1450或小于1474,Weave启动时无法正常选择FastDataPath模式。Weave启动后,套筒模式已被选中。默认模式应该是FastDb。这个问题也和内核的版本有关。2、问题分析Weave的FastDataPath采用ODP技术,即内核中的OVS模块,直接将数据包发送给Container中的ovs模块。启动Weave时,会自动选择使用sleeve模式还是FastDb模式,通过发送心跳包来决定。出现这个问题时,可以在云主机上的DockerlogsWeave日志中看到报错信息:FastDbtimedoutwaitingforvxlanheartbeat。心跳数据包是一个UDP包,目的端口号是6784,有些云主机上接口的MTU值是1454,但是发送UDP心跳数据包的时候,发送了1474字节,所以包会分片在IP层进行,但主机无法发送心跳报文。MTU值改成1500后就可以发送出去了。当MTU为1454时,将出现以下ICMP错误信息。(图3:错误的ICMP报文)上面错误的ICMP报文是内核中的ip_fragment函数调用ICMP_send函数发送的,if(unlikely((((iph->frag_off&htons(IP_DF))&&!skb->ignore_df)||(IPCB(skb)->frag_max_size&&IPPCB(skb)->frag_max_size>mtu))){IP_INC_STATS(dev_net(dev),IPSTATS_MIB_FRAGFAILS);ICMP_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,htonl(mtu));kfree_skb(skb);return-EMSGSIZE;}从上面的代码可以看出,如果出现错误的ICMP报文,需要建立如下判断条件iph->frag_off&htons(IP_DF))&&!skb->ignore_df。通过抓包消息分析可以看出iph->frag_off&htons(IP_DF))的值为true,那么skb->ignore_df的值需要为0,这里的关键是当值skb->ignore_df的赋值为0。通过分析Weave发送心跳包的过程,我们可以看到,在vxlan_tnl_send函数中,skb->ignore_df被赋值为1,在调用隧道的发送函数iptunnel_xmit时,会调用skb_scrub_packet函数,skb->在该函数中重新设置ignore_df为0(内核版本:3.10.0-327.el7),导致ICMP目的地不可达,后续发送报文时错误码为ICMP_FRAG_NEEDED。voidskb_scrub_packet(structsk_buff*skb,boolxnet){skb->tstamp.tv64=0;skb->pkt_type=PACKET_HOST;skb->skb_iif=0;skb->ignore_df=0;skb_dst_drop(skb);secpath_reset(skb);nf_reset(skb);nf_reset_trace(skb);if(!xnet)return;skb_orphan(skb);skb->mark=0;}以上代码是centos7的3.10.0-327.el7,在一些老内核版本3.10.在0-123.el7上iptunnel_xmit调用secpath_reset(skb)函数,并没有重新初始化skb->local_df(低版本内核使用local_df),即skb->local_df的值还是1,所以在此版本不会出现上述问题。staticinlinevoidsecpath_reset(structsk_buff*skb){#ifdefCONFIG_XFRMsecpath_put(skb->sp);skb->sp=NULL;#endif}(图:内核版本不同导致设置不同)虽然新内核版本存在这个问题,但是内核本身没有问题是Weave用户态管理datapath程序和内核的适配有问题(它没有使用ovs-switchd)。在OVS中,隧道类型可以设置为df_default=false来分片。解决方法是保证接口的MTU值默认为1500。综上所述,Weave的ODP功能使用了内核特性,以上两个使用Weave的FastDb功能遇到的问题都与内核息息相关。通过对内核层的分析,可以定位到问题的根源,所以以后遇到类似的问题时,可以多从内核的角度去考虑。【本文为专栏作者“大U的科技课堂”原创文章,转载请微信♂(ucloud2012)联系作者】点此查看更多本作者好文
