應用容器化後爲什麼性能下降這麼多?

作者:雲之舞者

原文:https://juejin.cn/post/7268663683881828413

背景

隨着越來越多的公司擁抱雲原生,從原先的單體應用演變爲微服務,應用的部署方式也從虛機變爲容器化,容器編排組件 k8s 也成爲大多數公司的標配。然而在容器化以後,我們發現應用的性能比原先在虛擬機上表現更差,這是爲什麼呢?

基於 Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus 實現的前後端分離博客,包含後臺管理系統,支持文章、分類、標籤管理、儀表盤等功能。

  • GitHub 地址:https://github.com/weiwosuoai/WeBlog

  • Gitee 地址:https://gitee.com/AllenJiang/WeBlog

壓測結果

容器化之前的表現

應用部署在虛擬機下,我們使用 wrk 工具進行壓測,壓測結果如下:

壓測結果

從壓測結果看,平均 RT 爲 1.68ms,qps 爲 716/s,我們再來看下機器的資源使用情況,cpu 基本已經被打滿。

cpu 基本已經被打滿

容器化後的表現

使用 wrk 工具進行壓測,結果如下:

壓測結果

從壓測結果看,平均 RT 爲 2.11ms,qps 爲 554/s,我們再來看下機器的資源使用情況,cpu 基本已經被打滿。

cpu 基本已經被打滿

性能對比結果

0R4Q89

「總體性能下降:RT(25%)、QPS(29%)」

原因分析

架構差異

由於應用在容器化後整體架構的不同、訪問路徑的不同,將可能導致應用容器化後性能的下降,於是我們先來分析下兩者架構的區別。我們使用 k8s 作爲容器編排基礎設施,網絡插件使用 calico 的 ipip 模式,整體架構如下所示。

架構差異

這裏需要說明,雖然使用 calico 的 ipip 模式,由於 pod 的訪問爲 service 的 nodePort 模式,所以不會走 tunl0 網卡,而是從 eth0 經過 iptables 後,通過路由到 calico 的 calixxx 接口,最後到 pod。

性能分析

在上面壓測結果的圖中,我們容器化後,cpu 的軟中斷 si 使用率明顯高於原先虛擬機的 si 使用率,所以我們使用 perf 繼續分析下熱點函數。

性能分析

爲了進一步驗證是否是軟中斷的影響,我們使用 perf 進一步統計軟中斷的次數。

軟中斷的次數

「我們發現容器化後比原先軟中斷多了 14%,到這裏,我們能基本得出結論,應用容器化以後,需要更多的軟中斷的網絡通信導致了性能的下降。」

軟中斷原因

由於容器化後,容器和宿主機在不同的網絡 namespace, 數據需要在容器的 namespace 和 host namespace 之間相互通信, 使得不同 namespace 的兩個虛擬設備相互通信的一對設備爲 veth pair, 可以使用 ip link 命令創建,對應上面架構圖中紅色框內的兩個設備,也就是 calico 創建的 calixxx 和容器內的 eth0。我們再來看下 veth 設備發送數據的過程

static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
{
...
    if (likely(veth_forward_skb(rcv, skb, rq, rcv_xdp)
...
}

static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,
       struct veth_rq *rq, bool xdp)
{
 return __dev_forward_skb(dev, skb) ?: xdp ?
  veth_xdp_rx(rq, skb) :
  netif_rx(skb);//中斷處理
}


/* Called with irq disabled */
static inline void ____napi_schedule(struct softnet_data *sd,
         struct napi_struct *napi)
{
 list_add_tail(&napi->poll_list, &sd->poll_list);
  //發起軟中斷
 __raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

通過虛擬的 veth 發送數據和真實的物理接口沒有區別,都需要完整的走一遍內核協議棧,從代碼分析調用鏈路爲 veth_xmit -> veth_forward_skb -> netif_rx -> __raise_softirq_irqoff,「veth 的數據發送接收最後會使用軟中斷的方式,這也剛好解釋了容器化以後爲什麼會有更多的軟中斷,也找到了性能下降的原因。」

優化策略

原來我們使用 calico 的 ipip 模式,它是一種 overlay 的網絡方案,容器和宿主機之間通過 veth pair 進行通信存在性能損耗,雖然 calico 可以通過 BGP,在三層通過路由的方式實現 underlay 的網絡通信,但還是不能避免 veth pari 帶來的性能損耗,針對性能敏感的應用,那麼有沒有其他 underly 的網絡方案來保障網絡性能呢?那就是 macvlan/ipvlan 模式,我們以 ipvlan 爲例稍微展開講講。

基於 Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus 實現的前後端分離博客,包含後臺管理系統,支持文章、分類、標籤管理、儀表盤等功能。

  • GitHub 地址:https://github.com/weiwosuoai/WeBlog

  • Gitee 地址:https://gitee.com/AllenJiang/WeBlog

ipvlan L2 模式

IPvlan 和傳統 Linux 網橋隔離的技術方案有些區別,它直接使用 linux 以太網的接口或子接口相關聯,這樣使得整個發送路徑變短,並且沒有軟中斷的影響,從而性能更優。如下圖所示:

ipvlan L2 模式

上圖是 ipvlan L2 模式的通信模型,可以看出 container 直接使用 host eth0 發送數據,可以有效減小發送路徑,提升發送性能。

ipvlan L3 模式

ipvlan L3 模式,宿主機充當路由器的角色,實現容器跨網段的訪問,如下圖所示:

ipvlan L3 模式

Cilium

除了使用 macvlan/ipvlan 提升網絡性能外,我們還可以使用 Cilium 來提升性能,Cilium 爲雲原生提供了網絡、可觀測性、網絡安全等解決方案,同時它是一個高性能的網絡 CNI 插件,高性能的原因是優化了數據發送的路徑,減少了 iptables 開銷,如下圖所示:

Cilium

雖然 calico 也支持 ebpf,但是通過 benchmark 的對比,Cilium 性能更好,高性能名副其實,接下來我們來看看官網公佈的一些 benchmark 的數據,我們只取其中一部分來分析,如下圖:

benchmark 數據

benchmark

無論從 QPS 和 CPU 使用率上 Cilium 都擁有更強的性能。

總結

容器化帶來了敏捷、效率、資源利用率的提升、環境的一致性等等優點的同時,也使得整體的系統複雜度提升一個等級,特別是網絡問題,容器化使得整個數據發送路徑變長,排查難度增大。不過現在很多網絡插件也提供了很多可觀測性的能力,幫助我們定位問題。

我們還是需要從實際業務場景出發,針對容器化後性能、安全、問題排查難度增大等問題,通過優化架構,增強基礎設施建設才能讓我們在雲原生的路上越走越遠。

最後,感謝大家觀看,也希望和我討論雲原生過程中遇到的問題。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ezjon9KNNrl7Oy_PafEyOw