基於kubernetes的VM解決方案探討

一、背景

eBay從2015年就開始適配kubernetes平臺並逐漸部署各個團隊的產品。然而eBay仍然部署着很大規模的OpenStack集羣。同時管理kubernetes集羣和OpenStack集羣需要耗費更多的人力和物力。但由於eBay內部還有一部分業務無法遷移到容器,我們能否用一套控制平面(control plane)同時管理容器和虛擬機呢

二、已有的方案

在幾年的時間裏,kubernetes不斷髮展壯大,各個功能逐漸完善,越來越多的公司基於kubernetes構建雲平臺。在eBay內部,越來越多的產品基於kubernetes構建,因此用kubernetes來統一雲平臺是大勢所趨,我們需要一種方案來基於kubernetes管理虛擬機

現在社區有兩套相對成熟的基於kubernetes來管理虛擬機的方案,分別是kubevirt和virtlet

1. Kubevirt

Kubevirt是redhat發起的項目,它使用CRD去描述一個VM(Virtual Machine,虛擬機),通過它的控制器(controller)去把CRD轉換成一個POD。由於它使用的是CRD而不是POD,導致需要額外的控制器來實現kubernetes裏**原生的部署和服務(deployment,service)這些功能。VM的實例運行在容器(container)**裏,一個VM有對應的libvirt來管理。Kubevirt的社區比較活躍,但版本還在較早的階段。

2. Virtlet

Virtlet來自於Mirantis,跟kubevirt的不同之處在於它使用POD來描述一個VM。因爲kubelet是通過CRI接口跟下面的運行時交互的,virtlet實現了一套VM的CRI。因爲POD是kubernetes的一等公民,任何現有的kubernetes功能都可以用於virtlet管理的VM,且不需要額外的控制器,比如服務、部署等等,這樣幾乎不需要額外的學習和維護成本。但因爲一些VM特定的信息無法完全用POD來描述,virtlet藉助了POD的註解(annotation)來表達更多VM的信息。

三、我們的選擇

根據我們的評估以及eBay現有的架構,選擇了virtlet來作爲kube VM方案。首先kube VM入口是基於AZ(Availability Zone,可用區域)的,已經有了一套CRD來描述現有的kubernetes節點,只需要加一個provider(提供者)就可以重用現有AZ的各種控制器。其次,需要各種定製來達到跟OpenStack VM同樣的功能和性能。Virtlet因爲有一套單獨的CRI實現,可以更容易地加入各種定製。

1. Virtlet概覽

圖1是來自virtlet社區的架構圖。一個節點上virtlet POD包含三個容器

  • virtlet:接收CRI調用,管理VM
  • libvirt:接收virtlet的請求創建、停止或銷燬VM
  • VMs:所有virtlet管理的VM都會在這個容器的命名空間裏

圖1(點擊可查看大圖)

2. 運行時

圖2(點擊可查看大圖)

01 CRIProxy

如圖2所示,因爲virtlet有單獨的CRI實現,如果在一個節點上既需要支持VM又要支持容器,就需要有一個proxy來分發CRI調用

  • 鏡像:CRIProxy通過區分一個可配置的前綴來區分是一個容器鏡像還是一個VM鏡像。
  • 運行時:CRIProxy通過特定的註解來區分容器或VM。

默認情況下所有的調用會分發給容器運行時。

02 改進

因爲引入了CRIProxy,多了一層中間調用,使這個架構看起來不夠統一,多了一層可能的出錯點,增加了出問題的機率以及調試的難度。所以我們下階段計劃開發一個VM shim,如圖3所示。它滿足containerd的shim規範。這樣單個節點的運行時看起來更統一和乾淨。

圖3(點擊可查看大圖)

四、模型

剛有提到,操作VM的入口是在AZ層,而且已經有了CRD來描述一個節點。同一個AZ管理着一個或者多個kubernetes集羣

圖4(點擊可查看大圖)

這裏的providervirtlet,專門管理VM的生命週期。如圖4所示,它主要的任務是根據ComputeNode,首先挑選一個kubernetes集羣,然後在這個集羣裏創建一個對應VM的POD,同步他們之間的狀態。對於用戶而言,下面的POD是不可見的,用戶通過創建或者刪除ComputeNode來管理VM

五、定製

