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