创业公司的容器化之路
作者簡(jiǎn)介: 章燁明,杏仁醫(yī)生CTO。中年程序員,關(guān)注各種技術(shù)和團(tuán)隊(duì)管理。本文首發(fā)杏仁醫(yī)生技術(shù)站
1. 創(chuàng)業(yè)公司的技術(shù)挑戰(zhàn)
托爾斯泰說:“幸福的家庭都是相同的,不幸的家庭各有各的不幸。”互聯(lián)網(wǎng)創(chuàng)業(yè)公司也一樣。大部分互聯(lián)網(wǎng)創(chuàng)業(yè)公司,都會(huì)碰到以下幾個(gè)技術(shù)挑戰(zhàn)。
- 如何快速、低成本的搭建系統(tǒng),同時(shí)確保安全穩(wěn)定?
- 如何快速的構(gòu)建和發(fā)布應(yīng)用,滿足業(yè)務(wù)需求?
- 如何提高團(tuán)隊(duì)開發(fā)效率,確保開發(fā)質(zhì)量?
這個(gè)列表肯定不完整,但這三個(gè)應(yīng)該是創(chuàng)業(yè)公司技術(shù)團(tuán)隊(duì)都會(huì)面臨的共通的問題。當(dāng)然杏仁不能說完全解決了這幾個(gè)問題,但還是取得了一些進(jìn)展。我們接下來簡(jiǎn)單介紹下,我們杏仁是怎么應(yīng)對(duì)這些挑戰(zhàn)的,以及容器又可以帶來什么?
該系列文章會(huì)分為三篇。第一篇介紹容器化之前,杏仁技術(shù)架構(gòu)的發(fā)展歷史。第二篇介紹容器以及杏仁的容器化方案。第三篇最后總結(jié)為什么我們認(rèn)為創(chuàng)業(yè)公司應(yīng)該用容器,以及為什么容器可以幫助我們應(yīng)對(duì)這三個(gè)挑戰(zhàn)。
2. 杏仁早期
在 2012 年以前,大部分互聯(lián)網(wǎng)公司包括創(chuàng)業(yè)公司,都是直接購(gòu)買服務(wù)器,租用 IDC 機(jī)房的機(jī)架部署的。應(yīng)用是直接運(yùn)行在物理機(jī)上的,要擴(kuò)展必須購(gòu)買新服務(wù)器。IDC 經(jīng)常出各種故障,如果碰到 IDC 遷移的話,就更痛苦,必須半夜搬機(jī)器,天亮前上線??傊畬?duì)創(chuàng)業(yè)公司的成本、服務(wù)穩(wěn)定性、工作效率都是有很大的消耗。
不過杏仁醫(yī)生很幸運(yùn),正好趕上了公有云的成熟,所以一開始就是基于公有云搭建的。杏仁醫(yī)生最早期的架構(gòu)如下:
這個(gè)架構(gòu)非常簡(jiǎn)單,其中負(fù)載平衡、數(shù)據(jù)庫(kù)都是基于騰訊云的。然后騰訊云也提供了一些基礎(chǔ)的監(jiān)控、告警和安全服務(wù)。然后就是兩個(gè)應(yīng)用,一個(gè)移動(dòng)后端 API,一個(gè)運(yùn)營(yíng)平臺(tái),都是基于 Play 的 Scala 應(yīng)用。
很多人可能會(huì)好奇為什么選擇 Scala/Play 進(jìn)行開發(fā),畢竟 Scala 在國(guó)內(nèi)應(yīng)該用的不多。這里一方面因?yàn)樾尤梳t(yī)生是繼承了看處方的架構(gòu),當(dāng)初看處方就是基于 Scala/Play 開發(fā)的, 團(tuán)隊(duì)對(duì)這一套方案比較熟悉。我們需要快速的構(gòu)建杏仁醫(yī)生,自然就會(huì)選擇最熟悉的語言和框架。而且對(duì)于中小規(guī)模的應(yīng)用,Scala/Play 的開發(fā)效率的確非常高。Scala 本身的表達(dá)能力非常強(qiáng),是一門很有意思的語言。很多好學(xué)的工程師,也會(huì)對(duì)新的語言也會(huì)比較感興趣。
3. 應(yīng)用拆分和CI/CD
經(jīng)過一年多快速演進(jìn),整個(gè)應(yīng)用越來越龐雜。所以我們對(duì)應(yīng)用進(jìn)行了拆分,并且隨著業(yè)務(wù)擴(kuò)張,應(yīng)用也越來越多,例如 HIS、CRM 等。所以我們的架構(gòu)變成了這個(gè)樣子。
這時(shí) Scala 最大的問題開始體現(xiàn)出來了,那就是編譯速度的問題。那時(shí)候我們的應(yīng)用部署方式也很原始,必須登陸服務(wù)器運(yùn)行一個(gè) Shell 腳本,它會(huì)拉取代碼,然后編譯、打包、運(yùn)行,整個(gè)過程需要耗費(fèi)5~10分鐘。而我們 API 應(yīng)用的節(jié)點(diǎn)后來增加到 5、6 臺(tái),即使兩臺(tái)同時(shí)發(fā)布,也需要 20 分鐘才能全部發(fā)布完成。如果發(fā)布后出了問題,那就吐血了,因?yàn)榛貪L也是一樣的流程,又需要 20 分鐘。
有一次我們做了一個(gè)送 Apple Watch 的活動(dòng),半夜 12 點(diǎn)開始。我們出了個(gè)很低級(jí)的 BUG,活動(dòng)一開始就蹭蹭蹭的一分鐘送好幾個(gè) Apple Watch。我們創(chuàng)業(yè)公司也沒多少錢,每一個(gè)都是白花花的銀子,心痛啊。修復(fù)很簡(jiǎn)單,但發(fā)布或者回滾都得先編譯,太慢了,于是我們一狠心把服務(wù)器給停了,幾分鐘后才部署上了新的代碼。
這是我們覺得必須要有一個(gè)自動(dòng)化的發(fā)布系統(tǒng)了。其實(shí)在幾年以前,發(fā)布都是需要運(yùn)維執(zhí)行的,研發(fā)提交給運(yùn)維,運(yùn)維手動(dòng)部署。那自然發(fā)布不可能很頻繁,并且對(duì)開發(fā)和運(yùn)維都是很大的負(fù)擔(dān)。但漸漸的敏捷和 Devops 的文化成為主流,持續(xù)集成和發(fā)布(CI/CD)成為一項(xiàng)基礎(chǔ)設(shè)施。
我們第一版 CI/CD 很簡(jiǎn)單,是基于 Jenkins 的,通過腳本進(jìn)行編譯、打包,然后拷貝到服務(wù)器上發(fā)布。因?yàn)橹灰虬淮渭纯?#xff0c;緩解了部署慢的問題。但還是存在幾個(gè)問題。
- 首先,沒有應(yīng)用倉(cāng)庫(kù)。打包是一次性的,部署的時(shí)候會(huì)備份當(dāng)前應(yīng)用目錄,用于回滾,所以只能回滾到上一個(gè)版本。
- 其次,健康檢比較簡(jiǎn)單,只能檢測(cè)應(yīng)用是否啟動(dòng)。我們遇到過應(yīng)用啟動(dòng)了,測(cè)活也沒問題,但服務(wù)還是有嚴(yán)重問題、基本不可用的情況。
- 最后,不支持灰度發(fā)布,出問題只能回滾。
期間我們有一次較大的故障,也是因?yàn)檫@幾個(gè)因素,花了很長(zhǎng)時(shí)間才恢復(fù)。痛定思痛,于是我們又開發(fā)了 Frigate 發(fā)布系統(tǒng),它的架構(gòu)大致如下圖。
Frigate 有一個(gè)應(yīng)用倉(cāng)庫(kù),即 App Repository。應(yīng)用倉(cāng)庫(kù)會(huì)保存發(fā)布的應(yīng)用版本,回滾的時(shí)候可以指定版本。
Watcher 組建實(shí)現(xiàn)了比較強(qiáng)大的應(yīng)用檢測(cè)功能。除了一般的 HTTP 檢測(cè),還可以從日志、監(jiān)控里獲取數(shù)據(jù),可以根據(jù)異常數(shù)、錯(cuò)誤率等進(jìn)行進(jìn)一步的健康檢測(cè)。
Frigate 支持分組和分階段發(fā)布。例如現(xiàn)發(fā)布2臺(tái)機(jī)器,然后健康檢查,或者中間可以有一些人工檢查,然后再發(fā)布剩余的機(jī)器。
后來回頭看,Frigate 雖然沒有使用容器,但其實(shí)是實(shí)現(xiàn)了容器編排的很多功能。Frigate 發(fā)布的截圖如下,這是基于 Jenkins Pipeline 的。
4. 微服務(wù)化
系統(tǒng)多了,依賴復(fù)雜、數(shù)據(jù)沒有隔離、邏輯重復(fù),接下來一個(gè)必然的方向就是微服務(wù)。關(guān)于微服務(wù),我們公眾號(hào)有兩篇文章(樂高式微服務(wù)化改造(上)、樂高式微服務(wù)化改造(下))對(duì)此有比較詳細(xì)的分析說明,這里就簡(jiǎn)單介紹一下。
我們的服務(wù)注冊(cè)和發(fā)現(xiàn)是基于 Consul 的,負(fù)載平衡是通過 Nginx 實(shí)現(xiàn)的。下圖是整個(gè)服務(wù)注冊(cè)和發(fā)現(xiàn)的過程:
有幾點(diǎn)是值得一提的。
首先,我們的微服務(wù)是基于 HTTP 和 Json 的,沒有采用二進(jìn)制的協(xié)議如 Protobuf、Thrift 等。其實(shí) HTTP 和二進(jìn)制協(xié)議的性能差別,并沒有很多人想的那么大,一般也就2、3倍的差距(沒有親測(cè))。對(duì)大部分企業(yè),這個(gè)差別根本就不是瓶頸,特別是現(xiàn)在還有 HTTP2。如果真的有需要,還可以在 HTTP2 上跑二進(jìn)制協(xié)議,通過框架在服務(wù)端和客戶端加一層就可以實(shí)現(xiàn)。
其次,我們的微服務(wù)對(duì)應(yīng)用是無侵入的。我們沒有采用常見的 Dubbo、SpringCloud 框架。一方面我們服務(wù)調(diào)用方有 Java 應(yīng)用也有 Scala 應(yīng)用,要接入還是要花點(diǎn)功夫。另一方面,我們認(rèn)為微服務(wù)框架發(fā)展的未來方向是非侵入性的獨(dú)立的微服務(wù)基礎(chǔ)設(shè)施層。其實(shí)這和容器編排的理念是一致的,并且最近提出的 Service Mesh 概念,就是進(jìn)一步的延伸,我們認(rèn)為這才是微服務(wù)的未來。
最后,我們每個(gè)微服務(wù)都會(huì)生成一個(gè) SDK,便于調(diào)用方調(diào)用。SDK 集成了熔斷、異步、分布式追逐(開發(fā)中)等功能。
搭建了微服務(wù)基礎(chǔ)框架后,我們開發(fā)了好幾個(gè)微服務(wù),有業(yè)務(wù)的例如訂單、預(yù)約等,有基礎(chǔ)設(shè)施的例如推送、短信等。當(dāng)然其實(shí)有些并不算“微”。
但是我們發(fā)現(xiàn),整個(gè)體系依然存在不少問題
基于云服務(wù),成本低了,效率高了。但運(yùn)維還是面向資源的,并且資源利用率不高。
有了持續(xù)集成和部署的能力。但新增節(jié)點(diǎn)、新建服務(wù)等,依然需要大量人工運(yùn)維,并且擴(kuò)展并不方便。
實(shí)踐微服務(wù),改進(jìn)了應(yīng)用架構(gòu)。但依賴管理、監(jiān)控等尚未完善,穩(wěn)定性仍然不夠。
5. 容器是什么?
上面我們簡(jiǎn)單說明了杏仁容器化之前的架構(gòu)發(fā)展。下面我們就來談?wù)勅萜鳌?/p>
2013 年 Docker 橫空出世,到 2015 年已經(jīng)漸漸進(jìn)入大家的視野。容器當(dāng)然不一定是 Docker,而且容器現(xiàn)在也是有標(biāo)準(zhǔn)的。但一說容器大家肯定會(huì)想到 Docker。所以我們這里說的容器,主要就是指 Docker。
容器到底是什么呢?顧名思義,容器就是用來裝東西的。在這里它用來裝的就是應(yīng)用程序。容器的特點(diǎn)簡(jiǎn)單說就那么四點(diǎn):
容器是自包含的,它打包了應(yīng)用程序及其所有依賴,可以直接運(yùn)行。以前應(yīng)用程序的依賴管理一直是個(gè)大問題,即使像 RPM 、Maven、Ansible 等都能解決一部分問題,但并沒有一個(gè)所有應(yīng)用程序通用的標(biāo)準(zhǔn)機(jī)制,直到容器出現(xiàn)。
容器是可移植的,可以在幾乎任何地方以相同的方式運(yùn)行。這就可以確保應(yīng)用在開發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境等都有完全一樣的運(yùn)行環(huán)境。
容器是互相隔離的,同一主機(jī)上運(yùn)行的多個(gè)容器,不會(huì)互相影響。即一個(gè)容器中運(yùn)行的應(yīng)用程序,是訪問不到其他容器的資源的(進(jìn)程、網(wǎng)絡(luò)、文件、用戶等),除非配置為共享的資源。
- 容器是輕量級(jí)的,體現(xiàn)在容器的秒級(jí)啟動(dòng),并且占用資源很少。
容器能做的很多事情,虛擬機(jī)也能做,那它們有什么區(qū)別呢?下面這張圖是 Docker 官網(wǎng)的截圖,很好的說明了兩者的區(qū)別。
但最根本的差別,其實(shí)就是最后一點(diǎn):輕量。很多人可能覺得這只是一個(gè)簡(jiǎn)單的差別,但其實(shí)不是。因?yàn)榫褪沁@一點(diǎn)使得容器可以成為一種 標(biāo)準(zhǔn)化的應(yīng)用發(fā)布方式。
上個(gè)世紀(jì) 5、60 年代集裝箱剛出現(xiàn),看上去也只是簡(jiǎn)單的差別,也沒有什么技術(shù)含量。但集裝箱提供了一個(gè)標(biāo)準(zhǔn)化的物流方式,全球的海陸空運(yùn)輸、碼頭裝卸等圍繞集裝箱形成了整個(gè)一個(gè)高效的物流體系。最終改變了世界貿(mào)易,促成了全球化。
所以容器這個(gè)標(biāo)準(zhǔn)化的應(yīng)用發(fā)布方式,最終會(huì)影響上層的整個(gè)應(yīng)用架構(gòu)。最終圍繞容器,會(huì)建立一套完整應(yīng)用架構(gòu)體系,帶來革命性的改變?,F(xiàn)在其實(shí)已經(jīng)可以看到一點(diǎn)端倪了,Kubernetes 基本已經(jīng)成為標(biāo)準(zhǔn),不久前 Google 還發(fā)布了 Istio 這個(gè) Service Mesh 工具,進(jìn)一步把微服務(wù)的基礎(chǔ)架構(gòu)也抽象了出來。
6. 容器編排是什么?
光有能裝應(yīng)用的容器還不夠,如果還是人工管理那么多容器,那也發(fā)揮不出容器的優(yōu)勢(shì)。所以我們需要一個(gè)容器編排系統(tǒng)。容器編排能提供以下功能:
- 應(yīng)用調(diào)度:應(yīng)用部署、無縫升級(jí)、彈性擴(kuò)展、自愈等。
- 資源管理:內(nèi)存、CPU、存儲(chǔ)空間、網(wǎng)絡(luò)等。
- 服務(wù)管理:命名空間、負(fù)載均衡、健康檢查等。
- 以及很多其他功能,如日志、監(jiān)控、認(rèn)證、授權(quán)等。
容器編排領(lǐng)域最主要的三個(gè)系統(tǒng)是 Docker Swarm、Kubernetes 以及 Marathon/Mesos。
Swarm 是 Docker 官方的方案,優(yōu)點(diǎn)就是簡(jiǎn)單,缺點(diǎn)是太簡(jiǎn)單了。
然后是 Google 的 Kubernetes,也叫 K8s。Kubernetes 最近一年大放光彩,幾乎統(tǒng)治了容器編排領(lǐng)域,就連 Docker 官方不久前也宣布了支持 Kubernetes 。它的優(yōu)勢(shì)就是有大廠支持,而且 Google 是把它作為戰(zhàn)略來布局的,你可以把它想象成當(dāng)年的 Android;它的社區(qū)也非常火爆。
技術(shù)上,Kubernetes 是一個(gè)集成的方案,設(shè)計(jì)非常優(yōu)秀,可以說是分布式系統(tǒng)的設(shè)計(jì)典范,Google 在這方面畢竟有很深入的經(jīng)驗(yàn)。缺點(diǎn)就是有點(diǎn)復(fù)雜,而且在今年之前它還是存在不少問題,包括性能問題、大版本的兼容性、部署復(fù)雜等,當(dāng)然現(xiàn)在已經(jīng)基本解決了。
Mesos 在 Docker 之前就有了,本身做的是分布式系統(tǒng)的資源管理,Mesos 很靈活,上面可以支持各種系統(tǒng),包括 Spark 等。Marathon 則是基于 Mesos 實(shí)現(xiàn)了編排的功能。
我們是去年年中考慮容器化的,我們最后選擇的方案是 Marathon/Mesos。原因一方面是之前 Jenkins 容器化已經(jīng)用到了 Marathon/Mesos,有些經(jīng)驗(yàn)。另一方面是該方案便于和當(dāng)前的架構(gòu)整合。Kubernetes 太過復(fù)雜,遷移的話,對(duì)架構(gòu)改動(dòng)太大。
7. 杏仁的容器化
我們?nèi)萜骰蟮募軜?gòu)是這樣的:
所有的應(yīng)用都以容器的方式運(yùn)行在 Mesos Slave 上,Mesos Master 統(tǒng)一管理 Mesos Slave 服務(wù)器。Marathon 通過 Mesos 調(diào)度容器,進(jìn)行發(fā)布、升級(jí)、擴(kuò)容等。Calico 是 Docker 的網(wǎng)絡(luò)解決方案,實(shí)現(xiàn)了一個(gè)容器一個(gè) IP 以及容器之間的互聯(lián)。而右上角部分,我們基本保留了我們之前的微服務(wù)架構(gòu),只是用于服務(wù)發(fā)現(xiàn)和注冊(cè)的 Consul Agent 替換成了 Registrator。
同時(shí)我們的 CI/CD 也相應(yīng)的做了調(diào)整。
Jenkins 自身現(xiàn)在也是基于容器的,會(huì)在 Mesos Slave 的容器里進(jìn)行編譯和打包。應(yīng)用會(huì)被打包成的 Docker 鏡像,上傳到我們的鏡像倉(cāng)庫(kù) Harbor 里。部署時(shí),Jenkins 調(diào)用 Marathon 的接口進(jìn)行部署,Marathon 則從 Mesos 申請(qǐng)資源。部署時(shí) Mesos 會(huì)從 Harbor 下載相應(yīng)的應(yīng)用鏡像并根據(jù)配置運(yùn)行。
有了這套系統(tǒng),我們創(chuàng)建應(yīng)用、擴(kuò)展應(yīng)用就很簡(jiǎn)單了。創(chuàng)建應(yīng)用時(shí)首先通過 Dockerfile 和 Jenkins 創(chuàng)建鏡像,然后在 Marathon 界面上,只需要準(zhǔn)備一個(gè) Json 配置(也可以通過 Form 配置),指定資源、實(shí)例數(shù)、鏡像、網(wǎng)絡(luò)、健康檢查、環(huán)境變量等,就可以很快上線一個(gè)新應(yīng)用。
有時(shí)候我們要準(zhǔn)備一個(gè)秒殺活動(dòng)或者推送幾百萬用戶,需要增加應(yīng)用實(shí)例,也只要在 Marathon 界面調(diào)整一個(gè)數(shù)字就可以了。
除了容器編排和 CI/CD,還有兩個(gè)很基礎(chǔ)的東西,一個(gè)是基于 ELK 的統(tǒng)一日志平臺(tái)、另一個(gè)是基于 Open-Falcon、StatsD、Graphite、Grafana 的 監(jiān)控告警平臺(tái)。大致結(jié)構(gòu)如下,具體實(shí)現(xiàn)以后有機(jī)會(huì)再專門寫文章介紹,這里就不詳述了。
最終,我們的整個(gè)平臺(tái)的組成是這樣的:
8. 容器化總結(jié)
到這里為止,這些就是杏仁目前的基礎(chǔ)平臺(tái)的架構(gòu)。通過這套系統(tǒng),我們提升了資源利用率,剛剛遷移到容器化環(huán)境的時(shí)候,我們只用了原來大約 6、70% 的云服務(wù)器。并且我們大大加強(qiáng)了我們的自動(dòng)化運(yùn)維的能力,完善了服務(wù)監(jiān)控。
但是這套系統(tǒng)也存在不少問題。
- 新增服務(wù)器節(jié)點(diǎn)還是需要一些手工操作。
- 容器、環(huán)境等的配置都是分散在各處,缺乏有效的管理。
- 對(duì)有狀態(tài)應(yīng)用支持不好。
- 系統(tǒng)存在一些冗余,例如有 Zookeeper、有 Etcd 還有 Consul。
- 不支持自動(dòng)擴(kuò)容。
- 部分基礎(chǔ)設(shè)施沒有容器化。
未來我們會(huì)繼續(xù)進(jìn)化,也許等時(shí)機(jī)成熟了,也不排除會(huì)遷移到公有云的容器服務(wù),或者自建 Kubernetes 集群。
總結(jié)
以上是生活随笔為你收集整理的创业公司的容器化之路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 上将军衔相当于什么官 解析军衔等级及对应
- 下一篇: 割鹿刀到底是什么刀?有什么来历?