怎樣構建一個容器集群?

pw36 9年前發布 | 36K 次閱讀 容器

本文是Google容器技術系列博客的第二篇, 第一篇中大致介紹了容器,Docker,以及Kubernetes的基本概念,這篇文章中對Kubernetes進行了相對深入的介紹,作者從Kubernetes中的一些核心概念入手,介紹了Google在構建容器集群管理系統中的一些核心要素。

在上周,來自“Google 云平臺全球解決方案小組”的專家MilesWard為我們做了關于容器技術的系列博客的開篇,在 前一篇博客中MilesWard大體介紹了關于容器,Docker以及Kubernetes的一些基本概念。如果你還沒有閱讀先前的文章,建議你先進行一些了解,這樣而可以補充一些相關的基礎知識,也將會幫助你更好的理解本文中介紹的內容。

這周,我們請來了Google的高級工程師,同時也是Kubernetes項目的核心成員 Joe Beda。他將從更深的層面上為我們介紹Google在容器技術使用過程中的一些核心技術概念。這些概念也是Kubernetes創建基的礎,理解這些概念也可以幫助我們更好地理解這一系列博客的后續文章。

怎樣構建一個容器集群?

最近一段時間,容器系統相關技術迅速崛起并受到了廣泛關注(比如 Docker)。 容器技術已經帶給我們很多令人興奮的實踐。容器的打包,遷移,并且在不同的環境中運行服務的能力,可以方便地讓我們管理自己的服務,從另一個角度上,這也 幫助我們提高了服務的“流動性”。但是,隨著用戶不斷將他們的服務遷移到生產環境中,新的問題也隨之出現,比如具體哪個容器運行在哪臺服務器上,怎樣同時 運行大量數目的容器,容器之間如何方便地進行跨主機通信等等,正是這些問題的出現,促使我們構建了 Kubernetes。Kubernetes是一個來自Google的開源的工具包,可以幫助我們解決這些新出現的問題。

正像我們在上一篇文章中所討論的那樣,我們認為Kubernetes是一個“容器集群管理器”。許多技術人員習慣把這個領域的項目稱之為“編排系 統(orchestration systems)”,他們或許是想將集群的管理工作比作是交響樂的編曲。但我從不這樣理解,交響樂(Orchestral music)編曲工作通常是提前根據旋律和配樂被細致地編排好,并且在表演之前,每個表演者的任務已經被明確指定好。而Kubernetes集群的管理過 程,更像是一個升級版的爵士樂即興表演。它是一個動態的系統,可以根據輸入的信息和當前系統的運行環境實時做出反應。

所以,我們不禁要問,到底是哪些因素幫助我們構建了一個容器集群? 是否可以這樣描述一個集群系統:這是一個動態的系統,在系統中可以放置多個容器,這些容器的狀態以及容器之間的通信都可以被系統所監控。事實的確如此,一 個容器集群正是由一個監控系統和一系列計算結點(不論是物理服務器或者是虛擬機)所組成的。在這篇文章的剩余部分,我們會著重探討三方面的話題:容器集群 由什么組成,容器集群應該怎樣應用到我們的實際工作中,以及組成容器集群的各個要素又是怎樣在一起發生作用的。此外,基于我們已有的經驗,一個容器集群還 應該包含一個管理層,我們將繼續探索這個管理層是如何實現的。

為何要以集群的方式運行容器?

在Google,我們所構建的容器集群需要符合一系列常見的要求:集群總是可用的,可以被打補丁并且被升 級,集群按需擴展,集群相關指標容易被測量(easily instrumented)和監控等等。根據容器本身的特性,服務可以通過快速、容易地方式進行部署,并且還可以將整個服務分成許多小的部分,以進行更加 細粒度的操作。雖然容器化的操作一定程度上為我們提供了方便,但是為了滿足我們提出的這些目標,我們仍然需要一個系統的解決方案來管理容器集群。

