- 過去IaaS層在網絡方便作了不少工做,已經造成了成熟的環境,若是和容器環境適配,兩邊都須要作不少改造
- 容器時代提倡微服務,致使容器的粒度之小,離散程度之大,原有IaaS層網絡解決方案很難承載如此複雜的需求
咱們來看下一些主流的容器網絡接入方案:
Host network
最簡單的網絡模型就是讓容器共享Host的network namespace,使用宿主機的網絡協議棧。這樣,不須要額外的配置,容器就能夠共享宿主的各類網絡資源。
共享物理網卡
這種接入方式主要使用SR-IOV技術,每一個容器被分配一個VF,直接經過PCIe網卡與外界通訊。SR-IOV 技術是一種基於硬件的虛擬化解決方案,可提升性能和可伸縮性。
SR-IOV 標準容許在虛擬機之間高效共享 PCIe設備,而且它是在硬件中實現的,能夠得到可以與本機性能媲美的 I/O 性能。啓用了 SR-IOV 而且具備適當的硬件和 OS 支持的 PCIe 設備(例如以太網端口)能夠顯示爲多個單獨的物理設備,每一個都具備本身的 PCIe 配置空間。
SR-IOV主要用於虛擬化中,固然也能夠用於容器:
SR-IOV配置(須要網卡支持,線上主流萬兆卡Intel 540能夠支持128個VF(Virtual Function)):
modprobe ixgbevf
lspci -Dvmm|grep -B 1 -A 4 Ethernet
echo 2 > /sys/bus/pci/devices/0000:82:00.0/sriov_numvfs
# check ifconfig -a. You should see a number of new interfaces created, starting with 「eth」, e.g. eth4
Intel也給Docker實現了一個[SR-IOV network plugin](
https://github.com/clearcontainers/sriov "SR-IOV network plugin"),一樣也有相應的[CNI(後面會提到)Plugin](
https://github.com/Intel-Corp/sriov-cni "CNI(後面會提到)Plugin")。SR-IOV的接入方式能夠達到物理網卡的性能,可是須要硬件支持,並且VF的數量是有限的。
共享容器網絡
多個容器共享同一個netns,只須要第一個容器配置網絡。好比Kubernetes Pod就是全部容器共享同一個pause容器的網絡。
VSwitch/Bridge
容器擁有獨立的network namespace,經過veth-pair鏈接到VSwitch或者Bridge上。
容器網絡技術突飛猛進,有些至關複雜,並且提供某種功能的方式也多種多樣。這樣就給容器runtime與調度系統的實現與各類接入方式的適配帶來的很大的困難。爲了解決這些問題,Docker與CoreOS相繼發佈了容器網絡標準,即是CNM與CNI,爲Linux容器網絡提供了統一的接口。
先來看下CNM:
Libnetwork是CNM的原生實現。它爲Docker daemon和網絡驅動程序之間提供了接口。網絡控制器負責將驅動和一個網絡進行對接。每一個驅動程序負責管理它所擁有的網絡以及爲該網絡提供的各類服務,例如IPAM等等。由多個驅動支撐的多個網絡能夠同時並存。網絡驅動能夠按提供方被劃分爲原生驅動(libnetwork內置的或Docker支持的)或者遠程驅動 (第三方插件)。原生驅動包括 none、bridge、overlay 以及 MACvlan。驅動也能夠被按照適用範圍被劃分爲本地(單主機)的和全局的 (多主機)。
- Sandbox:一個Sandbox對應一個容器的網絡棧,可以對該容器的interface、route、dns等參數進行管理。一個Sandbox中能夠有多個Endpoint,這些Endpoint能夠屬於不一樣的Network。Sandbox的實現能夠爲linux network namespace、FreeBSD Jail或其餘相似的機制。
- Endpoint: Sandbox經過Endpoint接入Network,一個Endpoint只能屬於一個Network。Endpoint的實現能夠是veth pair、Open vSwitch internal port或者其餘相似的設備。
- Network:一個Network由一組Endpoint組成,這些Endpoint彼此間能夠直接進行通訊,不一樣的Network間Endpoint的通訊彼此隔離。Network的實現能夠是linux bridge、Open vSwitch等。
咱們能夠以Docker操做爲例,簡單看下CNM建立容器的工做流:
- Create Network(docker network create)
- IpamDriver.RequestPool:建立subnetpool用於分配IP
- IpamDriver.RequestAddress:爲gateway獲取IP
- NetworkDriver.CreateNetwork:建立network和subnet
- Create Container(docker run / docker network connect)
- IpamDriver.RequestAddress:爲容器獲取IP
- NetworkDriver.CreateEndpoint:建立port
- NetworkDriver.Join:爲容器和port綁定
- NetworkDriver.ProgramExternalConnectivity
- NetworkDriver.EndpointOperInfo
CNM與libnetwork相對比較複雜,這裏不作詳細介紹,相關內容可參考libnetwork的[Design Doc](
https://github.com/docker/libn ... gn.md "Design Doc")。
再看下CNI:
其基本思想爲:Container Runtime在建立容器時,先建立好network namespace,而後調用CNI插件爲這個netns配置網絡,其後再啓動容器內的進程。
CNI插件包括兩部分:
- CNI Plugin負責給容器配置網絡,它包括兩個基本的接口
- 配置網絡:AddNetwork(net NetworkConfig,rt RuntimeConf)(types.Result,error)
- 清理網絡:DelNetwork(net NetworkConfig, rt RuntimeConf) error
- IPAM Plugin負責給容器分配IP地址,實現包括host-local和dhcp等
再來看下CNI建立容器的工做流:
- 容器runtime分配一個網絡命名空間以及一個容器ID
- 連同一些CNI配置參數傳給網絡驅動
- 網絡驅動會將該容器鏈接到網絡並將分配的IP地址以JSON的格式返回給容器runtime
全部CNI插件均支持經過環境變量和標準輸入傳入參數:
$ echo '{"cniVersion": "0.3.1","name": "mynet","type": "macvlan","bridge": "cni0","isGateway": true,"ipMasq": true,"ipam": {"type": "host-local","subnet": "10.244.1.0/24","routes": [{ "dst": "0.0.0.0/0" }]}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=eth0 CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/bridge
$ echo '{"cniVersion": "0.3.1","type":"IGNORED", "name": "a","ipam": {"type": "host-local", "subnet":"10.1.2.3/24"}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=a CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/host-local
直觀的來看,CNI 的規範比較小巧。它規定了一個容器runtime和網絡插件之間的簡單的契約(詳細內容請參考[SPEC](
https://github.com/containerne ... EC.md "SPEC"))。
在斟酌某項技術的時候,咱們須要考慮採納的成本;是否會增長對供應商的依賴。社區的採納程度和支持程度也是比較重要的考量。插件開發者還須要考慮哪一種方式相對比較容易實現。
目前,主流容器調度框架(Mesos、Kubernetes)與網絡解決方案(Calico、Flannel等)都對CNI有良好的支持,另外其簡單的接口使插件的開放成本很低;而且不依賴docker runtime,使容器解決方案有了更多選擇(例如Mesos Containerizer)。因此,咱們選擇了CNI。
在對比業內支持CNI接口的主流網絡解決方案以前,須要對多主機條件下,容器的組網方案作下分類,來方便你們理解不一樣解決方案之間的區別:
Flat
- L2 Flat
- 各個host中容器在虛擬與物理網絡造成的VLAN(大二層)中
- 容器能夠在任意host間遷移不用改變其IP
- 不一樣租戶IP不可Overlap
- L3 Flat
- 各個host中容器在虛擬與物理網絡中可路由,且爲32位路由
- 由於是32位路由,因此容器在任意host間遷移不用改變IP
- 不一樣租戶IP不可Overlap,容器與物理網絡IP也不可Overlap
L3 Hierarchy
- 各個host中容器在虛擬與物理網絡中可路由
- 路由在不一樣層次上(VM/Host/Leaf/Spine)以聚合路由的形式存在
- 相同CIDR的容器需在物理上被組織在一塊兒
- 容器在host間遷移須要改變IP
- 不一樣租戶IP不可Overlap,容器與物理網絡IP也不可Overlap
Overlay
- L2 over L3
- 容器可跨L3 Underlay進行L2通訊
- 容器可在任意host間遷移而不改變其IP
- L3 over L3
- 容器可跨L3 Underlay進行L3通訊
- 容器可在任意host間遷移可能須要改變其IP
- 不一樣租戶IP可Overlap,容器IP也可與Underlay網絡Overlap
下面咱們來看下社區比較承認的幾種容器網絡解決方案。
Flannel
經過給每臺宿主機分配一個子網的方式爲容器提供虛擬網絡,它基於Linux TUN/TAP,使用UDP封裝IP包來建立overlay網絡,並藉助etcd維護網絡的分配狀況。
控制平面上host本地的flanneld負責從遠端的ETCD集羣同步本地和其它host上的subnet信息,併爲POD分配IP地址。數據平面flannel經過Backend(好比UDP封裝)來實現L3 Overlay,既能夠選擇通常的TUN設備又能夠選擇VxLAN設備。
從上圖能夠看出,Flannel是典型的L3 over L3組網方案。
支持的Backend:
- udp:使用udp封裝,默認使用8285端口
- vxlan:vxlan封裝,須要配置VNI,Port(默認8472)和GBP
- host-gw:直接路由的方式
- 公有云vpc:aws-vpc、gce、ali-vpc
如上圖,能夠方便的與Docker集成:
source /run/flannel/subnet.env
docker daemon --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} &
也提供CNI接口:
CNI插件會將flannel網絡配置轉換爲bridge插件配置,並調用bridge插件給容器netns配置網絡。好比下面的flannel配置:
{
"name": "mynet",
"type": "flannel",
"delegate": {
"bridge": "mynet0",
"mtu": 1400
}
}
會被插件轉換爲:
{
"name": "mynet",
"type": "bridge",
"mtu": 1472,
"ipMasq": false,
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.1.17.0/24"
}
}
Flannel即是經過CNI與Mesos、Kubernetes集成。因爲歷史緣由,咱們線上Kubernetes集羣是經過前一種方式直接與Docker集成的。
優勢:
- 配置安裝簡單,使用方便
- 與公有云集成方便,VPC方式無Overhead
缺點:
- Vxlan模式對平滑重啓支持很差(重啓須要數秒來刷新ARP表,且不可變動配置,例如VNI、iface)
- 功能相對簡單,不支持Network Policy
- Overlay存在必定Overhead
Weave
不一樣於其它的multi-host方案,其支持去中心化的控制平面,各個host上的wRouter間經過創建Full Mesh的TCP連接,並經過Gossip來同步控制信息。這種方式省去了集中式的K/V Store,可以在必定程度上減低部署的複雜性。不過,考慮到docker libnetwork是集中式的K/V Store做爲控制平面,所以Weave爲了集成docker,它也提供了對集中式控制平面的支持,可以做爲docker remote driver與libkv通訊。
數據平面上,Weave經過UDP封裝實現L2 Overlay,封裝支持兩種模式:
- 運行在user space的sleeve mode:經過pcap設備在Linux bridge上截獲數據包並由wRouter完成UDP封裝,支持對L2 traffic進行加密,還支持Partial Connection,可是性能損失明顯。
- 運行在kernal space的 fastpath mode:即經過OVS的odp封裝VxLAN並完成轉發,wRouter不直接參與轉發,而是經過下發odp 流表的方式控制轉發,這種方式能夠明顯地提高吞吐量,可是不支持加密等高級功能。
- 全部容器都鏈接到Weave網橋
- weave網橋經過veth pair連到內核的OpenVSwitch模塊
- 跨主機容器經過openvswitch vxlan通訊
- policy controller經過配置iptables規則爲容器設置網絡策略
關於Service的發佈,Weave作的也比較完整。首先,wRouter集成了DNS功能,可以動態地進行服務發現和負載均衡,另外,與libnetwork 的overlay driver相似,weave要求每一個容器有兩個網卡,一個就連在lb/ovs上處理L2 流量,另外一個則連在docker0上處理Service流量,docker0後面仍然是iptables做NAT。同事,提供了一個容器監控和故障排查工具Weave Scope,能夠方便的生成整個集羣的拓撲並智能分組。
優勢:去中心化、故障自愈、加密通信、支持組播
缺點:
- sleeve mode性能損耗巨大
- full mesh模型限制了集羣規模
- Partial Connection在fastpath未實現
Contiv
思科開源的容器網絡方案,是一個用於跨虛擬機、裸機、公有云或私有云的異構容器部署的開源容器網絡架構,並與主流容器編排系統集成。Contiv最主要的優點是直接提供了多租戶網絡,並支持L2(VLAN),L3(BGP),Overlay(VXLAN)以及思科自家的ACI。
主要由兩部分組件組成:
- Netmaster
- 對外提供REST API
- 學習並分發路由到Netplugin
- 管理集羣資源(IP、VLAN、VXLAN ID等)
- Netplugin
- 運行在集羣中每一個節點上
- 實現了CNI與CNM接口
- 經過REST方式與Netmaster通信,監聽事件,並作相應網絡配置
- 數據平面使用OVS實現(插件架構,可替換,例如VPP、BPF)
優勢:
- 支持多種組網方式,集成現有SDN方案
- 多租戶網絡混部在同一臺主機上
- 插件化的設計,可替換的數據平面
- 即時生效的容器網絡Policy/ACL/QoS規則
- 基於OVS的高性能Load Balancer的支持
- 清晰的設計,方便二次開發
缺點:
- 功能豐富帶來的反作用即是配置和使用比較複雜
- 由於發展時間較短,社區和文檔都不怎麼成熟,也存在一些bug,穩定性有待商榷
- CNI plugin對Mesos支持不友好(跟Cisco官方溝通後,對Mesos支持仍在POC階段)
OpenContrail
是Juniper推出的開源網絡虛擬化平臺,其商業版本爲Contrail。
OpenContrail主要由控制器和vRouter組成:
- 控制器提供虛擬網絡的配置、控制和分析功能
- vRouter提供分佈式路由,負責虛擬路由器、虛擬網絡的創建以及數據轉發
vRouter支持三種模式:
- Kernel vRouter:相似於ovs內核模塊
- DPDK vRouter:相似於ovs-dpdk
- Netronome Agilio Solution (商業產品):支持DPDK, SR-IOV and Express Virtio (XVIO)
從上圖能夠看出,OpenContrail的架構是至關複雜的。相應的,其提供的feature基本覆蓋了前全部提到的方案;甚至包括基於Cassandra後端的流量採樣與分析平臺;因爲篇幅所限,這裏不作詳細介紹,相關內容可參考[Arch Doc](
http://www.opencontrail.org/op ... tion/ "Arch Doc")。
計算機科學的先驅David Wheeler曾經說過——「Any problem in computer science can be solved with another level of indirection.」, SDN便是Network的indirection。容器網絡解決方案多不勝數,咱們在這裏就不一一介紹了。可是咱們能夠看到,每種方案其實都是對network作了不一樣程度的indirection,可是不要忘了後面還有一句——「except of course for the problem of too many indirections」,因此你們在不一樣的方向上作出了取捨與權衡。正如前面所提到的,在斟酌某項技術的時候,咱們須要考慮採納成本、對供應商的依賴、社區的支持程度等等。咱們正在如此權衡以後,並沒選擇上面的方案,而是選擇了Calico,下面我會詳細說明。
Calico
[Calico](
https://www.projectcalico.org/ "Calico") 是一個純三層的數據中心網絡方案(不須要Overlay),而且與OpenStack、Mesos、Kubernetes、AWS、GCE等IaaS和容器平臺都有良好的集成。
Calico是一個專門作數據中心網絡的開源項目。當業界都癡迷於Overlay的時候,Calico實現multi-host容器網絡的思路確能夠說是返璞歸真——pure L3,pure L3是指容器間的組網都是經過IP來完成的。這是由於,Calico認爲L3更爲健壯,且對於網絡人員更爲熟悉,而L2網絡因爲控制平面太弱會致使太多問題,排錯起來也更加困難。那麼,若是可以利用好L3去設計數據中心的話就徹底沒有必要用L2。
不過對於應用來講,L2無疑是更好的網絡,尤爲是容器網絡對二層的需求則更是強烈。業界廣泛給出的答案是L2 over L3,而Calico認爲Overlay技術帶來的開銷(CPU、吞吐量)太大,若是能用L3去模擬L2是最好的,這樣既能保證性能、又能讓應用滿意、還能給網絡人員省事,看上去是件一舉多得的事。用L3去模擬L2的關鍵就在於打破傳統的Hierarchy L3概念,IP再也不之前綴收斂,你們乾脆都把32位的主機路由發佈到網絡上,那麼Flat L3的網絡對於應用來講即和L2如出一轍。
這個思路不簡單,刨了L2存在必要性的老底兒,實際上若是不用考慮可擴展性、隔離性,IP地址和MAC地址標識endpoint的功能上確實是徹底冗餘的,即便考慮可擴展性,一個用L3技術造成的大二層和一個用L2技術造成的大二層並無本質上的差距。並且,L3有成熟的、完善的、被廣泛承認接受的控制平面,以及豐富的管理工具,運維起來要容易的多。
因而,Calico給出了下面的設計:
Calico在每個計算節點利用Linux Kernel實現了一個高效的vRouter來負責數據轉發,而每一個vRouter經過BGP協議負責把本身上運行的workload的路由信息向整個Calico網絡內傳播—,路由條目全是/32的v4或者/128的v6。小規模部署能夠直接互聯,大規模下可經過指定的BGP route reflector來完成。 這樣保證最終全部的workload之間的數據流量都是經過IP路由的方式完成互聯的。Calico節點組網能夠直接利用數據中心的網絡結構(不管是L2或者L3),不須要額外的NAT,隧道或者Overlay Network。
此外,Calico基於iptables還提供了豐富而靈活的網絡Policy,保證經過各個節點上的ACLs來提供Workload的多租戶隔離、安全組以及其餘可達性限制等功能。另外,對CNM與CNI的支持使其能夠方便適配絕大多數場景。
這裏有必要說明下,CNI和CNM並不是是徹底不可調和的兩個模型。兩者能夠進行轉化的,calico項目最初只支持CNI,後面才加入CNM的支持。從模型中來看,CNI中的container應與CNM的sandbox概念一致,CNI中的network與CNM中的network一致。在CNI中,CNM中的endpoint被隱含在了ADD/DELETE的操做中。CNI接口更加簡潔,把更多的工做託管給了容器的管理者和網絡的管理者。從這個角度來講,CNI的ADD/DELETE接口其實只是實現了docker network connect和docker network disconnect兩個命令。相似的,[kubernetes/contrib](
https://github.com/kubernetes/ ... ocker "kubernetes/contrib")項目提供了一種從CNI向CNM轉化的過程,這裏就不作詳細介紹了。
咱們詳細看下Calico中的各個組件:
- Felix:Calico Agent,跑在每臺須要運行Workload的節點上,主要負責經過iptables來配置ACLs
- etcd:分佈式鍵值存儲,主要負責網絡元數據一致性,確保Calico網絡狀態的準確性
- confd:根據etcd上狀態信息,與本地模板,生成並更新BIRD配置
- BGP Client(BIRD):主要負責容器的路由信息分發到當前Calico網絡,確保Workload間的通訊的有效性
- BGP Route Reflector(BIRD):大規模部署時使用,摒棄全部節點互聯的 mesh 模式,經過一個或者多個BGP Route Reflector來完成集中式的路由分發
這個架構,技術成熟、高性能、易維護,看起來是生產級別的容器網絡環境最好的選擇。可是,也有不如意的地方:
- 沒有了外面的封裝,就談不上VRF,多租戶的話地址無法Overlap
- L2和L3的概念模糊了,那麼network級別的安全就難以實現,port級別的安全實現難是由於須要的規則都是1:1的,數量太多
上面並非什麼嚴重的問題。可是有一點,Calico控制平面的上述設計中,物理網絡最好是L2 Fabric,這樣vRouter間都是直接可達的,路由不須要把物理設備當作下一跳。若是是L3 Fabric,控制平面的問題就來了:物理設備若是要存32位的路由,路由表將變得巨大無比。
所以,爲了解決以上問題,Calico不得不採起了妥協,爲了支持L3 Fabric,Calico推出了IPinIP的選項,用做cross-subnet下容器間通信,可是有必定性能損耗。另外一種方案是回退到L3 Hierarchy(calico目前支持),若是對容器遷移保持IP不變的需求不大,能夠選擇這種,這也是咱們最後選擇的方案。不過,對於使用L2 Fabric、沒有多租戶需求的企業來講,用calico上生產環境應該最理想選擇。
Calico最理想的部署方式是基於L2 Fabrics的,官方也有相應說明([Calico over an Ethernet interconnect fabric](
http://docs.projectcalico.org/ ... abric "Calico over an Ethernet interconnect fabric")),這裏就不作詳細說明了。因爲咱們數據中心規模比較大,顯然L2 Fabrics不是理想的選擇。事實也是如此,咱們多個數據中心組成的網絡是以BGP爲控制層面組成的L3 Fabrics。每一個數據中心爲一個AS。這裏不討論跨數據中心的問題,你們只需知道咱們跨數據中心在L3上是互通的即可以。
官方給出了3種基於L3 Fabrics的組網建議(詳細介紹可點擊進去查看):
AS per Rack這種模型在每一個ToR下加入了RR(Route Reflector),關於RR,前面有提到過,這裏有必要解釋一下:
首先說下BGP的水平分割原則(這裏只談IBGP),從IBGP學習到的路由毫不對再傳播給其它的IBGP鄰居(能夠傳給EBGP鄰居)。IBGP水平分割主要是爲了防止在AS內部產生路由環路。因此,爲了AS內路由正確收斂,須要全部節點組成full mesh結構(也就是兩兩之間創建鄰居)。可是,這種架構在大型網絡中顯然行不通。
另外一種解決方案即是RR,一種C/S模型。在同AS內,其中一臺設備做爲RR,其餘設備做爲Client。Client與RR之間創建IBGP鏈接。路由反射器和它的客戶機組成一個Cluster。這裏不詳細討論RR的工做原理。顯然,路由反射破壞了水平分割的原則,因此便要許多額外的配置來防止環路,這會給實際部署過程帶來不少負擔。另外,咱們同一數據中心內運行的是IBGP,也就是都在同AS內。這使的配置改造更爲複雜,並且,顯然不符合AS per Rack。
另外兩種模型一樣要求每一個ToR在不一樣的AS,也面臨上述問題。另外,若是不在ToR上作路由聚合,AS per Compute Server會形成Linux服務器上有全網的路由,龐大的路由表也會成爲性能的瓶頸。
通過與網絡部門的認真研討,咱們最終選擇了下面的部署模型:
- 每一個Server選擇不一樣的AS號,且與數據中心AS不一樣,與ToR創建EBGP鄰居
- Server使用原有網關(默認路由),ToR不向Server宣告任何路由條目
- Server本地聚合路由到28位,並宣告給ToR
- 與網絡部門制定必定算法(方便自動化),用Server的IP地址計算出AS號
能夠看到,上述方案,幾乎不用對數據中心現有網絡架構作出改變;並且,也大大減小了Server與ToR上路由條目數。
實際部署Calico的Agent時,咱們還遇到了一些問題:官方默認只提供基於Docker的部署方式,也就是以前提到的幾個組件都部署在一個Docker容器中。calicoctl也是基於Docker容器作部署操做。可是,實際環境中,咱們線上的Mesos集羣有部分是隻是用Mesos Containerizer的,也就是並無Docker環境;並且,咱們認爲,Calico Agent加入對Docker daemon的依賴,考慮到穩定性,顯然不是一個良好的選擇。
因此,咱們對Calico組件進行拆解,從新拼裝,並去掉咱們用不到IPv6模塊;最終經過systemd在每臺server上運行bird、confd、felix這三個模塊,實現了去Docker化的部署(實際上對confd的模板也作了些改造,去掉了IPinIP的支持,而且適配的非Docker環境)。整個過程基於Ansible,實現了一鍵部署。
關於性能問題,雖然Calico在衆多基於軟件的容器網絡解決方案裏是性能最好的,可是也不是徹底沒有性能損耗。前面說過,Calico實現namespace內外通信是經過linux內核的veth pair來實現的(就是以前提到的接入方式中的VSwitch/Bridge)。確實增長了一層overhead,可是好在veth pair的實現比較簡單,性能損耗幾乎能夠忽略(無policy條件下)。咱們實際測試中,對於echo server(小報文測試),請求延遲會增長0.02到0.04ms;對於bandwidth(大報文測試,萬兆環境),很難看到差異。
到了這一步,你可能以爲咱們大功告成,實則否則。其實咱們還有不少事情能夠作,也須要去作。Calico支持豐富的安全策略,可是在server上是用kernel的netfilter實現的(iptables),大量policy下的性能仍有待考察。
業內也有一些其餘的實現思路,例如Google大力支持cilium方案,其policy的實現就用了BPF(Berkeley Packet Filter,最先在kernel 3.15中加入,4.8版本之後標爲stable)。BPF的基本思路是把用做包處理的代碼經過llvm編譯成ir,而後插入到正在運行的kernel網絡棧中,經過jit方式執行,大幅度提升了處理能力。這裏不作詳細介紹,相關信息能夠看這裏。cilium還加入了對XDP的支持(一種相似DPDK的技術,可是目前集成在kernel中),來加速網絡棧的處理。這些都不失爲將來改造calico的一個方向。
另一個問題是,calico目前並無對traffic shaping的支持。試想,當大量容器運行與同一物理機,其進出流量都共享物理網卡,若是不對容器流量進行限制,單個容器過度使用網絡資源就會影響其餘容器提供服務(磁盤IO也有一樣問題,可是不在本文討論範圍)。Calico社區目前也在討論這個問題,詳情可見這個issue,目前還在API制定的階段。
上述問題,底層實現目前可行選擇是linux自帶的tc(OVS一樣用到了tc)。上層切入點卻有兩個選擇,一是拓展Calico的API,在felix上實現應用tc規則的邏輯;另外一種是在CNI plugin上hook,經過相應的調度系統,傳參數進來,由plugin來應用tc規則。因爲,calico社區已經在討論這個問題,並且第二種方案實現起來成本也比較低。現階段,咱們只考慮方案二,也作了相應的POC。
實踐過程當中,因爲veth pair兩端的流量是相反的(ingress與egress),理論上能夠在兩端的egress上應用tc規則(tc只支持egress方向的shaping),來實現兩個方向上的QoS。可是,咱們仍是遇到了一些問題,namespace內的虛擬網卡上的tc規則雖然可配置,可是並不生效。並且,這些規則對容器內應用是可見、可修改的,這樣作也並不安全。因此,咱們用到了ifb內核模塊,把物理機上虛擬網卡的ingress流量變成egress,最終在在ifb網卡上應用tc規則,來實現容器egress方向的QoS。關於ifb,功能比較簡單,你們能夠看下kernel代碼中相關的介紹:
/* drivers/net/ifb.c:
The purpose of this driver is to provide a device that allows
for sharing of resources:
1) qdiscs/policies that are per device as opposed to system wide.
ifb allows for a device which can be redirected to thus providing
an impression of sharing.
2) Allows for queueing incoming traffic for shaping instead of
dropping.
The original concept is based on what is known as the IMQ
driver initially written by Martin Devera, later rewritten
by Patrick McHardy and then maintained by Andre Correa.
You need the tc action mirror or redirect to feed this device
packets.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version
2 of the License, or (at your option) any later version.
Authors: Jamal Hadi Salim (2005)
*/
迫於篇幅限制,此次就先到這裏吧,後續我會把一些詳細實現拿出來給你們分享,歡迎共同交流。
看了上面這麼多,有人可能會問,咱們作了這麼多工做,到底是爲了什麼。回過頭來看今天,咱們理所固然的用着方便的跨數據中心環網、接入負載均衡、共享存儲、對象存儲,甚至到上層的雲平臺等等。正是由於有了這些,業務們拿出編譯好的代碼,快的不到幾分鐘就能夠被線上的用戶所使用。
這就是基礎設施,這些是這座大樓的地基。正由於如此,基礎設施的改造才異常困難。咱們須要用合理的抽象來解決頑疾,更須要對上層建築的兼容。正如咱們在網絡方面所作的工做,讓容器和現有物理設備在三層上的互通,實現了網絡上對業務的透明;同時又提供了良好的性能,消除了業務遷移到雲平臺上的顧慮;在提升資源利用率的混部環境下,同時提供可配置的策略,爲服務質量提供了有效的保障。