k8s网络架构图_唯品会基于Kubernetes(k8s)网络方案演进
VIP PaaS在接近兩年時間里,基于kubernetes主要經(jīng)歷四次網(wǎng)絡(luò)方案的變遷:
1. kubernetes + flannel
2. 基于Docker libnetwork的網(wǎng)絡(luò)定制
3. kubernetes + contiv + kube-haproxy
4. 應(yīng)用容器IP固定
先簡單說一下背景,PaaS平臺的應(yīng)用管理包括應(yīng)用配置管理,應(yīng)用的運(yùn)行態(tài)管理。一個應(yīng)用的運(yùn)行態(tài)對應(yīng)kubernetes的一個Replication Controller(后面使用RC簡稱)和一個Service,應(yīng)用實例對應(yīng)kubernetes中的Pod, 我們基于這樣的管理方式,需要提供應(yīng)用之間的相互調(diào)用,同時對部分應(yīng)用要提供基于http/tcp的直接訪問。
首先說一下kubernetes + flannel。
flannel主要提供了跨主機(jī)間的容器通信;
在kubernetes的Pod、Service模型里,kube-proxy又借助iptables實現(xiàn)了Pod和Service間通信。
基于這種網(wǎng)絡(luò)訪問功能,我們平臺提供了以下功能:
基于gorouter提供的平臺域名的訪問 – watch k8s endpoints event管理router信息;
基于skydns并定制化kube2sky組件和kubelet,提供同一命名空間下應(yīng)用(Pod)之間基于業(yè)務(wù)域名的訪問 – kube2sky基于k8s Service annotation解析并注冊域名信息、kubelet設(shè)置容器啟動時的domain search及外部dns;
實現(xiàn)容器tty訪問控制臺 – 每臺k8s node部署平臺組件 tty agent(根據(jù)Pod所屬node信息, 建立對應(yīng)k8s結(jié)點(diǎn)的tty連接);
網(wǎng)絡(luò)訪問關(guān)系圖如下:
在k8s + flannel的模型下,容器網(wǎng)絡(luò)是封閉子網(wǎng),可以提供平臺內(nèi)部應(yīng)用之間基于4層和7層的調(diào)用,同時對外部提供應(yīng)用基于域名(工作在七層)的直接訪問,但無法滿足用戶在平臺外部需要直接使用IP訪問的需求。
在flannel網(wǎng)絡(luò)穩(wěn)定使用后,開始研究network plugin以使應(yīng)用服務(wù)實例以public IP 方式供用戶直接使用。
當(dāng)時docker的版本為1.8, 本身還不支持網(wǎng)絡(luò)插件.同時 kubernetes本身提供一套基于CNI的網(wǎng)絡(luò)插件, 但本身有bug[CNI delete invoked twice with non-infra container id #20379]。
于是我們嘗試從docker network plugin的角度入手,結(jié)合libnetwork從docker源碼的角度進(jìn)行定制。
整個架構(gòu)分為三層:
Client Layer – Docker CLI和kubernetes(Docker client);
Docker Layer – Docker daemon 并在代碼層面集成libnetwork(內(nèi)置OVS driver);
Controller Layer – ovsdb-server及network controller(自開發(fā)IPAM);
整體訪問結(jié)構(gòu)圖:
整個方案包括以下三個流程:
1. 啟動Docker Daemon:
初始化network controller -> 加載OVS Driver -> OVS Driver調(diào)用libovsdb創(chuàng)建docker0-ovs Bridge -> OVS Driver將主機(jī)上的一物理網(wǎng)卡attach到docker0-ovs上;
2. 啟動容器:
OVS Driver 創(chuàng)建veth pair 用于連接network namespaces -> OVS Driver調(diào)用network controller獲取容器IP和VLAN Tag -> OVS Driver將veth pair的一端添加到docker0-ovs上,并設(shè)置VLAN Tag -> OVS Driver設(shè)置容器內(nèi)interface的IP,Mac Address以及路由 -> 設(shè)置各network interface為up;
3. 停止容器:
OVS Driver調(diào)用network controller釋放容器IP -> 刪除network link -> OVS Driver調(diào)用libovsdb刪除port;
libnetwork工作完成了測試階段但沒有經(jīng)歷上線,隨著Docker版本的推進(jìn),Docker1.9開始支持 contiv netplugin,我們開始研究contiv應(yīng)用,在期間我們也完成了使用haproxy替換kube-proxy的開發(fā)[https://github.com/AdoHe/kube2haproxy],并最后采用docker1.10+contiv上線。
這里根據(jù)我們實際網(wǎng)絡(luò)訪問關(guān)系再描述下PaaS在contiv整體部署結(jié)構(gòu):
Kube-haproxy替代了kube-proxy,主要是提供服務(wù)ip的公共調(diào)用,同時避免了容器數(shù)量增加后帶來的iptables規(guī)則的大量增長,方便調(diào)試。
contiv帶來的方便是用戶可以根據(jù)實例IP直接進(jìn)行訪問;我們在使用過程中整體比較穩(wěn)定,中間出現(xiàn)過一次問題: 機(jī)房停電導(dǎo)致了部分IP的分配狀態(tài)不正確,而且contiv當(dāng)時還沒有提供查看已分配IP的接口。
Docker 1.10版本支持指定IP啟動容器,并且由于部分應(yīng)用對實例IP固定有需求,我們開始著手容器IP固定方案的設(shè)計與開發(fā)。
前面提到應(yīng)用運(yùn)行時,對應(yīng)k8s內(nèi)一個ReplicationController以及一個Service。 應(yīng)用的重新部署目前采用的策略主要是重建策略。 重建的流程包括刪除RC及RC下所有Pod,更新并創(chuàng)建新的RC(kubernetes會根據(jù)RC配置產(chǎn)生新的POD)。
在默認(rèn)的k8s+contiv的網(wǎng)絡(luò)環(huán)境下,容器(Pod)的IP網(wǎng)絡(luò)連接是由contiv network plugin來完成的, contiv master只實現(xiàn)了簡單的IP地址分配和回收,每次部署應(yīng)用時,并不能保證Pod IP不變。所以我們引入了新的Pod層面的IPAM,以保證同一個應(yīng)用多次發(fā)生部署時,Pod IP始終是不變的。
作為Pod層面的IPAM,我們把這一功能直接集成在了kubernetes。Pod作為k8s的最小調(diào)度單元,原有的k8s Pod Registry(主要負(fù)責(zé)處理所有與Pod以及Pod subresource相關(guān)的請求:Pod的增刪改查,Pod的綁定及狀態(tài)更新,exec/attach/log等操作) 并不支持在創(chuàng)建Pod時為Pod分配IP,Pod IP是通過獲取Pod Infra Container的IP來獲取的,而Pod Infra Container的IP即為contiv動態(tài)分配得來的。
Pod Registry 訪問設(shè)計圖:
在原有kubernetes代碼基礎(chǔ)上,我們修改了Pod結(jié)構(gòu)(在PodSpec中加入PodIP)并重寫了Pod Registry 同時引入了兩個新的資源對象:
1. Pod IP Allocator: Pod IP Allocator是一個基于etcd的IP地址分配器,主要實現(xiàn)Pod IP的分配與回收。
Pod IP Allocator通過位圖記錄IP地址的分配情況,并且將該位圖持久化到Etcd;
2. Pod IP Recycler: Pod IP Recycler是一個基于etcd的IP地址回收站,也是實現(xiàn)PodConsistent IP的核心。Pod IP Recycler基于RC全名(namespace + RC name)記錄每一個應(yīng)用曾經(jīng)使用過的IP地址,并且在下一次部署的時候預(yù)先使用處于回收狀態(tài)的IP。
Pod IP Recycler只會回收通過RC創(chuàng)建的Pod的IP,通過其他controller或者直接創(chuàng)建的Pod的IP并不會記錄,所以通過這種方式創(chuàng)建的Pod的IP并不會保持不變; 同時Pod IP Recycle檢測每個已回收IP對象的TTL,目前設(shè)置的保留時間為一天。
這里對kubelet也進(jìn)行了改造,主要包括根據(jù)Pod Spec中指定IP進(jìn)行相關(guān)的容器創(chuàng)建(docker run加入IP指定)以及Pod刪除時釋放IP操作。
創(chuàng)建和刪除Pod的UML時序圖如下:
Pod的創(chuàng)建在PaaS里主要有兩種情形:
應(yīng)用的第一次部署及擴(kuò)容,這種情況主要是從IP pool中隨機(jī)分配;
應(yīng)用的重新部署:在重新部署時,已經(jīng)釋放的IP已根據(jù)RC全名存放于IP Recycle列表中,這里優(yōu)先從回收列表中獲取IP,從而達(dá)到IP固定的效果。
整體刪除過程為:由PaaSNg或kube-controller-manager調(diào)用apiserver Pod Delete并設(shè)置DeletionTimestamp, kubelet監(jiān)聽到刪除時間并獲取GracefulDeletiontime,刪除應(yīng)用容器, 通知apiserver釋放IP(釋放IP時獲取Pod所屬RC,根據(jù)是否有對應(yīng)RC 名稱決定是否存放在IP Recycle列表),刪除Pause Pod,通知apiserver 刪除Pod對象。
另外為了防止IP固定方案中可能出現(xiàn)的問題,我們在kubernetes中加入了額外的REST api: 包括對已分配IP的查詢,手動分配/釋放IP..。
對目前方案的總結(jié):
容器IP固定方案已上線,運(yùn)行基本沒問題,但穩(wěn)定性有待提升。主要表現(xiàn)為偶然性不能在預(yù)期時間內(nèi)停止舊Pod,從而無法釋放IP造成無法復(fù)用(初步原因是由于Docker偶爾的卡頓造成無法在規(guī)定時間內(nèi)停止容器)。我們短期的work around是使用額外添加的REST apiss手動修復(fù),后期IP固定方案會繼續(xù)加強(qiáng)穩(wěn)定性并根據(jù)需求進(jìn)行優(yōu)化。
總結(jié)
以上是生活随笔為你收集整理的k8s网络架构图_唯品会基于Kubernetes(k8s)网络方案演进的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 边框回归的损失函数_一文搞懂常用的七种损
- 下一篇: hql 字符串where语句_hiber