一、物理機時代
在虛擬機出現前的業務環境中,應用往往部署在物理機器上,但這樣的部署方式存在一些弊端:空閑資源難以得到復用,部署異構系統時需要重新采購物理資源,大量中小容量的機器使得運維成本提升。在這樣的情況下,如何降低基礎設施的管理成本便成為急切的需求。
二、虛擬機時代
虛擬機的出現使得用戶在一台物理機上能夠獨立運行多個相互隔離的系統,通過對資源的抽象化使得主機資源能夠被有效復用,這對於企業IT管理十分有益。然而,虛擬機同樣也會帶來一些問題:大量獨立系統的運行會占用許多額外開銷,消耗宿主機器資源,資源競爭時可能會嚴重影響系統響應;此外,每運行新的虛擬機都需要重新配置一遍環境,和在物理機上的情況基本無異,重復的環境配置操作則會消耗開發和運維人員的工作時間。此時需求便關注到如何減少虛擬化時的資源損耗,同時還能保證隔離性,以及使應用的上線周期更短,這便引導了容器技術的發展。
三、容器化時代
從2000年開始,各家類Unix操作系統廠商開始陸續推出容器相關的項目,2008年Google的Cgroups貢獻給Linux kernel 2.6.24后創造了LXC( Linux Containers),實現了多個獨立的Linux環境(容器)可運行在同一個內核。對於一個完整獨立運行環境來說,需要包含三個關鍵:環境隔離、資源控制和文件系統。在LXC中則分別通過Namespace、Cgroups、rootfs來實現相應的能力。
環境隔離——Namespace:LXC將內核全局資源封裝,每個Namespace都有一份獨立的資源,使得不同的進程在各自Namespace內對同一種資源的使用互不干擾,不會影響其他Namespace下的資源,實現了進程隔離。
資源控制——Cgroups:LXC通過Cgroups對資源進行控制,限制和隔離一組進程對系統資源的使用。在Cgroups出現之前OS只能對一個進程做資源限制,而 Cgroups可以對進程進行任意分組,如何分組由用戶自定義,借此實現對於一個Namespace的資源調度管理。
文件系統——rootfs:rootfs掛載在容器根目錄上,用來為容器進程提供隔離后執行環境的文件系統。rootfs包含一個操作系統所涉及的文件、配置和目錄,在Linux 操作系統內核啟動時,內核會先掛載一個只讀的rootfs,當系統檢測其完整性之后,決定是否將其切換到讀寫模式。
在通過LXC構建容器后,一台宿主機能夠實現多個相互隔離應用的運行。同時,共享內核使得每個容器又很輕量,解決了運行大量隔離應用時虛擬機資源消耗過重的弊端。然而,LXC雖解決了應用隔離的問題,但卻只是輕量的容器技術,沒有解決各平台軟件交付標准不統一的問題,如不同的軟件交付工具、應用運行規范不統一、環境依賴復雜等帶來的配置開銷。這些問題使容器技術的推廣依然比較有限,直到Docker的出現。
四、Docker
早期Docker是基於LXC開發,因此Docker容器也有着和LXC相似的特性,僅需要較少資源便可以啟動。但不同於LXC,Docker除了容器運行,還是一個打包、分發和運行應用程序的平台。Docker允許將應用和其依賴的運行環境打包在一起,打包好的“集裝箱“(鏡像)能夠被分發到任何節點上執行,無需再進行配置環境的部署。這樣使得Docker解決了開發和部署應用時環境配置的問題,規范化了應用交付和部署,降低了部署測試的復雜度以及開發運維的耦合度,極大提升了容器移植的便利性,便於構建自動化的部署交付流程。
Docker和虛擬機都是資源虛擬化發展的產物,但二者在架構上又有區別。虛擬機通過Hypervisor虛擬化主機硬件資源,然后構建客戶機操作系統,由宿主機的管理程序管理;Docker直接運行於主機內核,應用在主操作系統的用戶空間上執行獨立任務,不需要從操作系統開始構建環境,賦予了應用從交付到部署再到運維的獨立性。
虛擬機的啟動時間可能是分鍾級的,而Docker容器創建是秒級別。對於硬盤的使用Docker一般為MB級別,遠小於包含操作系統的虛擬機GB級磁盤使用量。對於操作系統來說,能支持運行的Docker容器數量遠多於虛擬機。
那么,如何用Docker啟動一個應用呢?對於啟動應用來說,首先需要獲得程序本身和所需要的環境配置。Docker會將應用運行所有需要的靜態資源如代碼、運行時環境、配置封裝為鏡像,鏡像通過Union FS采用分層存儲架構,通過描述文件Dockerfile指令構建鏡像層。
Dockerfile里包含多條指令描述該層應當如何構建,隨着鏡像層的逐層疊加,將一個完整的鏡像所需要的信息全部包含。外部則通過統一文件系統將相互疊加的層整合起來,以只可讀的統一文件(Union Read-Only File System)形式展現,這樣的分層存儲使得鏡像的復用和定制變的更為容易,壓縮了存儲空間。
鏡像運行之后的實體是容器。Docker容器同樣采用分層存儲,通過在鏡像頂部增加一層可讀可寫層,外部以可讀寫的統一文件(Union Read-Wirte File System)形式展現。當容器運行時所有的進程操作均在可讀可寫層,而下面的鏡像則不會被修改。容器的實質是進程,運行於自己獨立的命名空間。容器存儲層的生命周期和容器一樣,一旦容器消亡,存儲層也一並消亡,所以原生的容器是無狀態的,這也就是為什么之后編排系統會引入有狀態服務和持久化存儲以支持有狀態服務。
Docker的鏡像和容器通過三端的服務操作和管理:請求端Docker Client、主機端Docker Host和遠端拉取鏡像的倉庫Registry。Docker Client負責接收指令,與Docker Host下的守護進程Docker Daemon進行交互。Host提供了執行和運行應用程序的完整環境,其中的Docker Daemon用於管理Docker鏡像、容器、網絡和存儲卷,負責所有與容器相關的操作如拉取鏡像、創建容器等,會不斷偵聽Docker API請求並進行處理。Registry則是鏡像管理的倉庫,用戶可以將創建的鏡像提交到倉庫進行存儲,同時方便從倉庫拉下來鏡像為自己所用。
Docker容器技術在快速部署、環境標准化、隔離性方面的優勢得到了開發人員普遍認可,但是如果以一個完整PaaS平台為標准來衡量這些還不夠。首先,Docker提供了名為“容器”的隔離環境,但是面對多容器間有拓撲有關聯的場景,Docker還難以應對;其次,雖然容器解決了應用交付規范問題,但難以實現完全應用托管;另外,隨着基礎設施規模的擴大,開發中心必將分布式化,調度問題就需要解決。
2014年7月Docker宣布收購單機容器編排軟件Fig(后來命名為Docker Compose),並在同年12月推出了自己的容器集群編排項目Docker Swarm。Docker Swarm成功打造了兩方面能力:1.多容器編排能力,支持通過YAML文件聲明多容器應用,並定義容器之間的關系;2.分布式調度能力,允許跨集群節點調度容器。Docker Compose和Dockers Swarm可以基本滿足開發人員對於PaaS平台的需求,對於Docker向平台化發展的規划具有基石一般的意義。至此,Docker未來的發展似乎很樂觀,但是隨着Google加入,容器市場的局面發生了重大改變。
五、Kubernetes
2014年Google開源了名為Kubernetes(簡稱K8S)的項目,它是由Google內部Borg項目而開源出來的容器集群管理系統。Kubernetes繼承了Google豐富的大規模集群運維的經驗和基因,能夠提供復雜的、大規模的容器編排管理服務。2015年Google發布了Kubernetes第一個商業版本,代表Kubernetes進軍生產級容器規模管理,也意味着開始與Docker競爭PaaS平台的未來版圖。
Kubernetes集群由兩類節點構成:Master Node和Worker Node。Kubernetes采用聲明式的設計,任何操作指令都通過聲明式API與Master通信。Master Node可響應API聲明,進行集群管理和容器調度。容器則運行在Worker Node,Worker負責響應Master指令,執行容器啟停等維護操作。
Kubernetes中最基礎的調度單元是Pod(而不是容器)。Pod內可以封裝多個容器,以及這些容器共享的各種資源,包括存儲、獨立網絡IP等。那么為什么不直接以容器為最小調度單位,而是以Pod呢?在上一篇文章中介紹過容器封裝了不同組進程,但如果兩個模塊關系很緊密,例如通過共享存儲通信,那分割為兩個容器就比較困難了。為此Kubernetes提出把具有這種緊密關系的容器封裝在一個Pod對象中調度,可以近似理解Pod就是輕量的虛擬機,每個容器是運行在虛擬機的應用。
除了緊密的容器關系,一個面向生產的編排系統必然應當支持更多的容器關系,所以Kubernetes還提供了Deployment無狀態多副本關系、StatefulSet有狀態多副本關系、Job一次性長任務等許多對象以滿足多樣的編排需求。這些對象的基礎調度單元還是Pod,由控制器Controller控制Pod對象的狀態來實現聲明的編排關系。
下面以Deployment為例,大致介紹K8S容器編排是如何實現的
Kubernetes需要聲明一個Deployment的運行規格,就是聲明一個對象運行所希望保持的狀態。譬如我們需要部署一個Web Server組件,為了負載均衡和高可用,准備運行三個副本,此時就需要通過YAML聲明一個Deployment對象,指定Replicas為3。
通過API提交創建Deployment對象請求后,Master Node開始根據聲明進行處理。首先Master的調度器(kube-scheduler)會把Pod規划到合適的Worker,並將指令下發給對應Worker。假設三個Pod分別調度到三個Worker,每個Worker的容器生命周期管理組件(kubelet)都接收到Master節點的指令,然后啟動Pod。
如果系統運行一段時間后,訪問流量變大,需要擴容以提升響應能力,通過聲明擴容后副本數就可以實現。命令形如:kubectl scale deployment nginx-deployment--replicas 4命令執行后,Master便會進行擴容操作,把Web Server的第四個Pod副本,調度到某個Worker Node上。
對於正常運行的集群,Kubernetes會不斷的收集容器和節點狀態,並維護開發者聲明的規格。如果某個Worker Node突然宕機,Kubernetes會根據資源和節點工作情況把該Worker上原有Pod重新安排到健康的節點,保證系統正常提供服務。
Kubernetes使開發人員和工程師擁有了快速處理大型項目所需的管理工具和基礎架構。從負載測試或創建過渡環境,到將業務和在線應用程序移至生產環境,Kubernetes集群都可以對其進行管理。
六、小結
Docker自2013年發布之后早已成為了首屈一指的容器平台,它能提供輕量的虛擬化和一致性環境,可廣泛應用於:將開發環境貼近生產環境,提高開發效率;簡化代碼流水線;整合服務器提高利用率;多租戶場景。未來,從容器應用的角度,Docker仍有廣闊的發展空間,在容器主導地位並不斷發揮其價值。
而容器編排也已經脫離概念普及階段,獲得廣泛落地。目前很多國內外廠商都提供了基於Kubernetes的產品化的解決方案,如亞馬遜 EKS、微軟AKS、阿里雲ACK等。除此之外還有很多廠商基於自身既有特點,發展着自己的容器編排項目,例如亞馬遜 ECS、星環TCOS等,提供了豐富的附加能力。