Kubernetes 架構淺析
最近研究了一段時間的Kubernetes,將我們服務的測試環境服務部署到了Kubernetes上,上周末在團隊中分享了下,順便整理成文章。
閱讀對象:對Kubernetes尚未深入了解的同學
首先,為什么要用Kubernetes? 使用一個工具先要梳理下使用這個工具的目標,我們不是為了工具而用工具。
Kubernetes的目標用一張被很多人引用過的圖來說明最好: 一 句話,Kubernetes的目標是讓你可以像管理牲畜一樣管理你的服務,而不是像寵物一樣,同時提高資源的利用率,讓碼農關注在應用開發本身,高可用的 事情就交給Kubernetes吧。這個圖本來是openstack提出的,但純粹IaaS層的解決方案實現不了這個目標,于是有了 Kubernetes。
Kubernetes和Borg系出同門,基本是Borg的開源改進版本,引用Google Borg論文里的說法:
it (1) hides the details of resource management and failure handling so its users can focus on application development instead; (2) operates with very high reliability and availability, and supports applica- tions that do the same; and (3) lets us run workloads across tens of thousands of machines effectively
</blockquote>我們如何驗證是否達到這個目標了呢?
- 隨機關掉一臺機器,看你的服務能否正常
- 減少的應用實例能否自動遷移并恢復到其他節點
- 服務能否隨著流量進行自動伸縮
</ol>我們從一個簡單的多層應用的架構改進來探討下:
![]()
說明:
- mysql應該是一主多從的架構,這里為了簡單進行了省略
- service后面也會依賴數據庫等資源,這里為了簡單進行了省略
- 箭頭表示調用和依賴關系
</ul>具體分析一下為了達到我們的目標,需要做到改進:
- Loadbalancer 要調用后端應用服務節點,后端應用服務節點掛了或者遷移增加節點,都要變更Loadbalancer的配置。這樣明顯達不到目標,于是計劃將 Loadbalancer改造成Smart Loadbalancer,通過服務發現機制,應用實例啟動或者銷毀時自動注冊到一個配置中心 (etcd/zookeeper),Loadbalancer監聽應用配置的變化自動修改自己的配置。
- Web應用對后端資源的依賴,比如Mysql和Memcached,對應資源的ip一般是寫到配置文件的。資源節點變更或者增加都要變更應用配置。
- Mysql計劃該成域名訪問方式,而不是ip。為了避免dns變更時的延遲問題,需要在內網架設私有dns。高可用采用MHA方案,然后配合服務發現機制自動修改dns。
- Memcached計劃參照couchbase的方式,通過服務發現機制,使用SmartClient,客戶端應用監聽配置中心的節點變化。難點可能在于對Memcached的改造(可以參考couchbase)。另外也可以通過增加一層代理的機制實現。
</ul> </li>- 應 用節點遷移時依賴的系統和基礎庫不一樣如何處理?部署方式不一樣如何處理?磁盤路徑,監聽端口等沖突怎么辦?這個可以通過Docker這樣的容器技術,將 應用部署運行的方式進行標準化,操作系統和基礎庫的依賴允許應用自定義,對磁盤路徑以及端口的依賴通過Docker運行參數動態注入,而不需要變更應用配 置。Docker的自定義變量以及參數,需要提供標準化的配置文件。
- 服務遷移問題 每種服務都需要一個master調度中心,來監控實例狀態,確定要不要進行遷移,負責統一調度。并且每個服務器節點上要有個agent來執行具體的操作,監控該節點上的應用。另外還要提供接口以及工具去操作。
- 網絡以及端口沖突的問題比較麻煩 需要引入類似SDN的解決方案。
- 內存,cpu,以及磁盤等硬件資源,原來的習慣是購買服務器的時候就根據服務器的上的應用類型進行規劃,如果應用和硬件解耦,這種方式需要淘汰。但必須有一種調度機制讓應用遷移的時候可進行篩選。
</ol>總結一下,通過分析得出,要達到目標,關鍵是解耦,應用進程和資源(包括 cpu,內存,磁盤,網絡)的解耦,服務依賴關系的解耦。
我們上面的改造機制基本是按照個案進行設計,Kubernetes的則是要提供一套全面通用的機制。
然后,我們看看Kubernetes對以上問題的解決方案:
先上一張Kubernetes官方的架構圖
![]()
- 調度中心master,主要有四個組件構成:
etcd 作為配置中心和存儲服務(架構圖中的Distributed Watchable Storage),保存了所有組件的定義以及狀態,Kubernetes的多個組件之間的互相交互也主要通過etcd。
Kubernetes etcd registry的目錄結構</li>
etcdctl ls /registry
/registry/minions 保存node節點信息
/registry/namespaces
/registry/pods 保存所有的pods信息
/registry/ranges
/registry/serviceaccounts
/registry/services
/registry/controllers
/registry/events Kubernetes組件的變更事件都會寫到這個目錄下
- kube-apiserver 提供和外部交互的接口,提供安全機制,大多數接口都是直接讀寫etcd中的數據。
kube-scheduler 調 度器,主要干一件事情:監聽etcd中的pod目錄變更,然后通過調度算法分配node,最后調用apiserver的bind接口將分配的node和 pod進行關聯(修改pod節點中的nodeName屬性)。scheduler在Kubernetes中是一個plugin,可以用其他的實現替換(比 如mesos)。有不同的算法提供,算法接口如下:
type ScheduleAlgorithm interface {</li>
Schedule(api.Pod, NodeLister) (selectedMachine string, err error)
}- kube-controller-manager 承 擔了master的主要功能,比如和CloudProvider(IaaS)交互,管理 node,pod,replication,service,namespace等。基本機制是監聽etcd /registry/events下對應的事件,進行處理。具體的邏輯需要專門文章分析,此處不進行詳解。
</ul> </li>- 節點上的agent,主要有兩個組件:
- kubelet 主 要包含容器管理,鏡像管理,Volume管理等。同時kubelet也是一個rest服務,和pod相關的命令操作都是通過調用接口實現的。比如:查看 pod日志,在pod上執行命令等。pod的啟動以及銷毀操作依然是通過監聽etcd的變更進行操作的。但kubelet不直接和etcd交互,而是通過 apiserver提供的watch機制,應該是出于安全的考慮。kubelet提供插件機制,用于支持Volume和Network的擴展。
- kube-proxy 主 要用于實現Kubernetes的service機制。提供一部分SDN功能以及集群內部的智能LoadBalancer。前面我們也分析了,應用實例在 多個服務器節點之間遷移的一個難題是網絡和端口沖突問題。Kubernetes為每個service分配一個clusterIP(虛擬ip)。不同的 service用不同的ip,所以端口也不會沖突。Kubernetes的虛擬ip是通過iptables機制實現的。每個service定義的端 口,kube-proxy都會監聽一個隨機端口對應,然后通過iptables nat規則做轉發。比如Kubernetes上有個dns服務,clusterIP:10.254.0.10,端口:53。應用對 10.254.0.10:53的請求會被轉發到該node的kube-proxy監聽的隨機端口上,然后再轉發給對應的pod。如果該服務的pod不在當 前node上,會先在kube-proxy之間進行轉發。當前版本的kube-proxy是通過tcp代理實現的,性能損失比較大(具體參看后面的壓測比 較),1.2版本中已經計劃將kube-proxy完全通過iptables實現(https://github.com/kubernetes/kubernetes/issues/3760)
</ul> </li>- Pods Kubernetes 將應用的具體實例抽象為pod。每個pod首先會啟動一個google_containers/pause docker容器,然后再啟動應用真正的docker容器。這樣做的目的是為了可以將多個docker容器封裝到一個pod中,共享網絡地址。
- Replication Controller 控制pod的副本數量,高可用就靠它了。
- Services service 是對一組pods的抽象,通過kube-proxy的智能LoadBalancer機制,pods的銷毀遷移不會影響services的功能以及上層的調 用方。Kubernetes對service的抽象可以將底層服務和上層服務的依賴關系解耦,同時實現了和Docker links類似的環境變量注入機制(https://github.com/kubernetes/kubernetes/blob/release-1.0/docs/user-guide/services.md#environment-variables),但更靈活。如果配合dns的短域名解析機制,最終可實現完全解耦。
- Label key-value格式的標簽,主要用于篩選,比如service和后端的pod是通過label進行篩選的,是弱關聯的。
- Namespace Kubernetes中的namespace主要用來避免pod,service的名稱沖突。同一個namespace內的pod,service的名稱必須是唯一的。
- Kubectl Kubernetes的命令行工具,主要是通過調用apiserver來實現管理。
- Kube-dns dns 是Kubernetes之上的應用,通過設置Pod的dns searchDomain(由kubelet啟動pod時進行操作),可以實現同一個namespace中的service直接通過名稱解析(這樣帶來的 好處是開發測試正式環境可以共用同一套配置)。主要包含以下組件,這幾個組件是打包到同一個pod中的。
- etcd skydns依賴,用于存儲dns數據
- skydns 開源的dns服務
- kube2sky 通過apiserver的接口監聽kube內部變更,然后調用skydns的接口操作dns
</ul> </li>- Networking Kubernetes的理念里,pod之間是可以直接通訊的(http://kubernetes.io/v1.1/docs/admin/networking.html),但實際上并沒有內置解決方案,需要用戶自己選擇解決方案: Flannel,OpenVSwitch,Weave 等。我們測試用的是Flannel,比較簡單。
- 配置文件 Kubernetes 支持yaml和json格式的配置文件,主要用來定義pod,replication controller,service,namespace等。
</ol>Kubernates 可能帶來的改變
- 降低分布式應用開發運維的復雜度 縱 觀當前的各種分布式架構,hadoop,storm等,都是master-workers模式,框架很大一部分功能在節點的管理,處理程序的調度上,如果 基于Kubernetes來實現類似功能,這些基本都可以交給Kubernetes完成,框架只需要負責核心數據的流轉以及收集邏輯。當然,當前 Kubernetes的pod還未像Borg一樣直接支持batch job,但按照Kubernetes和Borg的關系,將來應該會支持(http://blog.kubernetes.io/2015/04/borg-predecessor-to-kubernetes.html)。
- 更完備的CI/CD(持續集成/持續交付)工具 CI是code-deploy的關鍵工具,但當前由于受限于部署環境的不一致,CI可做的事情有限,大多數還依賴用戶的自定義腳本。有了Kubernetes這樣的標準環境后,以后此類工具可以覆蓋測試環境部署,集成測試,上線部署等環節,實現標準化的交付工作流。
- Kubernetes之上的分布式存儲 Kubernetes官方提供了一個在Kubernetes上部署cassandra的 例子,只需要重寫一個基于Kubernetes apiserver的SeedProvider,就可以避免靜態配置seed ip,享受Kubernetes帶來的scale-out能力。再如前面我們提到的memcached的高可用例子,如果基于Kubernetes實現一 個memcached的smart client,只需要改下客戶端即可,非常簡單。個人認為以后在Kubernetes上的支持多租戶的分布式存儲會更加流行。只要解決了存儲服務的 scale-out問題,應用的scale-out一般不會有太大問題。Hypernetes就是一個實現了多租戶的Kubernetes版本。
- 企業應用的分發 當 前SaaS(on-demand)比較流行的一個很大的原因是原來的on-premise應用的部署運維成本太高,如果Kubernetes等分布式操作 系統得到廣泛應用,on-premise的成本降低,on-premise以及托管云模式對SaaS的格局也會帶來影響。這也是我們這樣的創業公司關注 Kubernetes的原因之一。
</ol>遇到的一些問題
最后總結一下我遇到的一些問題
- 墻 gcr.io已被墻,如果在本地用腳本在虛擬機安裝,請全程KX上網。如果在服務器上就自己想辦法下載,然后在配置文件中指定鏡像地址。
- 并發拉取鏡像導致鏡像文件破壞(https://github.com/kubernetes/kubernetes/issues/10623) 這個和docker也相關,建議先用腳本在各node上pull鏡像再部署。
- 同 一個pod內的多個容器啟動順序問題 同一個pod的多個容器定義中沒有優先級,啟動順序不能保證。比如kube-dns中,etcd要先啟動,然后skydns連接etcd創建基本的目錄, 最后kube2sky啟動,將kube中已經定義的數據同步到dns中。如果順序不對dns數據就不正常。如果遇到這種問題按順序重啟一下對應的容器即 可。這種問題當前需要應用自己通過重試機制解決。
- 容器內訪問外部網絡 如果使用了Flannel方案,但容器內無法訪問公網(node可以的情況),一般是iptables被搞壞了(https://github.com/coreos/flannel/issues/115)。
- 當前的Kubernetes沒有應用的概念,我們的應用包含4個自己開發的服務組件,還有一些依賴(mysql,redis,mongodb等),定義下來一共要20多個yaml。要實現一鍵安裝或者更新,還需要做不少工作。
- Kubernetes的公網負載均衡的解決方案依賴IaaS的實現,不夠靈活。
kube-proxy的性能問題,簡單的壓測結果如下: 10.254.2.99:80是service地址,后面有兩個pod。11.1.16.15:3000是其中一個pod。代碼是golang官方網站首頁的那個helloword。
</li> </ol>
鏈接,代碼以及壓測結果長微博上不能很好展示,可查看原文: http://jolestar.com/kubernetes-architecture/
來自: http://weibo.com/p/1001603912843031387951