在Google過去的10年間,我們發現一個容器集群管理器就可以滿足席上這些需求,并且這個集群管理器還可以為我們提供許多其他的好處:

  • 通過微服務的模 式進行開發,可以使整個開發過程變得更容易管理。集群管理器可以使我們把一個完整的服務分成許多小的部分,這些小的部分可以互相分開,分別進行管理和擴 展。這可以使我們在軟件開發階段,按照服務的復雜程度來組織我們的開發團隊,通過指定好清晰的接口讓不同的小的開發團隊來協同開發。
  • 面對故障時候的系統自我修復。當某個服務器發生故障的時候,集群管理器可以自動地在健康的服務器上重啟那些之前在發生故障的服務器上運行的任務。
  • 水平擴展變得更容易。一個容器集群可以為水平擴展提供工具,例如如果想要添加更多的計算能力,僅需要通過修改設置(重復記數)就能實現。
  • 高利用率和高效率。Google在將服務遷移到容器上之后,極大程度地增加資源的利用率和使用效率。
  • 集 群和服務的運維團隊的角色發生了改變。開發者可以將更多的精力集中在他們所提供的服務上,而并非集中在底層的基礎設施的支持上。例如,Gmail的運維和 開發團隊(operations and development teams)幾乎不用和集群的操作運維團隊直接交流就可以完成他們工作,這種關注點的分離可以使運維團隊發揮更大的作用。
  • </ul>
    現在,我們明白了,當前我們所做的事情還是很有意義的,所以讓我們一起探索構成一個優秀的集群管理系統到底需要哪些要素,以及如果你希望認識到以集群的方式運行容器的優勢,應該對哪些方面進行特別關注。

    要素一:動態容器分配

    想要構建一個成功的容器集群,你需要一點點“jazz即興表演技巧”。你需要將你的工作任務任務打包成一個容器鏡像并且明確地說明你的意圖,說明要如何運行容器以及將要在哪里運行容器。集群管理系統最后會決定到底你的工作任務在哪里運行,我們把這個過程稱為“集群調度”。

    這并不是意味著工作任務會被隨機地分配在計算結點上。正相反,在工作量被分配的時候,需要遵循一系列嚴格的限制,從計算機科學的角度來將,這會使 得集群調度變成一個有趣而又困難的問題(注釋1)。當需要調度的時候,調度器確定要把你的工作量放到一個有足夠剩余空間(例如CPU,RAM,I/O,存 儲)的虛擬機或者是物理服務器上。但是,為了滿足可靠性的目標,調度器可能需要把一系列的任務以跨主機的形式進行分配或者按一定的順序來排列(racks in order),以此來減少相關運行時發生故障的可能性。或者一些特殊的任務會被分配在一些有某些特殊的硬件(比如GPU,本地的SSD等等)的機器上。調 度器也會根據不斷變化的運行環境作出反應。并且應該在任務運行失敗的時候重新對任務進行調度,增加/縮小集群規模以提高效率。為了實現這個目的,我們鼓勵 用戶避免將一個容器固定在一個服務器上。有些時候你可能需要指定“我想讓某個容器在某個機器上運行”但這種情況應該比較少見。

    下一個問題是:我們進行調度操作的具體對象是什么?最簡單的答案就是使用單獨的容器。但是在某些時候,你希望有一系列的容器在一個主機上以合作的 方式在運行。例如一個數據加載器,需要一個數據庫服務一起運行或者是一個log compressor/saver進程同樣需要與一個服務來搭配運行。運行這些服務的容器通常需要被放在一起,并且你需要確保它們在動態配置的過程中并沒 有被分離開。為了實現這個目的,我們在Kubernetes中引入個一個概念:pod。一個pod是一系列容器的集合,這些容器在一起構成一個單元在服務 器(也可以被稱作Kubernetes結點)上被配置和調度。為了使得每次可以配置多個pod,Kubernetes采用一種可靠的方式將許多工作打包在 一個結點上。

     怎樣構建一個容器集群?


    要素二:按照集合的方式進行思考

    當在一個單獨的物理結點上工作時,一般的工具通常不會以批量的方式對容器進行操作。但是在容器 集群上進行工作的時候,你可能希望很輕易地就能實現服務的跨結點擴展。為了實現這一目標,你需要以集合的方式進行思考,而并非像之前一樣按照單例模式考 慮。并且你還希望這些容器集合都可以通過很容易地方式進行配置。在Kubernets中,我們引入了兩個額外的概念來管理一系列pod:label以及 replication controllers。

    Kubernets中的每一個pod都有一套key-value鍵值對與其相綁定,我們把這個鍵值對稱為labels。你可以通過構建一個基于這 些labels查詢,來篩選出一系列pods。Kubernets沒有一個所謂的組織pod的“正確的方式”。這完全取決于用戶,只要是適合用戶的組織方 式就是合適的。用戶可以根據應用程序的層來結構來組織,也可以根據地理位置來組織,或者是部署環境等等。實際上,因為labels是非層次結構的 (non-hierarchical),你可以同時以多種方式組織你的pod。

    舉例來說:比如你有一個簡單的服務,這個服務同時包含前端和后端兩個層次。同時你還有不同的環境:測試環境,交付環境(staging environment)以及生產環境。你可以同時利用多個標簽來標記你pod,比如用于生產環境的前端pod可以標記為:env=prod、 tier=fe 同時,用于生產環境后端pod可以標記為env=prod、tier=be。同理,你也可以按照類似方法來標記你在測試和交付環境中使用的pod。接下 來,當用戶需要對集群進行操作或者檢查的時候,就可以將操作范圍限制在標記為env=prod的pod中,這樣就可以同時看到在生產環境中的前端和后端的 pod。或者你只想查看你的前端環境,此時只需要查找標記為tier=fe的pods,這樣就可以查看跨越了測試、交付和生產三個不同的環境的前端 pods。隨著你添加更多的層次和不同的運行環境,你也可以按照自己的方式來設想和規劃,按照自己的方式定義這個系統將,使其更好地滿足你的需求。

     怎樣構建一個容器集群?


    擴展

    既然我們之前已經可以對擁有類似配置的物理服務器資源池進行識別和維護。我們可以參考這個功能來對容器集群進行水平擴展 (即“scaling out”)。為了使這個步驟更加容易,我們在Kubernets中維護了一個helper對象,我們稱其為replication controller 。它維護著一個存有pods的資源池,還有一些屬性用于描述這個資源池,包括期望進行擴展的數目 replication count ,還有一個pod 模板以及一個用于進行選擇/查詢的label。 實際上這個對象的原理理解起來也并不困難,下面是偽代碼:

    object replication_controller {
    property num_replicas
    property template
    property label_selector

    runReplicationController(num_desired_pods, template, label_selector) { loop forever {   num_pods = length(query(label_selector))   if num_pods > num_desired_pods {     kill_pods(num_pods - num_desired_pods)   } else if num_pods < num_desired_pods {     create_pods(template, num_desired_pods - num_pods)   } } } }</pre>

    對以上代碼進行分析,比如,你想要使用三個pod來運行一個php前端,你可能會使用一個合適的pod模板(指向你的php容器鏡像)創建一個 replication controller。其中的 num_replicas的值為3。你可能會通過一個label查詢 env=prod、tier=fe 來定位到一系列pod集合,之后這個replication controller對象就會對你找到的這些pod集合進行操作。通過這種方式replication controller將會很容易理解集群進行收縮/擴展之后預期的狀態,它會不斷對集群進行調整直至實現最后的狀態。如果你希望縮小或者擴大你的服務規 模,所有你需要做的僅僅是改變預期的replicaiton count,replication controller將會處理其余的問題。通過將注意力集中在系統的預期狀態,我們使這個問題變得易于處理。

     怎樣構建一個容器集群?


    要素三:集群內部服務之間的連接與通信

    你已經可用上面列出的幾個特性做一些很有趣的事了。任何高度并行的任務分發系統(持續集 成系統,視頻解碼等等)在工作的時候,它們的pod之間不需要做很多的交互。然而,大多數復雜的服務更多的是小型(微型)的網絡服務構成的,它們的pod 之間需要進行很多的交互,按照傳統的應用層級劃分,每一層就像是 中的一個結點。

    一個集群管理系統需要一個命名解析系統,這個解析系統可以與上面所描述的幾個要素一同進行工作。就像DNS提供的域名到IP地址的解析一樣,這個 命名服務可以將服務名稱解析成一個目標,以及一些額外的需求。特別地,系統的運行狀態發生變化的時候,這種變化應該很快地被系統所捕獲,一個“服務名稱” 應該能解析一系列的targets,可能還有額外的關于這些target的元信息(比如 碎片任務 shard assignment)。對于Kubernets API ,這個工作通過 label selector 以及watch API(注釋2)模式來完成。這為服務發現提供了一個很輕量級的形式。

    大多數的客戶端將不會因為僅僅想利用新的命名API的優勢就立即重寫(或者從來不會被重寫)大多數項目希望有一個單獨的地址以及一個端口以此可以 和其他層的服務進行通信,為了彌補這個不足,Kubernetse引入了服務代理的理念。這是一個簡單的網絡負載均衡/代理,可以為你進行名字查詢并且可 以以一個單獨的穩定的IP/端口的形式(通過DNS)在網絡上暴露給用戶。當前,這個代理做簡單的輪詢式負載均衡跨越所有的通過label selector識別出來的后端。按照計劃,Kubernets希望允許custom proxies/ambassadors ,這樣可以進行更靈活的指定域的決策(關注 Kubernetes roadmap來了解更多的細節)。事實上,MySQL也開始意識到ambassador的作用,它可以知道如何發送寫信息流到master結點,并且將信息流讀入read slave結點。

    總結

    現在你已經了解了以上三個關于集群管理系統的關鍵的要素即:動態的容器配置,容器集合的方式進行思考,集群內部服務之間的連接,是如何在一起發揮作用的。

    在這個文章的開始我們提出了這樣一個問題:“到底如何構建一個容器集群?”希望通過我們在上面文章中提出的信息和相關細節,你已經有了答案:簡而 言之,一個容器集群是一個動態的系統,這個系統可以存放和管理容器,容器以pod的形式組合在一起,在結點上運行,同時還包括內部用于相互連接和通信的信 道。

    當我們開始構建Kubernetes 的時候,我們的目標是 :使得Google對于容器的使用經驗具體化。我們最初僅僅關注容器的調度以及動態配置,然而,當我們徹底明白在構建一個真正的服務時,不同的系統是完全 必要的。我們立即發現,把其他的額外的要素加入進來是完全有必要的,比如:pods,labels以及replication controller。在我看來,這些絕對是構建一個可用的容器集群管理系統的最少的需要的模塊。

    Kubernetes仍然在在不斷發展,但目前的發展狀態還算不錯,我們剛剛推出了v0.8的版本,你可以從 這里下載,我們仍然在添加新的功能并且重新構建那些我們已有的功能。我們還推出了 roadmap v1.0, 這個項目已經開始啟動,并且一個很大的正在不斷成長的社區作為合作伙伴在做貢獻(就像ReaHat 、VMWare、Microsoft、IBM、CoreOS等等)還有許多用戶,他們在不同的環境中來使用Kubernetes。

    雖然我們在這個領域有很多實踐經驗,但是又很多問題Google也沒有答案,可能在集群使用過程中有一些特殊的要求和特別需要考慮的地方我們在目前還沒有意識到,考慮到這一點,請參與到我們正在建設的一些項目中來: Try it out、f ile bug reportsask for help or send a pull request (PR)

    -Posted by Joe Beda, Senior Staff Engineer and Kubernetes Cofounder

    注釋1:這是一個傳統的 背包問題,在通常情況下這是一個 NP難問題
    注釋2:“Watch API 模式”是這樣一種方法: 它可以從一個服務中來分發異步事件,在通常的鎖服務系統中這個很常見(zookeeper等等),這個方法最初源于 Google Chubby 的論文。客戶端本質上發送并且“掛起”一個請求,直到有變化發生。客戶端的請求這通常會加上版本號信息,所以客戶端會對任何變化保持最新的狀態。
    原文鏈接:What makes a container cluster? (翻譯:王哲 )

    ===========================
    譯者介紹
    王哲, 浙江大學SEL實驗室碩士研究生,目前在云平臺團隊從事科研和開發工作。浙大團隊對PaaS,Docker,大數據和主流開源云計算技術有深入的研究和二次開發經驗,團隊現聯合社區將部分技術文章貢獻出來,希望能對讀者有所幫助。

    來自:http://dockerone.com/article/173

 本文由用戶 pw36 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!