你們好,我是來自 CODING 的全棧開發工程師,我有幸在 CODING 參與了 Coding-Job 這個容器化的編排平臺的研發。你們對 CODING 可能比較瞭解, Coding.net 是一個一站式開發平臺,具備代碼託管,任務管理,產品演示和 WebIDE 等功能。總體功能看起來比較複雜且較爲分散。前端
這是咱們 Coding 的架構演進流程。那麼怎麼評判一個系統復不復雜,我的以爲看兩個指標,一個就是運維人員用多久時間能夠把新的代碼部署上線。好比說我以前所在的創業團隊,每次部署都是晚上九點之後不多訪問量的時候進行,部署當天晚上吃飯時還說半小時就差很少能夠更新上線了。最後發現四五個小時過去了尚未上線。爲何呢?可能有一些前端的樣式問題,有些配置文件沒有作好。爲何會找出這些問題?不少狀況下是由於,咱們線上和線下狀態的不統一。這是其中一個指標,還有一個指標就是,一個新同事來到公司之後,要多長時間能夠把整個系統的開發環境部署起來。好比說我是一個後端程序員,得先要裝一個虛擬機,Nginx 服務器,數據庫,具體語言的編譯環境,以及運行 npm install 來安裝一些前端的代碼庫,這些操做,加上國內有奇葩的網絡問題,咱們一般會耗費半個到一個工做日之久。而用了 Coding-Job 來部署本地開發環境,這個時間能夠下降到半個小時。
Coding 網站一開始是一個簡單的 Java War 包,編譯、打包、上傳,再到上線,還算是一個比較簡單的操做。隨着業務的快速發展,好比說有短信功能,有郵件、短信推送,這些東西如何把它融合在一塊兒,是一件比較棘手的事情。前兩年微服務也比較火,咱們也採用了微服務的架構,容許開發者用他最拿手的框架和語言,選一個微服務來作。好比說短信,短信模塊寫好了,接口文檔寫好,發給寫 Coding 後臺的同事,而後一下功夫就完事兒了。
這種方式使咱們的代碼可以跟的上 Coding 迅猛的業務發展。但咱們忽然發現一個問題,就感受全公司已經沒有人會清楚的記得每一個微服務是怎麼編譯、配置和啓動的了。因而就想,有沒有這樣一種東西,跟黑盒子同樣,可讓咱們把一堆代碼放到裏面去。不用管黑盒子是怎麼配置運行的,把它放到線上去,讓它能跑起來,代碼在裏面怎麼配置是盒子的事情,我外面環境脫胎換骨,也不影響盒子裏面的正常運行。這個黑盒子,好像就能夠解決咱們的問題,而這,其實就是虛擬化技術。在 2015 年初 Docker 剛火起來的時候,咱們研究了當時傳統方案虛擬機和 Docker 技術,最後採用了 Docker。git
圖中是傳統方案-虛擬機的架構圖,咱們在買了主機(Infrastructure)裝了宿主機操做系統(Host Operating System)之後,想要實現虛擬化技術,就要安裝一套虛擬機管理軟件(Hypervisor),好比 VMware vSphere,它能夠容許跑三個黑盒子,其實就是虛擬機,在三個虛擬機裏面分別跑三個操做系統(GuestOS),而在這三個操做系統之上,咱們才運行咱們真正想要運行的東西,即 App一、App二、App3 這三個微服務。那麼想回來,爲何咱們要奇奇怪怪地啓動三個虛擬機呢?爲何要將大量地物理資源耗費在無關緊要的上層操做系統上面呢?程序員
人們終於想清楚了這件事情,就開始着手研究輕量級的虛擬化技術。Liunx 內核的命名空間(Name Space)和 控制組 Cgroups (Control Groups) 等技術應運而生,實現了進程間命名空間的隔離,網絡環境的隔離,和進程資源控制。Docker 正是依靠這些技術忽然火熱了起來。咱們在用 Docker 跑微服務的時候,圖中的虛擬機管理軟件被替換成了一個 Docker Engine,每一個 App 跑在 Docker 容器中,容器與宿主機共享一個操做系統,它能節省較多的物理資源,啓動速度也由虛擬機的分鐘級達到秒級,I/O 性能也接近直接運行與宿主機上。
說到命名空間,你們都知道 Linux 只會有一個 PID 爲 1 的進程 init,有了命名空間之後,在每一個進程命名空間裏面,PID 列表是相互隔離的,在容器裏面,也能夠有一個 PID 爲 1 的進程,這就實現了進程間命名空間的隔離。而控制組,它是能夠作到對每一個容器所佔用的物理資源的控制,好比指定 CPU 的使用,塊設備的讀寫速度。結合這兩種技術,咱們能夠作到的是讓 Docker 容器在保持必定性能的同時,儘量地在隔離性上面接近虛擬機。github
因而咱們就將 Coding 的微服務一個一個地遷移到 Docker 內,以容器的方式部署運行,而後你會覺得是這樣一幅場景。docker
但事實上多是這樣的。在 Coding 咱們的大大小小的微服務五十餘個,就像雞蛋不能放在一個籃子裏同樣,容器也不能被放在同一臺雲主機上面,而是整整齊齊地分開來放,否則怎麼能叫分佈式呢?咱們的微服務,對於文件系統,對於彼此的網絡還存在一些依賴性,像一個蜘蛛網同樣串在一塊兒,對於微服務所容許的主機位置和相應的主機配置都是有要求的。因此是須要一箇中心化的東西去幫咱們去存放每一個服務的配置以及它們運行的代碼甚至 Docker 鏡像。而這個中心化的東西所作的事情就是編排,就好像咱們在聽音樂會上的時候,臺上的指揮家同樣,它告訴這臺雲主機作什麼任務,以什麼配置運行這個任務,用什麼代碼來執行它,而後再告訴另一臺機器,又作什麼任務等等。數據庫
爲此咱們研究了兩款業界比較火的兩款開源容器管理框架,也就是這個中心化的東西。
Apache Mesos 是一個用於集羣的操做系統,它會把一個集羣所具備的全部物理資源抽象成一臺計算機供用戶使用,好比你的集羣裏有一百臺服務器,每臺服務器一個 CPU,那直白的來講 Apache Mesos 能給你一臺具備一百個 CPU 的電腦。apache
Mesos 作的比較好一點的是物理資源的分配。圖中能夠看到 Mesos 具備 Framework 的概念,它等同於咱們一般所說的應用程序,每一個 Framework 由調度器(Scheduler)和執行器(Executor)兩部分組成。調度器負責調度任務,執行器負責執行任務。你們能夠看到圖中間部分有 Mesos Master,圖下方有幾個 Mesos Slave, Master 是管事的,至關於包工頭,而 Slave 是奴隸,負責幹活兒的。這些 Slave 其實就是咱們的雲主機,每個 Slave 就是一臺主機,這邊能夠看到一個物理資源分配的流程,首先 Slave1 發現它有 4GB 內存和 4CPU 的空閒物理資源,因而它向 Mesos Master 彙報,詢問它有沒有任務能夠作,而後 Mesos Master 會詢問當前可用的 Framework1 的調度器有沒有能夠分配給 Slave1 的活兒,調度器按照空閒物理資源取出了兩個任務迴應給 Mesos Master,而後 Mesos Master 又轉發給 Slave1,Slave1 又開始幹活了。固然此時你們會發現 Slave1 上面還有 1GB 內存和一個 CPU 是空閒的,那麼它能夠經過 Mesos Master 請求 Framework2 去請求任務來作。
因此 Mesos 能帶給咱們的好處,一是高效,咱們經過對主機的物理資源量化以及對任務的資源需求量化,使得多個任務能在同一臺主機上運行,並充分利用其物理資源,二是可擴展性,Mesos 提供五十多種 Framework 幫助咱們處理多種多樣的任務。npm
另一款名叫 Kubernetes 的 Docker 容器集羣管理框架也特別火熱,它借鑑了谷歌的 Borg,爲容器化系統提供了資源調度,部署運行,服務發現等各類功能。它更加傾向於容器的編排,具備系統自愈功能,咱們來看一下它的架構。後端
我先講架構圖,左邊是一個控制的節點,右邊是一臺臺的 Slave,我能夠在左上角用 kuberctl 提交一個做業,讓 kubernetes 幫你把做業分配一個 Slave 上面去。在 Kubernetes 裏面,調度任務的單位是 Pod,中文是豆莢。就像豆莢同樣,裏面是有不少豆子的,那這些豆子是什麼呢?這些豆子實際上是咱們的 Docker 容器,這些容器共享一個豆莢,在 Kubernetes 裏面就是一個共享容器組的概念。全部在一個 Pod 裏面的 Docker 容器都是共享命名空間的,這解決了一些特殊場景的需求。由於使用 Docker 的最佳實踐是在每個容器裏面只作一件事,不把 Docker 容器當作虛擬機來用。而這意味着有些時候,好比 B 進程須要監測(watch) A 進程的進程號(PID)或者和 A 進程進行 IPC 通訊。若是是一個容器一個命名空間,這顯然是不能直接實現的,而這就是 Pod 的應用場景。服務器
那麼咱們如今來看一下一個 Pod 是怎麼來定義,這邊所示的 Pod Definition 是咱們經過 kubectl 向 Kubernetes 提交做業的配置文件。在 Pod 中有多個 Container, 這裏它定義了一個 Container 是 Nginx 以及 Nginx 的一些配置,例如鏡像名和容器端口。
接下來咱們講一下它的自愈系統,kubernetes 經過副本控制器(Replication Controller)來實現一個或者多個 Pod 的運行的控制。上圖是咱們向 Replication Controller 提交的配置文件,Template 字段表明了這個 Controller 使用的 Pod 的模板,而 Replicas 字段表明了咱們但願 kubernetes 用這個 Pod 模板產生多少個 Pod 同時運行,這涉及到 kubernetes 裏面一個叫理想狀態(desired state)的概念。提交了這個配置文件之後,若是有任何一個 Pod 由於某種緣由 Down 掉了,那麼原先應該運行三個的副本只剩下兩個,而 kubernetes 會想辦法達到理想狀態,所以它會啓動一個新的副本,最終變成三個副本,這個就是 kubernetes 的自愈系統。
那麼聽了 Mesos 的資源管理和 kubernetes 的自愈管理之後,我以爲它們作的已經至關成熟了,然而想要把它們投入到生產當中,可能仍是會遇到一些坑的。好比想用 Mesos,那麼首先要考慮咱們 Coding 一般所運行的服務是一些常駐服務,不是批處理任務,因此須要額外安裝 Marathon 這樣的長期運行任務管理框架(Framework)來作到這件事情。如果在生產環境中遇到性能問題,怎麼去調優也是一個棘手的事。而在 kubernetes 中,咱們並不想讓自愈系統替咱們把服務恢復在任何一臺機器上,目前 Coding 仍是有着一些對機器和運行位置有着苛刻要求的微服務。除此以外,這兩款框架也在迅速的開發迭代中,文檔也仍需補充,因此投入到生產中,咱們認爲爲時過早。咱們考慮到使用原生的 Docker API 就能夠作到大部分咱們想要用到的功能,因而咱們就開始本身開發了一套容器編排平臺,Coding-Job。
Coding-Job 的任務配置分紅三層,Service、Job 和 Task,圖中的 Service 是核心(core)服務,說明它是被衆多服務所依賴的。每一個 Job 定義了一件要作的事情,而每一個 Job 要具體被分配去作的時候,會細分爲 Task,每一個 Task 可使用不一樣的配置,運行在不一樣的機器上。
Coding-Job 是 C/S 架構的,在客戶端,有命令行版的操做。分別是 Push(推送配置文件操做)和 UP/DOWN/UPDATE 這些對 Job 的啓動、中止和按照配置更新 Job。
此外 Coding-Job 的服務器端會展示一個 WebUI 界面,咱們能夠經過 WebUI 來獲知每一個容器的運行狀態,經過 INSPECT 操做來提供容器的具體信息(與 docker inspect 一致),LOG 功能供查看容器 LOG,History 是顯示每一個 JOB 的歷史容器的配置。此外,Coding-Job 服務端會定時請求各 Slave 系統資源使用及Docker 運行數據,這些數據也會在 WebUI 上顯示出來。
這是 Coding-Job 的使用架構圖,Host-一、Host-2 這些是 Slave 機器。開發者在拉取最新的代碼之後,用預先提供的打包命令生成一個 docker image 文件,經過 docker push 把鏡像上傳到私有 Docker Registry 中。同時開發者會根據最新的代碼標籤更新任務配置文件,經過 Coding-Job 客戶端 PUSH 到 Coding-Job 服務端所使用的 etcd 中。這個 etcd 起到了存儲任務配置信息的做用,因爲它是可監測變化的存儲(watchable storage),在收到新的任務配置信息後,Coding-Job 服務端就能夠各類方式通知到運維人員(Ops)。運維人員若是確認要更新線上代碼,調用 Coding-Job 客戶端的 UP 或者 UPDATE 命令,就可讓 Slave 機器開始下載新的代碼鏡像來運行,因而更新就完成了。
那麼好處是什麼呢?首先是,一分鐘發佈新組件(服務)再也不是夢,在此基礎上,歷史容器的功能加上每次更新後任務配置提交到 Git 倉庫中,容許咱們追溯每一個組件的歷史線上版本。其次,咱們能夠利用它在公司內網環境內五分鐘啓動一個跟線上幾乎一致的 Staging 環境,這個 Staging 可被用於新功能的內測。最後一個優勢,是咱們在使用 Coding-Job 的過程當中,開發者再也不對線上環境一頭霧水,而是全透明的,能夠知道生產環境是怎麼組建的。在運維檢查整站狀態的時候,能夠經過 WebUI 得到一些編排意見,甚至這個只讀的 WebUI 能夠開放到公網上,讓全部人均可以看到 Coding 的服務提供狀態。
Happy Coding ; )
References