Virtlet已經有了大部分所需要的功能,但還不能完全滿足需求,我們對以下方面做了定製和改進

  • VM網絡
  • NUMA Pin
  • VM的重啓、停止
  • OpenStack鏡像兼容
  • Virtlet的可靠性

1. VM網絡

01 Virtlet網絡管理

對於現有的eBay VM,都是橋接(bridge)網絡,只需要在libvirt domain指定目標橋接器,libvirt就會在啓動VM的時候創建一個TAP接口,並把這個接口連接到指定的橋接器。

然而對於kubernetes的VM,網絡接口是調用CNI插件先配置好,然後纔會創建和啓動VM。Virtlet引入了vmwrapper,它是所有VM啓動的入口,virtlet會把vmwapper設置成libvirt domain的入口(emulator)

圖5(點擊可查看大圖)

從圖5可見,啓動一個libvirt實例後,vmwrapper會被首先執行

  • Virtlet偵聽在一個unix domain socket上,並且virtlet已經打開了相應VM的TAP接口。
  • vmwrapper發起連接跟virtlet通信,拿到TAP接口的文件描述符(FD)
  • vmwrapper填寫好網絡相關的參數,最後啓動真正的qemu進程

qemu網絡參數舉例如下

這裏涉及到了進程間傳遞描述符(FD),virtlet使用的是Linux的SCM(Socket level control messages)方式

02 網絡改進

每一次CNI插件配置網絡,都會建一個新的網絡命名空間(network namespace),一般需要有一對veth pair來連接主機的網絡和新建的網絡空間。Virtlet默認VM網絡如圖6:

圖6(點擊可查看大圖)

連通一個VM,需要建一個bridge,一對veth pair,這樣網絡的性能會有一定的影響。

VM本身有強隔離性,我們又有自己的CNI插件實現,爲了減少網絡的路徑,創建VM的網絡不需要新的網絡命名空間。最終一個節點的網絡結構如圖7所示:

圖7(點擊可查看大圖)

03 Vhost net

Virtlet不支持vhost net,而eBay OpenStack上使用virtio的VM都有vhost net。增加這個功能,對每一個TAP接口,都需要有一個vhost net的描述符,這個描述符是通過打開/dev/vhost-net而來的。描述符的傳遞跟前面提到過的TAP描述符傳遞類似。

04 支持TSO(TCP Segment Offload)

在對virtlet的VM進行網絡性能測試的時候,網絡吞吐量只有OpenStack VM的一半,在VM裏面,soft IRQ負載很高,最終發現virtlet VM的網絡沒有打開TSO和TX/RX校驗

無論是Open Stack的VM還是virtlet的VM,都沒有顯示配置這個選項,但爲什麼OpenStack的VM默認是打開的呢?

前面提到,常規VM的網絡接口是libvirt創建和刪除的,而virtlet的VM網絡接口是由CNI創建、virtlet管理的。通過閱讀libvirt和qemu的源代碼,libvirt在打開TAP接口的時候會加一個IFF_VNET_HDR的選項,qemu檢查到這個選項後, VM實例的網絡接口就會默認打開TSO。通過這個小小的改動,virtlet VM的網絡吞吐量跟OpenStack的VM旗鼓相當

2. NUMA Pin

eBay數據中心的服務器都有2個或以上的NUMA節點,在這些節點上運行的VM,需要把他們儘量固定在NUMA節點上,否則跨NUMA節點的訪問會帶來性能問題。Kubernetes有CPU manager這個功能,CPU的分配也顧及到了機器上的NUMA節點,但不能完全滿足要求,原因如下

  • CPU是獨佔的。也就是一個CPU被分配給一個POD後,其它任何POD就不能再分配到這個CPU了,這樣不能做到超售。
  • CPU的分配可能會跨NUMA節點,但並不平衡。比如一個POD需要8個CPU,那就有可能2個CPU分配在一個NUMA節點,另外的6個CPU分配在另一個NUMA節點,導致了不平衡。

基於以上原因,我們沒有使用CPU manager這個功能,而是在virtlet裏增加了一個模塊來管理NUMA節點的分配和釋放(這也是virtlet有自己的CRI實現的好處之一)

  • 固定VM所運行的NUMA節點,但不固定VM運行在NUMA節點對應的CPU。比如一臺機器有2個NUMA節點,CPU 0-15NUMA 0CPU 16-31NUMA 1,如果申請一個4核的VM,那這個VM只會運行在其中的一個NUMA節點上,但VM能用到的CPU可以是這個NUMA節點上所有的CPU(當然這只是標準的VM,對於有高性能要求的VM,既要有NUMA固定也需要有CPU固定)。
  • 對於NUMA的分配,採取的是CPU、內存最平衡的方式,也就是在一個NUMA節點上,在分配量不超過某個閾值的前提下,計算已經分配的CPU和內存,加上將要被分配的VM需要的CPU和內存,CPU分配量和內存分配量差值的絕對值哪個最小,哪個優先級就最高

