微服務(wù)架構(gòu)可以視為面向組件架構(gòu)和面向服務(wù)架構(gòu)結(jié)合的產(chǎn)物。大多數(shù)和微服務(wù)相關(guān)的討論都是分析業(yè)務(wù)應(yīng)用如何微服務(wù)化,如何遠(yuǎn)程調(diào)用,如何服務(wù)治理,談?wù)摶A(chǔ)設(shè)施服務(wù)的卻很少,本文主要討論基礎(chǔ)設(shè)施服務(wù)的微服務(wù)化方案。
本文由 SFDC 演講內(nèi)容整理而成,共 3015 字,閱讀大概需要 13 分鐘。
多微的服務(wù)才叫微服務(wù)?
微服務(wù)這一兩年非;,云、容器等技術(shù)的發(fā)展都是在給微服務(wù)鋪路,因?yàn)橛脩舯举|(zhì)上需要的是服務(wù),不是資源。
但大多數(shù)和微服務(wù)相關(guān)的討論都是分析業(yè)務(wù)應(yīng)用如何微服務(wù)化,如何遠(yuǎn)程調(diào)用,如何服務(wù)治理,談?wù)摶A(chǔ)設(shè)施服務(wù)的卻很少,我們今天來聊聊這個(gè)。
討論微服務(wù),遇到的第一個(gè)問題就是多微的服務(wù)才能叫微服務(wù)呢?是否有個(gè)標(biāo)準(zhǔn),比如多少行代碼?多少個(gè)方法?多少個(gè)接口?
我們來看看微服務(wù)這個(gè)概念的最早定義:
大家不用全部仔細(xì)看完,只需看看我標(biāo)出來的幾個(gè)關(guān)鍵詞:
- Small Service 這個(gè)好理解,就是微服務(wù)就是小服務(wù)。
- Independently Deployable 可獨(dú)立部署。微服務(wù)就是將原來的共享庫的依賴方式,改為遠(yuǎn)程調(diào)用的依賴方式,每個(gè)微服務(wù)都是獨(dú)立部署的服務(wù)。
- Fully AutoMated Deployment 完全的自動化部署。
最后一點(diǎn)往往被大家忽略,為什么微服務(wù)就要完全的自動化部署呢?因?yàn)橐郧暗膸讉(gè)服務(wù),被拆分為成百上千的服務(wù),如果沒有完全的自動化部署,基本上是不可維護(hù)的。
當(dāng)然,你可以說『我就是不差錢,我就招上千個(gè)人來管這些服務(wù)』就不叫微服務(wù)了?但這違背了我們搞微服務(wù)的目標(biāo)。
再回歸到微服務(wù)這個(gè)概念。我個(gè)人認(rèn)為微服務(wù)化本身包含兩層意思:
一層是拆。這個(gè)大家提到的多,將大的服務(wù)拆成小的服務(wù)粒度,通過遠(yuǎn)程調(diào)用解決依賴。
一層是合。就是整個(gè)系統(tǒng)的微服務(wù)的所有組件之間應(yīng)該是一個(gè)整體的分布式系統(tǒng),按集群化的方式來設(shè)計(jì),服務(wù)之間能互相感知,進(jìn)行自動化協(xié)作。
我們來看一個(gè)微服務(wù)的例子。
這里面有 A、B、C、D 四個(gè)服務(wù),每個(gè)服務(wù)都依賴數(shù)據(jù)庫以及緩存,對外的服務(wù)有負(fù)載均衡器,服務(wù)之間互相依賴,異步交互通過隊(duì)列進(jìn)行。如上圖所示,通過這樣一個(gè)簡單的微服務(wù)例子,我們可以看出基礎(chǔ)設(shè)施都有哪些。
基礎(chǔ)設(shè)施是否需要微服務(wù)化?
要解答這個(gè)問題,我們先看看當(dāng)前的基礎(chǔ)設(shè)施服務(wù)的主要解決方案:
第一種是大的互聯(lián)網(wǎng)公司普遍使用的一種方案。
基礎(chǔ)設(shè)施服務(wù)托管給基礎(chǔ)設(shè)施部門,基礎(chǔ)設(shè)施部門包含運(yùn)維、DBA 等。比如開發(fā)人員需要一套 MySQL 服務(wù)的時(shí)候,提出申請,基礎(chǔ)設(shè)施部門負(fù)責(zé)搭建和運(yùn)維,開發(fā)人員只需要連接 MySQL 的 IP 進(jìn)行使用即可。
第二種方式是托管給云廠商,是使用當(dāng)前云的基礎(chǔ)設(shè)施服務(wù)。
比如 QingCloud、AWS 都提供基礎(chǔ)設(shè)施服務(wù),當(dāng)你需要一套 MySQL ,在控制臺即可創(chuàng)建,然后獲取到連接 IP ,這樣可以省去運(yùn)維基礎(chǔ)設(shè)施服務(wù)的成本。
但是這兩種方式都有一些問題:
開發(fā)測試流程中的基礎(chǔ)設(shè)施服務(wù)如何部署,如何自動化?
這個(gè)沒法委托給基礎(chǔ)設(shè)施部門,需要開發(fā)人員自己動手搞。但開發(fā)人員一方面也沒有精力搞一套完整的自動化工具,但即便是搞了,也解決不了開發(fā)環(huán)境和線上環(huán)境異構(gòu)的問題。前面有位講師的分享也說到了這個(gè)問題,異構(gòu)問題總會導(dǎo)致故障,沒出現(xiàn)故障也是時(shí)候沒到。
基礎(chǔ)設(shè)施服務(wù)遷移、伸縮、故障恢復(fù)時(shí)應(yīng)用如何感知?
比如有個(gè) MySQL 集群,當(dāng)前數(shù)據(jù)庫請求量太大,擴(kuò)容了從庫,應(yīng)用如何自動感知到這個(gè)變化,從而使用新的從庫?
我們再回顧下微服務(wù)的要求:集群化與自動化。當(dāng)前的基礎(chǔ)設(shè)施服務(wù)的解決方案,不能滿足微服務(wù)的集群化,自動化這兩點(diǎn)要求。我們可以得出結(jié)論:基礎(chǔ)設(shè)施服務(wù)屬于微服務(wù)系統(tǒng)中的一部分,需要和業(yè)務(wù)服務(wù)互相感知,需要被微服務(wù)化。
基礎(chǔ)設(shè)施服務(wù)如何微服務(wù)化?
基礎(chǔ)設(shè)施服務(wù)種類多樣。比如前面那個(gè)簡單的微服務(wù)系統(tǒng),就用到了很多基礎(chǔ)設(shè)施服務(wù),各種服務(wù)的有各種不同的配置方式。同時(shí),這些服務(wù)的集群機(jī)制也是多種多樣的。
我們舉幾個(gè)例子來說明下。
[
Zookeeper 的主要配置文件是 zoo.cfg,這個(gè)配置文件中需要列出整個(gè)集群中的所有節(jié)點(diǎn),以及對應(yīng)的 Server ID。
另外,每個(gè)節(jié)點(diǎn)還有一個(gè)獨(dú)立的 MyID 配置文件,這個(gè)文件中寫了當(dāng)前節(jié)點(diǎn)的 Server ID 。比如要把這個(gè)集群擴(kuò)展到 5 個(gè)節(jié)點(diǎn),首先要算出新的節(jié)點(diǎn)的 Server ID 號,生成新的節(jié)點(diǎn)的 MyID 配置文件,同時(shí)需要變更每個(gè)節(jié)點(diǎn)的 zoo.cfg 配置文件,把新節(jié)點(diǎn)的 Server ID 和 IP 都寫進(jìn)去,然后重啟服務(wù)。
HAproxy 的配置文件中的每個(gè) Backend 后會配置一個(gè) Server 列表。如果后端服務(wù)伸縮,就需要變更這個(gè) Server 列表。
Redis Cluster 的這個(gè)例子我只是想說明下, Redis 并不是通過配置文件來維護(hù)集群信息的,而是通過動態(tài)命令。創(chuàng)建集群,增刪節(jié)點(diǎn),都需要調(diào)用命令進(jìn)行。
Kafka 是通過 Zookeeper 來做服務(wù)發(fā)現(xiàn)的,所以如果 Zookeeper 集群變更,就需要變更它的配置文件中的 zookeeper.connect 配置項(xiàng)。
粗略的看了以上的幾個(gè)例子,大家對基礎(chǔ)設(shè)施服務(wù)的配置和集群的多樣性有了初步的體驗(yàn)。
既然要微服務(wù)化,就需要設(shè)計(jì)服務(wù)的注冊發(fā)現(xiàn)以及配置變更方案。微服務(wù)理想中的方案是應(yīng)用內(nèi)部自發(fā)現(xiàn),監(jiān)聽配置中心,自動進(jìn)配置變更。但現(xiàn)實(shí)的狀況前面的例子也看到了,我們不可能等待這么多的服務(wù)逐漸都改造升級了再用,所以唯一可行的辦法就是通過非侵入的方式,進(jìn)行第三方的服務(wù)注冊,以及配置變更。
QingCloud 應(yīng)用調(diào)度系統(tǒng)實(shí)踐
青云QingCloud 在 IaaS 調(diào)度系統(tǒng)之上構(gòu)建了應(yīng)用集群的調(diào)度系統(tǒng),它知道集群里的 VM 節(jié)點(diǎn)的變化,然后將集群的基礎(chǔ)信息注冊到我們的元信息服務(wù) Metad 中。
每個(gè) VM 節(jié)點(diǎn)里面都運(yùn)行一個(gè) Confd 進(jìn)程,監(jiān)聽 Metad 的元信息,一旦發(fā)生變化,則變更本地的配置和服務(wù)。如上圖所示,應(yīng)用集群依賴一個(gè) Zookeeper 集群,二者都關(guān)聯(lián)在 Metad 中。如果 Zookeeper 集群的節(jié)點(diǎn)發(fā)生變化,應(yīng)用集群是可以通過 Metad 感知到變化,并且通過 Confd 進(jìn)行配置變更。
下面我分別介紹一下該過程使用到的一些組件。
Etcd 是一個(gè)開源的分布式的一致性 KV 存儲,提供元信息的持久化,同時(shí)它支持 Watch 機(jī)制,可以實(shí)現(xiàn)變更推送。
Metad 是我們自己研發(fā)的一個(gè)開源的元信息服務(wù)。它的后端對接 Etcd ,和 Etcd 實(shí)時(shí)同步數(shù)據(jù)。
前面也說了,當(dāng)前青云QingCloud 的方案是通過調(diào)度系統(tǒng)進(jìn)行服務(wù)注冊。這種注冊方式有一個(gè)問題就是,節(jié)點(diǎn)啟動的時(shí)候,它不清楚自己所處的角色,也就是不知道『我是誰』。人生哲學(xué)的頭一個(gè)難題就是回答『我是誰』,服務(wù)器也有這個(gè)困境。所以我們在 Metad 中保存了 IP 到元信息之間的映射關(guān)系,客戶端請求一個(gè)固定的接口 /self 就可以拿到自己本節(jié)點(diǎn)的信息,當(dāng)前節(jié)點(diǎn)所處的集群的信息,以及當(dāng)前集群依賴的其他集群的信息。它也支持 Watch 機(jī)制,實(shí)現(xiàn)變更推送。
Confd 是一個(gè)開源的配置變更工具,我們在其基礎(chǔ)上進(jìn)行了二次開發(fā),后端對接 Metad。
它通過監(jiān)聽 Metad 的變更通知,同步元信息到本地緩存,然后根據(jù)模板渲染配置文件,如果發(fā)現(xiàn)配置不一樣,則進(jìn)行應(yīng)用的配置變更,同時(shí)觸發(fā)腳本讓應(yīng)用程序重新加載配置(Reload 或者 Restart)。
下面我們通過一個(gè)例子來說明下。
還是一個(gè) Zookeeper 的例子,我們首先有個(gè)集群的編排配置文件,定義了其中每個(gè)節(jié)點(diǎn)的鏡像、CPU、內(nèi)存、磁盤、節(jié)點(diǎn)數(shù)量,以及啟動、停止等 Service 腳本。
我們給 zoo.cfg 定義了一個(gè)配置文件模板,這個(gè)模板是給 Confd 用來渲染配置文件的。模板的語法是 Go Template 語法,如果不熟悉這個(gè)模板語法也沒關(guān)系,大家可以看出這段腳本是在循環(huán)配置中心的 Hosts 列表,然后生成 Server ID 和 IP 之間的映射配置。
這是那個(gè) MyID 配置文件的模板,這個(gè)很簡單,我們給集群的每個(gè)節(jié)點(diǎn)都會分配一個(gè) SID,直接將 SID 寫到 MyID 配置文件就好。
對與 Docker 的支持
青云QingCloud 的集群編排支持 KVM 和 Docker 兩種鏡像,這樣就可以實(shí)現(xiàn) KVM 和 Docker 的混排。如果應(yīng)用有特殊需求 Docker 鏡像不能滿足,比如 Kernel 版本,則可以使用 KVM 鏡像。
當(dāng)然我們這里的 Docker 鏡像不是標(biāo)準(zhǔn)的 Docker 鏡像方式,鏡像默認(rèn)啟動的進(jìn)程,也就是 Init 進(jìn)程必須是 Confd,然后應(yīng)用通過 Confd 來啟動。這個(gè)方式和大家用 Docker 的習(xí)慣不一樣,比如 Zookeeper 的鏡像,習(xí)慣啟動的第一個(gè)進(jìn)程就是 Zookeeper 服務(wù)。
那我們?yōu)槭裁床挥?Docker 默認(rèn)的方式呢?
這是一個(gè)理想和現(xiàn)實(shí)相互妥協(xié)的方案。理想中 Docker 的應(yīng)用配置,應(yīng)該是靜態(tài)配置通過環(huán)境變量,動態(tài)的通過配置中心。
比如 JVM 的啟動內(nèi)存設(shè)置就是靜態(tài)配置,通過環(huán)境變量傳遞,如果應(yīng)用需要變更內(nèi)存設(shè)置,直接銷毀舊的容器實(shí)例,重新啟動新的并傳遞新的環(huán)境變量即可。但現(xiàn)實(shí)的狀況,大多數(shù)應(yīng)用的配置還都是通過配置文件,無論動態(tài)還是靜態(tài)。
我們?yōu)榱俗兏鼞?yīng)用的配置文件,就需要通過 Confd,所以我們的 Docker 鏡像默認(rèn)先啟動 Confd。
下面我們和業(yè)界的其他一些方案做一些比較。
Ansible/Puppet/Salt/Chef 這一系列配置變更以及自動化工具,本質(zhì)上都是純靜態(tài)的配置變更工具。
它們是把變量通過模板渲染成配置文件,這里的靜態(tài)指的是這些變量在編寫配置規(guī)則時(shí)就是確定的,所以它們的服用機(jī)制并不通用。
比如有人寫了一個(gè)自動部署 Zookeeper 集群的 Ansible 模塊,但當(dāng)你想用這個(gè)模塊部署自己服務(wù)的時(shí)候,會發(fā)現(xiàn)需要修改許多變量,比如網(wǎng)絡(luò)、存儲等等。
它們的靜態(tài)模式導(dǎo)致的另外一個(gè)問題就是服務(wù)的依賴變更不好解決,比如 HAProxy 那個(gè)例子,當(dāng)后端服務(wù)伸縮的時(shí)候,要變更 HAProxy 配置,還是得手動修改變量,然后重新執(zhí)行配置變更腳本。所以它們只能是半人工半自動化工具,對動態(tài)的故障遷移以及伸縮,容災(zāi)也沒有好的辦法。
Kubernetes 的目標(biāo)是通用的容器編排系統(tǒng),做了很多通用的抽象,試圖通過 DNS,虛擬 IP 這樣的通用機(jī)制來解決服務(wù)間的依賴問題。
比如 MySQL 的例子,MySQL 從庫伸縮后應(yīng)用如何感知?
它的解決方案是 MySQL Slave 可以作為一個(gè)獨(dú)立的服務(wù),會分配一個(gè) DNS Name,以及一個(gè) 虛擬 IP。應(yīng)用連接的時(shí)候通過 DNS 以及虛擬 IP 進(jìn)行,并不需要知道后面的每個(gè)從庫節(jié)點(diǎn) IP。
但這種方式的問題就是有些場景滿足不了,比如 Zookeeper 集群中的每個(gè)節(jié)點(diǎn)都需要能和其他節(jié)點(diǎn)直接通信,類似的還有 Elasticsearch。Kubernetes 的 Elasticsearch 解決方案是給 Elasticsearch 寫一個(gè)插件,通過 Kubernetes 提供的注冊中心接口來發(fā)現(xiàn)集群中的其他節(jié)點(diǎn)。
但如果應(yīng)用不支持插件就比較麻煩,比如 Redis Cluster,Redis Cluster 運(yùn)行在 Kubernetes 上,需要把每個(gè)節(jié)點(diǎn)都作為一個(gè) Service,如果要擴(kuò)展節(jié)點(diǎn)的話,必須新增 Kubernetes 的 Service 配置文件,然后節(jié)點(diǎn)運(yùn)行之后再通過手動調(diào)用命令進(jìn)行初始化。它支持全局的配置文件映射,但只是純靜態(tài)配置,不支持變更。
Mesos 的目標(biāo)是通用的資源調(diào)度和分配系統(tǒng)。
如果把應(yīng)用要放到 Mesos 之上,應(yīng)用需要通過擴(kuò)展 Framework 來實(shí)現(xiàn),開發(fā)成本比較高。如果用通用的容器方式,也有和 Kubernetes 類似的問題。
所以當(dāng)前看來,我們的方案是相對可行度比較高,容易實(shí)踐,對各種不同的集群應(yīng)用的包容性也比較高的方案。
最后再介紹一下我們的新應(yīng)用中心。
主要基于前面的應(yīng)用調(diào)度系統(tǒng),給企業(yè)提供應(yīng)用標(biāo)準(zhǔn)化開發(fā)平臺,可以快速將應(yīng)用云化,實(shí)現(xiàn)應(yīng)用的秒級部署和彈性伸縮。同時(shí)提供計(jì)費(fèi)服務(wù)以及客服平臺,讓企業(yè)應(yīng)用快速實(shí)現(xiàn)商業(yè)化。當(dāng)前還在邀請內(nèi)測階段。
那這個(gè)對開發(fā)者有什么意義呢?
我覺得可能會帶來基礎(chǔ)研發(fā)運(yùn)維部門在企業(yè)中的角色轉(zhuǎn)換。
因?yàn)楫?dāng)前基礎(chǔ)研發(fā)運(yùn)維部門在企業(yè)中屬于業(yè)務(wù)支撐的部門,基本上是成本部門,并不是直接生產(chǎn)利潤的部門。但如果有了這樣的平臺,基礎(chǔ)研發(fā)運(yùn)維部門可以通過企業(yè)應(yīng)用市場將自己的基礎(chǔ)組件共享出來,進(jìn)行售賣。
比如每個(gè)大一點(diǎn)的互聯(lián)網(wǎng)公司都會搞一套 MySQL 分布式的集群方案,進(jìn)行自動的分庫分表,如果能在應(yīng)用市場中找到這樣的工具,中小企業(yè)肯定也是愿意買單的,所以也可以說服務(wù)器端研發(fā)人員的春天到了。