使用Docker網(wǎng)絡(luò)解決方案weave遇到的那些坑
前言
Weave 作為 Docker 跨主機(jī)集群網(wǎng)絡(luò)解決方案的一種,可以用于連接部署在多臺主機(jī)上的 Docker 容器,使用網(wǎng)絡(luò)的應(yīng)用程序不必去配置端口映射等信息,Weave的通信支持加密,所以用戶可以從一個(gè)不受信任的網(wǎng)絡(luò)連接到主機(jī)。Weave 在控制層面和 Calico 類似,在數(shù)據(jù)層面通過UDP封裝實(shí)現(xiàn) L2 overlay 。 Weave 在 1.2 版本之前都是使用 usersapce 實(shí)現(xiàn),在 weave-1.2版本后, weave 結(jié)合了內(nèi)核的 openvswitch 模塊,實(shí)現(xiàn)了 open vSwitch datapath(odp)功能,結(jié)合 kernel 的 vxlan 特性,在網(wǎng)絡(luò)性能上有較大的提升。 由于 odp 功能和內(nèi)核相關(guān)模塊結(jié)合較為緊密,在實(shí)際使用中可能遇到一些和內(nèi)核相關(guān)的坑,本文描述的兩個(gè)問題都和內(nèi)核有關(guān)系。
坑一:weave fastdp造成虛擬機(jī)網(wǎng)絡(luò)中斷
1. 問題描述
在 weave 1.2 版本之后,考慮到原先 sleeve 模式的網(wǎng)絡(luò)性能較差,增加了 fastdp 的模式,該模式成為了 weave 啟動(dòng)時(shí)的默認(rèn)模式,在 fastdp 模式中使用了 kernel 中的 openvswitch 模塊,做報(bào)文封裝時(shí)使用了 vxlan 協(xié)議。在使用 qemu-kvm 創(chuàng)建的云主機(jī)上, 如果安裝的是 centos7.0 ,內(nèi)核版本為 kernel-3.10.123 ,在啟動(dòng) weave 并使用 fastdp 模式時(shí),會(huì)造成 virtio_net 虛擬網(wǎng)卡無法發(fā)送數(shù)據(jù),進(jìn)而導(dǎo)致整個(gè)虛擬機(jī)的網(wǎng)絡(luò)中斷的問題。
2. 問題分析
造成網(wǎng)絡(luò)斷開的原因是因?yàn)橛|發(fā)了內(nèi)核的一個(gè) bug ,該內(nèi)核 bug 的 commit 鏈接地址:https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=8a0cafc9a8131cc545dc9924aed38f7176ee4ad7 。
觸發(fā)該 bug 主要是由于 Weave 在初始化時(shí)會(huì)發(fā)送一個(gè) 60000 字節(jié)的 UDP 數(shù)據(jù)包進(jìn)行 PMTU 的探測,并且 Weave 發(fā)送使用的套接字為 raw socket ,導(dǎo)致了 virtio_net 使用的內(nèi)存被污染,表現(xiàn)的現(xiàn)象就是無法通知到宿主機(jī)上 vhost 取數(shù)據(jù),在接口上看到發(fā)送報(bào)文的計(jì)數(shù)始終不會(huì)增加。
該問題不是只有 weave 才能觸發(fā),使用普通的應(yīng)用程序只要在建立 socket 時(shí),使用是 raw socket ,并且在發(fā)送數(shù)據(jù)時(shí),發(fā)送的數(shù)據(jù)大于接口的 MTU 值,接口的 UFO 功能是打開的,都極有可能觸發(fā)該問題,造成網(wǎng)絡(luò)的中斷。
圖1:fastdp 模式的數(shù)據(jù)流原理
3. 解決方案
有兩種方式可以解決該問題:
1)升級內(nèi)核,保證內(nèi)核版本大于等于 3.13 ,
2)關(guān)閉虛擬機(jī)網(wǎng)卡的 ufo 特性,centos7.1 的 kernel-3.10.229 內(nèi)核已經(jīng)修復(fù)了該問題。
圖2:guest 通知 vhost 讀取數(shù)據(jù)流程
坑二:Weave無法使用fastdp模式
1. 問題描述
在內(nèi)核版本 CentOS Linux (3.10.0-327.10.1.el7.x86_64) 7 (Core) 上 ,weave 版本大約 1.2 ,如果云主機(jī)的 MTU 值為 1450 或者小于 1474 , weave 啟動(dòng)時(shí)無法正常的選擇 fast data path模式。在 weave 啟動(dòng)后一直選擇 sleeve 模式,本應(yīng)該默認(rèn)模式為 fastdp ,該問題也和內(nèi)核的版本相關(guān)。
2. 問題分析
Weave的fast data path路徑使用到了 odp 技術(shù),也就是內(nèi)核中的OVS模塊,在 Container 中直接發(fā)送數(shù)據(jù)包到 ovs 模塊。在啟動(dòng) Weave 時(shí),會(huì)自動(dòng)選擇使用 sleeve 模式還是 fastdp 模式,這里通過發(fā)送心跳包來決定的,出現(xiàn)該問題時(shí),在云主機(jī)通過 docker logs weave 的日志可以看到這樣出錯(cuò)的信息:fastdp timed out waiting for vxlan heartbeat。
這個(gè) heartbeat 的數(shù)據(jù)包,是一個(gè) UDP 包,目的端口號為 6784 ,在某些云主機(jī)上接口的MTU值為 1454 ,但是在發(fā)送 UDP 的 heartbeat 數(shù)據(jù)包時(shí),發(fā)送的是 1474 字節(jié),這樣就會(huì)對報(bào)文在IP層進(jìn)行分片,但在主機(jī)上發(fā)現(xiàn)心跳報(bào)文發(fā)送不出,當(dāng) MTU 的值修改為 1500 后,就可以發(fā)送出去,在 MTU 為 1454 的情況下,會(huì)出現(xiàn)下面的 ICMP 的錯(cuò)誤報(bào)文。
圖3: 出現(xiàn)的錯(cuò)誤icmp報(bào)文
出現(xiàn)上面錯(cuò)誤的 icmp 報(bào)文,是內(nèi)核中的 ip_fragment 函數(shù)調(diào)用 icmp_send 函數(shù)發(fā)送的,
- if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
- (IPCB(skb)->frag_max_size &&
- IPCB(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;
- }
通過上面的代碼可以看出,如果出現(xiàn)上面出錯(cuò)的 ICMP 報(bào)文,下面的判斷條件 iph->frag_off & htons(IP_DF)) && !skb->ignore_df 需要成立,通過對抓取的報(bào)文分析可知 iph->frag_off & htons(IP_DF)) 的值為真,那么 skb->ignore_df 值需要為 0 ,這里的關(guān)鍵在于 skb->ignore_df 的值是何時(shí)賦值為0的。
通過分析 weave 發(fā)送心跳包的流程可知,在 vxlan_tnl_send 函數(shù)中,對 skb->ignore_df 賦值為 1 ; 在***調(diào)用 tunnel 的發(fā)送函數(shù) iptunnel_xmit 時(shí),調(diào)用了 skb_scrub_packet 函數(shù),在該函數(shù)中又重新對 skb->ignore_df 賦值為0.( kernel 版本為:3.10.0-327.el7 ),造成后續(xù)發(fā)送報(bào)文時(shí),發(fā)送了 ICMP 目的不可達(dá),并且錯(cuò)誤碼為 ICMP_FRAG_NEEDED 的報(bào)文。
- void skb_scrub_packet(struct sk_buff *skb, bool xnet)
- {
- 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;
- }
上面代碼是 centos 7 的 3.10.0 - 327.el7 ,而在老一些的內(nèi)核版本 3.10.0 - 123.el7 上, iptunnel_xmit 調(diào)用的是 secpath_reset(skb) 函數(shù),在該函數(shù)中并沒有對 skb->local_df (低版本的內(nèi)核使用的是 local_df )進(jìn)行重新的初始化,也就是 skb->local_df 值仍舊為1,因此在該版本上不會(huì)出現(xiàn)該問題。
- static inline void
- secpath_reset(struct sk_buff *skb)
- {
- #ifdef CONFIG_XFRM
- secpath_put(skb->sp);
- skb->sp = NULL;
- #endif
- }
圖4:內(nèi)核版本不同造成設(shè)置不同
在新的內(nèi)核版本中存在該問題,但是內(nèi)核本身是沒問題的,還是 weave 用戶態(tài)的管理 datapath 的程序和內(nèi)核的適配上有問題(它并不是使用 ovs-switchd ),在 ovs 中的對 tunnel 類型可以設(shè)置為 df_default = false 進(jìn)行分片。
3. 解決方案
保證接口 MTU 值為默認(rèn)的1500.
總結(jié)
Weave 的 odp 功能使用了內(nèi)核的特性,在使用 weave 的 fastdp 功能時(shí)遇到上面兩個(gè)問題,都是和內(nèi)核有著緊密聯(lián)系,通過對內(nèi)核層面的分析,可以定位到問題的根因。在后續(xù)遇到類似問題時(shí),可以多從內(nèi)核的角度進(jìn)行考慮。
【本文是51CTO專欄機(jī)構(gòu)作者“大U的技術(shù)課堂”的原創(chuàng)文章,轉(zhuǎn)載請通過微信公眾號(ucloud2012)聯(lián)系作者】