# 第13章 調度
到目前為止,我們已經看了很多在單臺主機上運行Docker的案例。然而,你的基礎設施必定會在某一刻增長到單臺宿主機不足以承載其服務的程度或者說你想要在一些高可用的環境下運行Docker容器。這時候就需要將它擴展到多臺機器上。這會帶來很多的挑戰,不僅在于多宿主機的Docker網絡配置,更重要的是容器的編排和[集群](https://en.wikipedia.org/wiki/Computer_cluster)的管理。
如果沒有合適的集群和編排的管理工具,你會發現要么是自己被容器基礎設施所綁架,深陷于繁瑣的日常事務中,而不是管理和操縱它,要么便是重復造輪子,最終寫了很多自己的容器管理工具。事實上,Docker已經意識到了這方面的需求并投入了巨大的精力到它的[網絡模型](https://blog.docker.com/2015/06/networking-receives-an-upgrade/)的改造當中,而且還創建了一個名為[Swarm](https://github.com/docker/swarm/)的Docker原生集群管理工具。
關于集群管理這個話題,如果要深入講解的話可以單獨寫一本書了。這里面包含了大量需要關注的內容:如何增刪宿主機,如何確保它們的健康狀態,以及如何伸縮擴展來管理負載等。在本章中,我們將把重點放在集群管理和容器編排中一個特別關鍵的部分:**調度**。
作業調度的基本理念很簡單,而且它的歷史可以追溯到大型機與高性能計算(HPC)。與其把計算機的資源投入到各自特定的工作負載,還不如將整個數據中心看做是一個巨大的計算和存儲的資源池,然后只需要在上面設置需要運行的計算作業即可,就像它是一臺巨大的計算機一樣。事實上,Mesosphere作為Docker領域里基礎設施的一員,它們的主打產品正是“數據中心操作系統”。
現在,讓我們先把具體實現方面的內容放一放,把重點放在根本問題上。為了更好的理解Docker主機集群里容器調度問題的本質,我們需要先做一些鋪墊工作,并且了解一下調度的真正意義。
如果給調度問題作一個30秒的簡單概括,我們就可以說調度問題是為一些計算任務分配一組硬件資源(如CPU、內存、存儲和網絡容量),在滿足任務需求的同時完成這些任務。
可以影響調度決策的因素有很多。執行成本顯然是需要考慮進去的,但是我們也需要考量延遲、吞吐量、錯誤率、完成時間以及服務的交付質量等因素。因此需要根據重要程度(面向用戶)為任務排優先級,或者在調度一些帶有明顯負載波動的關鍵作業時安排較少的時間窗口。
每個任務可能由多個進程組成,對應的是不同的數據存儲和網絡需求,網絡需求也許還有一定的網絡帶寬或者地域要求,因此用戶得把它們放在靠近彼此的區域,由于可能還會有一些冗余的要求,所以用戶還得在其他地方存放幾個冗余的副本,如放到另一個數據中心或者可用區。
應用所需的資源量在不同時間點會因為負載的不同發生變化,由于它們做過擴容和縮容,因此需要根據其擴容或縮容做重新調度。此外,隨著時間的推移,新的應用也將被部署到環境里,因此必須定期重新考慮調度策略。有些任務可能只是臨時執行的,而有些則是長期運行的,如果基于生命周期來分配資源也能產生一定的收益。
度量和監控是關鍵:這是吞吐量及延遲與需求之間的博弈,資源利用率和容量的規劃很重要,但是同樣也得考慮成本和預算,還有那些即將產生的價值。當用戶把調優作為最終目標(如最大化吞吐量、最低延遲等)的時候,調度問題本身就成了一類[調優問題](https://en.wikipedia.org/wiki/Program_optimization)。
通過幾十年來的不斷優化,操作系統這一層的調度已經做得相當出色了,因此在絕大多數情況下,我們所做的調度都是基于單臺主機的。就目前而言,跨主機的調度還遠未成熟。
與虛擬機(VM)的調度不同,容器通常都不固定大小,它們隨時可以修改自己的內存使用量。這使得容器的調度比將一些固定大小的虛擬機裝配到物理裸機的[**裝箱問題**](https://en.wikipedia.org/wiki/Bin_packing_problem)要困難得多。
最優調度是一個非常棘手的問題,只有在一個非常大的規模下投入巨大的工程人力才可能看到收益。
正如我們之前提到的那樣,與單臺主機的調度問題相比,跨集群之間的調度會更加復雜。單臺主機的調度重點關注少數CPU上運行大量線程和進程的問題,其目的在于避免資源分配不均,保證單個進程不會運行太長的時間,并且確保交互進程可以在超時之前命中資源。除此之外,它還主張一些資源利用方面的公平調度,如引入IO帶寬等概念。在[NUMA](https://en.wikipedia.org/wiki/Non-uniform_memory_access)系統中,隔離性也會被考慮進去,但它不是主要的驅動因素。
在單個或者多個數據中心中,乃至于在云環境下,該問題的規模將會擴大好幾個數量級。延遲會變得至關重要,主機在執行作業時會出現異常,并且每個任務的時間跨度都會相應變長。此外,任務的負載強度方面會有很大的差異,有Hadoop的[MapReduce](https://en.wikipedia.org/wiki/MapReduce)作業,其工作量一般覆蓋到整個集群;也有[MPI](https://en.wikipedia.org/wiki/Message_Passing_Interface)類型的作業,它們需要在任務開始之前準備好相應的可用資源;傳統的Web應用,這些通常只要提供的容量超過它們所需即可。
因此,一個集群里容器的調度真的是一個很令人頭疼的問題,這涉及到了異構環境和不同的工作負載兩方面。這個世界不存在“一刀切”的完美方案,因此各類機構創建了各式各樣一整套的調度工具,它們之中的絕大多數均是從自身的角度出發致力于解決一類特定的負載問題。
[Mesos](http://mesos.apache.org)是由[加利福尼亞大學伯克利實驗室](http://www.berkeley.edu)的研究人員開發的一款特別有意思的解決方案,該實驗室給業界創造了太多的驚喜,而這次它依然沒有讓業界失望。Mesos以一個高級抽象的概念實現了所謂的二級調度,即Mesos本身在計算集群里提供了一層對底層計算資源的抽象,隨后再把它們提供\[或者說調度\]給任意數量的在它之上運行的\[異構\]框架服務。然后每個框架再根據它自己特定的應用場景實現屬于它自己的一套任務調度策略。
這意味著Mesos只維護集群的資源,然后基于優勢資源[公平算法](https://www.cs.berkeley.edu/~alig/papers/drf.pdf)決定把哪些資源分配給哪個框架服務。重要的是Mesos將實際的任務調度委托給了特定的框架服務,就這一點來說自有其利弊。
這一模型為用戶提供了很大的靈活性,用戶無需再操心底層的計算資源的處理,而可以根據特定的應用場景專心地編寫屬于自己的、合適的任務調度器。從另一方面來說,用戶無法看到整個集群狀態的簡要概況。除此之外,在Mesos認為資源分配是相對公平的情況下,用戶也無法在自己需要它們的時候強制讓Mesos為用戶分配更多的資源。Mesos評估資源公平的標準是基于框架啟動時提交給Mesos資源分配器的一些指標。
當用戶運行了大量長期運行的任務時事情可能會變得比較麻煩,它們可能會占用大量的計算資源并因此導致那些尚未調度的任務因為沒有資源消費而阻塞。換句話說:Mesos在用于同構的批量處理型應用時表現得最好,這一點已經成功地被像[Apache Spark](https://spark.apache.org)這樣的項目所驗證,它在Mesos之上實現了一個異常快速的數據處理框架。
運行在Mesos之上的框架服務,它們所調度的計算任務實際上是由Mesos稱之為容器類[\[1\]](part0019.xhtml#anchor131)(Containerizer)的進程來調度的。Mesos自0.20版本起,便在容器類的選擇方面加入了對Docker的支持。如今Mesos仍然還沒有完全實現對Docker的全面支持,但是該版本實現的功能已經足以用來調度一些復雜的Docker任務。
[Twitter](https://twitter.com)證明了Mesos可以成功駕馭大規模基礎設施下的異構應用。他們已經為長期運行的服務開發了一個叫[Aurora](http://aurora.apache.org)的框架,它支持資源搶占和其他許多實用的功能。業界另一個很受歡迎的框架是由[Mesosphere](https://mesosphere.com)開發的[Marathon](https://mesosphere.github.io/marathon/),它提供了一個遠程的REST API來方便用戶編排。
如果想了解有關Mesos的更多詳細內容,不妨去讀一讀它的這篇[白皮書](http://mesos.berkeley.edu/mesos_tech_report.pdf)。
與Mesos相比,Kubernetes算是一個非常年輕的產品,它2014年6月才開始啟動,在2015年7月發布了1.0版本。然而,它有一段很長的發展歷史,實際上它是Google自Omega和之后的Borg這樣的內部調度系統創建的一個開源版本。
這篇由Google發表的關于[Borg](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43438.pdf)的論文簡要講述了該產品宏大的發展歷程,還有Google是如何在內部基于容器的架構來完成調度的。憑借如此宏大的背景,Kubernetes自然也引起了很多人的注意,而如今它已經被CoreOS和Red Hat這樣的企業開發和采納。
Kubernetes覆蓋了應用部署涉及的整個范疇:調度、更新、維護和擴展。它支持Docker容器,并且將它們歸類到名為“pod”的一個個小集群的組,“pod”共享一個網絡命名空間,如此一來它們之間的網絡通信就變得簡單很多;這也就意味著一個小組的容器可以共同組成一個單獨的應用,如Web應用和Redis存儲。
使用Kubernetes的API可以管理容器、pod、備份、卷、私密信息、元數據以及所有其他組件,而且支持多方面的用途,這一點也正是Kubernetes的主打功能。由于該API的設計原型源自于Google的早期項目,因此可以說它已經經受了很好的實際考驗。
Kubernetes目前還不是一個易于安裝的組件。業界比較清晰的教程當屬Red Hat的Kubernetes[初學向導](https://access.redhat.com/articles/1353773)了,然而這里面仍然有很多手動配置的步驟。引入Docker可能會減輕必要的配置管理的工作量,但是為它構建一個完整的調度環境仍然需要花費很大力氣。
到目前為止,最容易上手的體驗方案當然還是使用Google容器引擎的實現。有一篇必看的[Wordpress入門教程](https://cloud.google.com/container-engine/docs/tutorials/hello-wordpress),快速、簡單、易于上手,在部署完成時它將會為用戶安裝和配置好Kubernetes標準的“kubectl”命令行工具,如此一來用戶便可以在集群里輕松運行屬于自己的Docker容器了。
調度系統可以說是平臺即服務(PaaS)的一塊基石,而Kubernetes正成為這一領域里的核心成員。
Red Hat的[OpenShift](https://www.openshift.com)是一個開源(并且已經托管了)的平臺即服務的產品。在它發行版本3之前它可以說是一個相當于標準的開源版的“Heroku”PaaS產品,但是版本3是在[Atomic項目](http://www.projectatomic.io),特別是Docker和Kubernetes的基礎之上做了一次[**完全重寫**](https://blog.openshift.com/openshift-v3-platform-combines-docker-kubernetes-atomic-and-more/),Atomic項目本身是Red Hat應對CoreOS的舉措,它是其他一大堆開源項目的載體。
OpenShift于2015年6月發布后全面上市,當你希望得到的是一個完全成熟的PaaS時,它會是一個很好的切入Kubernetes的方法。
OpenShift是怎樣適配容器的?為了獲得一個完整的了解,我們專門采訪了Red Hat公司OpenShift項目的首席工程師Clayton Coleman先生。
**記者**:“OpenShift v3是否真的是一款自吹自擂的產品又或者說是一堆開源項目打包在一起的可以隨時被替換掉的‘杰作’?顯然在某種程度上來說它是的,但是我更感興趣的是它是如何在面向你的客戶的產品以及面向你的開發過程的工具兩者間協調工作的。”
**Clayton Coleman**:“OpenShift是一系列建立在Kubernetes之上的部署、構建和應用生命周期管理工具的集合。對于每個單獨的組件,我們盡量不去采用那些明擺著非常偏門的技術。例如,針對邊緣路由[\[2\]](part0019.xhtml#anchor132)和代理我們寫了一個通用的模式,它兼容了Apache、Nginx、F5和許多其他的可配置的負載均衡器,但是在默認情況下我們還是采用最常見的HAProxy來配置。我們自然希望可以從多方途徑獲取支持,但是我們目前主要還是采用Git。Kubernetes正在加入對Rocket容器引擎的支持,因此OpenShift可以利用這一點,也許最終可以替代Docker。我們試圖成為眾多不同的概念和開發工作流之間關聯的紐帶,而不是假定所有應用都是一個自頂向下的生命周期。”
**記者**:“PaaS如何才能在企業里真正落地?誰是最早的先驅,在此過程中又作了哪些決策?容器和Docker的加入會加快推進的步伐嗎?顯然就目前而言,它是一個很大的革新,并且這中間變化的過程也相當有趣。”
**Clayton Coleman**:“企業內的PaaS一般都是由大量的內部應用的部署成本所驅動,他們希望借助PaaS可以將部署模式和流程標準化,并且希望可以一定程度地提高基礎設施和工具的靈活性。我想Docker帶來的更多的是自下而上的變革,原因在于它是一項低門檻的技術,對于一些簡單的任務它可以完成的很好。我們已經看到許多企業將Docker應用到了平常頻繁接觸的操作流程里,在這里運維和開發團隊在以鏡像為原子單位的應用部署中受益。當然,它也有不利的一面。在Docker引入容器帶來巨大的提升的同時仍然需要實施大量的定制/腳本化的流程,相比之下我們認為引入更大規模的集群管理模式(通過集群管理/PaaS工具)可以帶來更大的收益(一個是呈倍數級的改善,一個則是附加增量形式的迭代)。”
**記者**:“你把Kubernetes里的pod模型描述成一個微型的虛擬機,例如,一組應用通常是通過磁盤和Unix套接字在本地進行通信來協同工作的,而不是通過遠程的網絡IO傳輸。有沒有什么工具可以用來做這種結構的現有應用的遷移,將它們按照現有的[Red Hat容器安全標準](https://securityblog.redhat.com/2015/04/29/container-security-just-the-good-parts/)的規格改造成具備更高安全性的標準結構?所以,你有沒有看到過一個現有應用的實際遷移的案例,或者說業界有沒有大規模在PaaS上運行新的應用的案例?”
**Clayton Coleman**:“如今我們所看到的結構是新舊模式之間[\[3\]](part0019.xhtml#anchor133)的分裂,因此選擇其中任何一種應用模式都會趨向于在某方面設計的過分簡單化,這就導致它在另外一種模式下很難正常運轉。一個好應用的組成應該比‘僅僅只是一個容器’要復雜得多,并且它們通常需要抽象的是共享的磁盤資源、本地網絡,或者是不應該耦合到主容器里的‘側艙’形式的客戶端。因此從第一天萌生組建它的想法時你就需要建立一個本地聚合的概念來幫助將新舊應用模型有機的結合到一起。”
**記者**:“不可變基礎架構和Atomic項目都是時下非常新興和熱門的,在你看來,對不可變基礎架構的市場需求有多大?你如何看待這個趨勢?”
**Clayton Coleman**:“為了使得基礎架構的不可變性真正落地,你必須以一種可重復的方式來不斷地實施變更。新的不可變鏡像的流動完全取決于一個自動化的且易于理解的構建流。不可變基礎架構的優點是減少了重現問題或者跟蹤故障時需要理解的變量的數量,但是反過來,不可變基礎架構本身的成本在于將一切事物自動化。”
**記者**:“其他的一些廠商似乎在推容器遺留的一些東西,即在一個完全可變的系統里運行多個應用,而Red Hat似乎是想推動一個應用對應一個容器的結構,必要的話再圍繞著構建一系列的pod。這樣的描述正確嗎?這是否是由客戶需求驅動的?”
**Clayton Coleman**:“有時候,構建一些較復雜的容器是很有必要并且也很有價值的。一般來說,減少復雜度是一個很合理的做法,但是常常會有一些實際的需求必須通過一些復雜的容器來滿足,而我們仍然可以從圍繞著它們所構建的彈性基礎設施中受益。我認為容器化的基礎設施所取得的收益在很大程度上取決于你所處的環境將責任域切分成多個子組件的實現能力(這便是人們提倡的SOA,或者說最近比較熱門的微服務概念)。但是這仍然不能排除不存在大的、可變的,或者復雜的容器。”
在第14章里,我們將介紹Docker中的服務發現,它可以使一個計算機網絡上的任意服務都可以找到它需要通信的其他服務。
- - - - - -
[\[1\]](part0019.xhtml#ac131) 它是Mesos Slave節點的一部分,可以將其理解為是Mesos中任務的容器。——譯者注
[\[2\]](part0019.xhtml#ac132) 將客戶連接到因特網的路由器被稱為邊緣路由器。——譯者注
[\[3\]](part0019.xhtml#ac133) 即把Docker當做簡單的微型虛擬機和完全容器化的應用架構兩套模式。——譯者注
- 版權信息
- 版權聲明
- 內容提要
- 對本書的贊譽
- 譯者介紹
- 前言
- 本書面向的讀者
- 誰真的在生產環境中使用Docker
- 為什么使用Docker
- 開發環境與生產環境
- 我們所說的“生產環境”
- 功能內置與組合工具
- 哪些東西不要Docker化
- 技術審稿人
- 第1章 入門
- 1.1 術語
- 1.1.1 鏡像與容器
- 1.1.2 容器與虛擬機
- 1.1.3 持續集成/持續交付
- 1.1.4 宿主機管理
- 1.1.5 編排
- 1.1.6 調度
- 1.1.7 發現
- 1.1.8 配置管理
- 1.2 從開發環境到生產環境
- 1.3 使用Docker的多種方式
- 1.4 可預期的情況
- 為什么Docker在生產環境如此困難
- 第2章 技術棧
- 2.1 構建系統
- 2.2 鏡像倉庫
- 2.3 宿主機管理
- 2.4 配置管理
- 2.5 部署
- 2.6 編排
- 第3章 示例:極簡環境
- 3.1 保持各部分的簡單
- 3.2 保持流程的簡單
- 3.3 系統細節
- 利用systemd
- 3.4 集群范圍的配置、通用配置及本地配置
- 3.5 部署服務
- 3.6 支撐服務
- 3.7 討論
- 3.8 未來
- 3.9 小結
- 第4章 示例:Web環境
- 4.1 編排
- 4.1.1 讓服務器上的Docker進入準備運行容器的狀態
- 4.1.2 讓容器運行
- 4.2 連網
- 4.3 數據存儲
- 4.4 日志
- 4.5 監控
- 4.6 無須擔心新依賴
- 4.7 零停機時間
- 4.8 服務回滾
- 4.9 小結
- 第5章 示例:Beanstalk環境
- 5.1 構建容器的過程
- 部署/更新容器的過程
- 5.2 日志
- 5.3 監控
- 5.4 安全
- 5.5 小結
- 第6章 安全
- 6.1 威脅模型
- 6.2 容器與安全性
- 6.3 內核更新
- 6.4 容器更新
- 6.5 suid及guid二進制文件
- 6.6 容器內的root
- 6.7 權能
- 6.8 seccomp
- 6.9 內核安全框架
- 6.10 資源限制及cgroup
- 6.11 ulimit
- 6.12 用戶命名空間
- 6.13 鏡像驗證
- 6.14 安全地運行Docker守護進程
- 6.15 監控
- 6.16 設備
- 6.17 掛載點
- 6.18 ssh
- 6.19 私鑰分發
- 6.20 位置
- 第7章 構建鏡像
- 7.1 此鏡像非彼鏡像
- 7.1.1 寫時復制與高效的鏡像存儲與分發
- 7.1.2 Docker對寫時復制的使用
- 7.2 鏡像構建基本原理
- 7.2.1 分層的文件系統和空間控管
- 7.2.2 保持鏡像小巧
- 7.2.3 讓鏡像可重用
- 7.2.4 在進程無法被配置時,通過環境變量讓鏡像可配置
- 7.2.5 讓鏡像在Docker變化時對自身進行重新配置
- 7.2.6 信任與鏡像
- 7.2.7 讓鏡像不可變
- 7.3 小結
- 第8章 存儲Docker鏡像
- 8.1 啟動并運行存儲的Docker鏡像
- 8.2 自動化構建
- 8.3 私有倉庫
- 8.4 私有registry的擴展
- 8.4.1 S3
- 8.4.2 本地存儲
- 8.4.3 對registry進行負載均衡
- 8.5 維護
- 8.6 對私有倉庫進行加固
- 8.6.1 SSL
- 8.6.2 認證
- 8.7 保存/載入
- 8.8 最大限度地減小鏡像體積
- 8.9 其他鏡像倉庫方案
- 第9章 CI/CD
- 9.1 讓所有人都進行鏡像構建與推送
- 9.2 在一個構建系統中構建所有鏡像
- 9.3 不要使用或禁止使用非標準做法
- 9.4 使用標準基礎鏡像
- 9.5 使用Docker進行集成測試
- 9.6 小結
- 第10章 配置管理
- 10.1 配置管理與容器
- 10.2 面向容器的配置管理
- 10.2.1 Chef
- 10.2.2 Ansible
- 10.2.3 Salt Stack
- 10.2.4 Puppet
- 10.3 小結
- 第11章 Docker存儲引擎
- 11.1 AUFS
- 11.2 DeviceMapper
- 11.3 BTRFS
- 11.4 OverlayFS
- 11.5 VFS
- 11.6 小結
- 第12章 Docker 網絡實現
- 12.1 網絡基礎知識
- 12.2 IP地址的分配
- 端口的分配
- 12.3 域名解析
- 12.4 服務發現
- 12.5 Docker高級網絡
- 12.6 IPv6
- 12.7 小結
- 第13章 調度
- 13.1 什么是調度
- 13.2 調度策略
- 13.3 Mesos
- 13.4 Kubernetes
- 13.5 OpenShift
- Red Hat公司首席工程師Clayton Coleman的想法
- 第14章 服務發現
- 14.1 DNS服務發現
- DNS服務器的重新發明
- 14.2 Zookeeper
- 14.3 基于Zookeeper的服務發現
- 14.4 etcd
- 基于etcd的服務發現
- 14.5 consul
- 14.5.1 基于consul的服務發現
- 14.5.2 registrator
- 14.6 Eureka
- 基于Eureka的服務發現
- 14.7 Smartstack
- 14.7.1 基于Smartstack的服務發現
- 14.7.2 Nerve
- 14.7.3 Synapse
- 14.8 nsqlookupd
- 14.9 小結
- 第15章 日志和監控
- 15.1 日志
- 15.1.1 Docker原生的日志支持
- 15.1.2 連接到Docker容器
- 15.1.3 將日志導出到宿主機
- 15.1.4 發送日志到集中式的日志平臺
- 15.1.5 在其他容器一側收集日志
- 15.2 監控
- 15.2.1 基于宿主機的監控
- 15.2.2 基于Docker守護進程的監控
- 15.2.3 基于容器的監控
- 15.3 小結
- DockOne社區簡介
- 看完了