3. VM的重啓和停止

Virtlet沒有提供VM重啓(reset)和停止(stop),但eBay有些團隊需要這樣的功能。前面提到,virtlet的VM就是一個普通的POD,在kubernetes裏如果一個POD不是在運行(running)狀態,kubernetes就會不斷重試,去把POD帶回到運行狀態。因此需要一個單獨的模塊來管理VM的狀態

  • 綜合當前VM的實際狀態和用戶所期望的狀態,做到最後狀態一致。
  • 增加一個POD的註解,裏面包含所**需要(request)的狀態和實際(status)**狀態。
  • 在給kubelet彙報VM狀態的時候,如果用戶顯示的是該VM已停止,仍然要向kubelet彙報是運行狀態,這樣kubelet就不會反覆地調用啓動的接口。

4. OpenStack鏡像兼容

eBay運行着很大規模的OpenStack集羣,有許多已有的VM鏡像,virtlet必須能無縫地使用這些已有的鏡像。

在eBay,絕大部分OpenStack VM的網絡信息是通過configdrive靜態注入的方式,由cloutinit來完成網絡的配置的,雖然virtlet的文檔裏聲稱支持configdrive,但他們使用的是不同的configdirve的版本,大部分的現有鏡像都不能正確拿到configdrive所注入的信息。

因此需要實現另一個版本的configdrive,甚至還能兼容Windows VM。滿足這兩個需求,有兩個關鍵的地方

  • Configdrive盤必須有config-2的標籤。
  • Configdrive必須是vfat格式的(雖然configdrive可以是vfat或者iso,但某些Windows的cloudinit不能識別ISO格式)。

六、可靠性

儘管VM的生命週期用kubernetes來管理,但VM和容器還是有不同的地方:在VM的生命週期之內,不管是POD重啓、virtlet重啓以及節點重啓,VM的所有數據不能有任何丟失;而容器在重啓之後數據就會消失(不包括host path)

Virtlet要能用在生產環境,必須做到數據不丟失。還有節點上的VM能夠脫離virtlet運行良好,這樣才能給virtlet升級。從一開始,我們就非常關注virtlet的可靠性,也找到了virtlet可靠性所存在的一些問題

  • 升級virtlet本身,所有的VM也莫名其妙地消失了。
  • 重啓節點,VM所有的數據丟失。
  • 停止VM和virtlet,然後再啓動virtlet和VM,VM再也不能啓動。

如果以上的問題不能解決,virtlet就不能用在生產環境,需要用其它方案甚至重新開發一套來代替。幸運的是通過積極閱讀代碼和調試,我們解決了上面的問題,社區也接受了這些補丁

對於基於virtlet的一些改進和定製,需要保證每次改動不影響已有的功能,從一開始我們就開發了集成測試端到端測試來保證可靠性

七、性能

有了可靠的保障,性能也必須達標,參照物就是已有的OpenStack VM

  • 我們使用各種基準測試工具對相同CPU、內存以及磁盤的virtlet VM和OpenStack VM進行測試。
  • 使用生產環境的鏡像流量來比較兩者的CPU和內存使用率,以及在每秒時間內能夠處理的事務(TPS Transaction Per Second)

比較下來的結果是兩者沒有差距,達到了我們的預期和要求

八、結語

Kubernetes愈來愈受各家公司重視,這是未來幾年甚至數十年雲平臺的趨勢。但是從老的平臺過渡到新的平臺需要時間,一刀切的方式會帶來無法遇見的風險和額外的耗時,特別是像eBay這樣有多樣產品和大體量的雲平臺公司。用kubernetes來管理VM使這種過渡成爲了可能,既不需要同時維護兩套平臺,也減少了新老交替帶來的風險

本文轉載自公衆號eBay技術薈(ID:eBayTechRecruiting)

原文鏈接

https://mp.weixin.qq.com/s/CZzD1qf9QSGW9F5rsg13-Q

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章