使用Docker網路解決方案weave遇到的那些坑

weixin_33860722發表於2017-07-16

前言

Weave 作為 Docker 跨主機叢集網路解決方案的一種,可以用於連線部署在多臺主機上的 Docker 容器,使用網路的應用程式不必去配置埠對映等資訊,Weave的通訊支援加密,所以使用者可以從一個不受信任的網路連線到主機。Weave 在控制層面和 Calico 類似,在資料層面通過UDP封裝實現 L2 overlay 。 Weave 在 1.2 版本之前都是使用 usersapce 實現,在 weave-1.2版本後, weave 結合了核心的 openvswitch 模組,實現了 open vSwitch datapath(odp)功能,結合 kernel 的 vxlan 特性,在網路效能上有較大的提升。 由於 odp 功能和核心相關模組結合較為緊密,在實際使用中可能遇到一些和核心相關的坑,本文描述的兩個問題都和核心有關係。

坑一:weave fastdp造成虛擬機器網路中斷

問題描述

在 weave 1.2 版本之後,考慮到原先 sleeve 模式的網路效能較差,增加了 fastdp 的模式,該模式成為了 weave 啟動時的預設模式,在 fastdp 模式中使用了 kernel 中的 openvswitch 模組,做報文封裝時使用了 vxlan 協議。在使用 qemu-kvm 建立的雲主機上, 如果安裝的是 centos7.0 ,核心版本為 kernel-3.10.123 ,在啟動 weave 並使用 fastdp 模式時,會造成 virtio_net 虛擬網路卡無法傳送資料,進而導致整個虛擬機器的網路中斷的問題。

問題分析

造成網路斷開的原因是因為觸發了核心的一個 bug ,該核心 bug 的 commit 連結地址:https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=8a0cafc9a8131cc545dc9924aed38f7176ee4ad7

觸發該 bug 主要是由於 Weave 在初始化時會傳送一個 60000 位元組的 UDP 資料包進行 PMTU 的探測,並且 Weave 傳送使用的套接字為 raw socket ,導致了 virtio_net 使用的記憶體被汙染,表現的現象就是無法通知到宿主機上 vhost 取資料,在介面上看到傳送報文的計數始終不會增加。

該問題不是隻有 weave 才能觸發,使用普通的應用程式只要在建立 socket 時,使用是 raw socket ,並且在傳送資料時,傳送的資料大於介面的 MTU 值,介面的 UFO 功能是開啟的,都極有可能觸發該問題,造成網路的中斷。

1728994-a244f8f66246053e.png

圖1:fastdp 模式的資料流原理

解決方案

有兩種方式可以解決該問題:

1)升級核心,保證核心版本大於等於 3.13 ,

2)關閉虛擬機器網路卡的 ufo 特性,centos7.1 的 kernel-3.10.229 核心已經修復了該問題。

1728994-5dfdf55fa37d8300.png

圖2:guest 通知 vhost 讀取資料流程

坑二:Weave無法使用fastdp模式

問題描述

在核心版本 CentOS Linux (3.10.0-327.10.1.el7.x86_64) 7 (Core) 上 ,weave 版本大約 1.2 ,如果雲主機的 MTU 值為 1450 或者小於 1474 , weave 啟動時無法正常的選擇 fast data path模式。在 weave 啟動後一直選擇 sleeve 模式,本應該預設模式為 fastdp ,該問題也和核心的版本相關。

問題分析

Weave的fast data path路徑使用到了 odp 技術,也就是核心中的OVS模組,在 Container 中直接傳送資料包到 ovs 模組。在啟動 Weave 時,會自動選擇使用 sleeve 模式還是 fastdp 模式,這裡通過傳送心跳包來決定的,出現該問題時,在雲主機通過 docker logs weave 的日誌可以看到這樣出錯的資訊:fastdp timed out waiting for vxlan heartbeat。

這個 heartbeat 的資料包,是一個 UDP 包,目的埠號為 6784 ,在某些雲主機上介面的MTU值為 1454 ,但是在傳送 UDP 的 heartbeat 資料包時,傳送的是 1474 位元組,這樣就會對報文在IP層進行分片,但在主機上發現心跳報文傳送不出,當 MTU 的值修改為 1500 後,就可以傳送出去,在 MTU 為 1454 的情況下,會出現下面的 ICMP 的錯誤報文。

1728994-2e94250ffa9f91c1.png

圖3: 出現的錯誤icmp報文

出現上面錯誤的 icmp 報文,是核心中的 ip_fragment 函式呼叫 icmp_send 函式傳送的,

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;

}

通過上面的程式碼可以看出,如果出現上面出錯的 ICMP 報文,下面的判斷條件 iph->frag_off & htons(IP_DF)) && !skb->ignore_df 需要成立,通過對抓取的報文分析可知 iph->frag_off & htons(IP_DF)) 的值為真,那麼 skb->ignore_df 值需要為 0 ,這裡的關鍵在於 skb->ignore_df 的值是何時賦值為0的。

通過分析 weave 傳送心跳包的流程可知,在 vxlan_tnl_send 函式中,對 skb->ignore_df 賦值為 1 ; 在最後呼叫 tunnel 的傳送函式 iptunnel_xmit 時,呼叫了 skb_scrub_packet 函式,在該函式中又重新對 skb->ignore_df 賦值為0.( kernel 版本為:3.10.0-327.el7 ),造成後續傳送報文時,傳送了 ICMP 目的不可達,並且錯誤碼為 ICMP_FRAG_NEEDED 的報文。

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 ,而在老一些的核心版本 3.10.0 - 123.el7 上, iptunnel_xmit 呼叫的是 secpath_reset(skb) 函式,在該函式中並沒有對 skb->local_df (低版本的核心使用的是 local_df )進行重新的初始化,也就是 skb->local_df 值仍舊為1,因此在該版本上不會出現該問題。

static inline void

secpath_reset(struct sk_buff *skb)

{

#ifdef CONFIG_XFRM

secpath_put(skb->sp);

skb->sp = NULL;

#endif

}

1728994-a76bc676a598be7d.png

圖4:核心版本不同造成設定不同

在新的核心版本中存在該問題,但是核心本身是沒問題的,還是 weave 使用者態的管理 datapath 的程式和核心的適配上有問題(它並不是使用 ovs-switchd ),在 ovs 中的對 tunnel 型別可以設定為 df_default = false 進行分片。

解決方案

保證介面 MTU 值為預設的1500.

總結

Weave 的 odp 功能使用了核心的特性,在使用 weave 的 fastdp 功能時遇到上面兩個問題,都是和核心有著緊密聯絡,通過對核心層面的分析,可以定位到問題的根因。在後續遇到類似問題時,可以多從核心的角度進行考慮。

本文由『UCloud核心團隊』原創,作者:曾福振。

未經允許不得私自轉載,比心~

相關文章