
微服務(wù)架構可以視為面向組件架構和面向服務(wù)架構結合的產(chǎn)物。大多數和微服務(wù)相關(guān)的討論都是分析業(yè)務(wù)應用如何微服務(wù)化,如何遠程調用,如何服務(wù)治理,談?wù)摶A設施服務(wù)的卻很少,本文主要討論基礎設施服務(wù)的微服務(wù)化方案。
本文由 SFDC 演講內容整理而成,共 3015 字,閱讀大概需要 13 分鐘。
多微的服務(wù)才叫微服務(wù)?
微服務(wù)這一兩年非常火,云、容器等技術(shù)的發(fā)展都是在給微服務(wù)鋪路,因為用戶(hù)本質(zhì)上需要的是服務(wù),不是資源。
但大多數和微服務(wù)相關(guān)的討論都是分析業(yè)務(wù)應用如何微服務(wù)化,如何遠程調用,如何服務(wù)治理,談?wù)摶A設施服務(wù)的卻很少,我們今天來(lái)聊聊這個(gè)。

討論微服務(wù),遇到的第一個(gè)問(wèn)題就是多微的服務(wù)才能叫微服務(wù)呢?是否有個(gè)標準,比如多少行代碼?多少個(gè)方法?多少個(gè)接口?
我們來(lái)看看微服務(wù)這個(gè)概念的最早定義:

大家不用全部仔細看完,只需看看我標出來(lái)的幾個(gè)關(guān)鍵詞:
- Small Service 這個(gè)好理解,就是微服務(wù)就是小服務(wù)。
- Independently Deployable 可獨立部署。微服務(wù)就是將原來(lái)的共享庫的依賴(lài)方式,改為遠程調用的依賴(lài)方式,每個(gè)微服務(wù)都是獨立部署的服務(wù)。
- Fully AutoMated Deployment 完全的自動(dòng)化部署。
最后一點(diǎn)往往被大家忽略,為什么微服務(wù)就要完全的自動(dòng)化部署呢?因為以前的幾個(gè)服務(wù),被拆分為成百上千的服務(wù),如果沒(méi)有完全的自動(dòng)化部署,基本上是不可維護的。
當然,你可以說(shuō)『我就是不差錢(qián),我就招上千個(gè)人來(lái)管這些服務(wù)』就不叫微服務(wù)了?但這違背了我們搞微服務(wù)的目標。

再回歸到微服務(wù)這個(gè)概念。我個(gè)人認為微服務(wù)化本身包含兩層意思:
一層是拆。這個(gè)大家提到的多,將大的服務(wù)拆成小的服務(wù)粒度,通過(guò)遠程調用解決依賴(lài)。
一層是合。就是整個(gè)系統的微服務(wù)的所有組件之間應該是一個(gè)整體的分布式系統,按集群化的方式來(lái)設計,服務(wù)之間能互相感知,進(jìn)行自動(dòng)化協(xié)作。
我們來(lái)看一個(gè)微服務(wù)的例子。


這里面有 A、B、C、D 四個(gè)服務(wù),每個(gè)服務(wù)都依賴(lài)數據庫以及緩存,對外的服務(wù)有負載均衡器,服務(wù)之間互相依賴(lài),異步交互通過(guò)隊列進(jìn)行。如上圖所示,通過(guò)這樣一個(gè)簡(jiǎn)單的微服務(wù)例子,我們可以看出基礎設施都有哪些。
基礎設施是否需要微服務(wù)化?
要解答這個(gè)問(wèn)題,我們先看看當前的基礎設施服務(wù)的主要解決方案:

第一種是大的互聯(lián)網(wǎng)公司普遍使用的一種方案。
基礎設施服務(wù)托管給基礎設施部門(mén),基礎設施部門(mén)包含運維、DBA 等。比如開(kāi)發(fā)人員需要一套 MySQL 服務(wù)的時(shí)候,提出申請,基礎設施部門(mén)負責搭建和運維,開(kāi)發(fā)人員只需要連接 MySQL 的 IP 進(jìn)行使用即可。
第二種方式是托管給云廠(chǎng)商,是使用當前云的基礎設施服務(wù)。
比如 QingCloud、AWS 都提供基礎設施服務(wù),當你需要一套 MySQL ,在控制臺即可創(chuàng )建,然后獲取到連接 IP ,這樣可以省去運維基礎設施服務(wù)的成本。
但是這兩種方式都有一些問(wèn)題:

開(kāi)發(fā)測試流程中的基礎設施服務(wù)如何部署,如何自動(dòng)化?
這個(gè)沒(méi)法委托給基礎設施部門(mén),需要開(kāi)發(fā)人員自己動(dòng)手搞。但開(kāi)發(fā)人員一方面也沒(méi)有精力搞一套完整的自動(dòng)化工具,但即便是搞了,也解決不了開(kāi)發(fā)環(huán)境和線(xiàn)上環(huán)境異構的問(wèn)題。前面有位講師的分享也說(shuō)到了這個(gè)問(wèn)題,異構問(wèn)題總會(huì )導致故障,沒(méi)出現故障也是時(shí)候沒(méi)到。
基礎設施服務(wù)遷移、伸縮、故障恢復時(shí)應用如何感知?
比如有個(gè) MySQL 集群,當前數據庫請求量太大,擴容了從庫,應用如何自動(dòng)感知到這個(gè)變化,從而使用新的從庫?
我們再回顧下微服務(wù)的要求:集群化與自動(dòng)化。當前的基礎設施服務(wù)的解決方案,不能滿(mǎn)足微服務(wù)的集群化,自動(dòng)化這兩點(diǎn)要求。我們可以得出結論:基礎設施服務(wù)屬于微服務(wù)系統中的一部分,需要和業(yè)務(wù)服務(wù)互相感知,需要被微服務(wù)化。
基礎設施服務(wù)如何微服務(wù)化?

基礎設施服務(wù)種類(lèi)多樣。比如前面那個(gè)簡(jiǎn)單的微服務(wù)系統,就用到了很多基礎設施服務(wù),各種服務(wù)的有各種不同的配置方式。同時(shí),這些服務(wù)的集群機制也是多種多樣的。
我們舉幾個(gè)例子來(lái)說(shuō)明下。
[

Zookeeper 的主要配置文件是 zoo.cfg,這個(gè)配置文件中需要列出整個(gè)集群中的所有節點(diǎn),以及對應的 Server ID。
另外,每個(gè)節點(diǎn)還有一個(gè)獨立的 MyID 配置文件,這個(gè)文件中寫(xiě)了當前節點(diǎn)的 Server ID 。比如要把這個(gè)集群擴展到 5 個(gè)節點(diǎn),首先要算出新的節點(diǎn)的 Server ID 號,生成新的節點(diǎn)的 MyID 配置文件,同時(shí)需要變更每個(gè)節點(diǎn)的 zoo.cfg 配置文件,把新節點(diǎn)的 Server ID 和 IP 都寫(xiě)進(jìn)去,然后重啟服務(wù)。

HAproxy 的配置文件中的每個(gè) Backend 后會(huì )配置一個(gè) Server 列表。如果后端服務(wù)伸縮,就需要變更這個(gè) Server 列表。

Redis Cluster 的這個(gè)例子我只是想說(shuō)明下, Redis 并不是通過(guò)配置文件來(lái)維護集群信息的,而是通過(guò)動(dòng)態(tài)命令。創(chuàng )建集群,增刪節點(diǎn),都需要調用命令進(jìn)行。

Kafka 是通過(guò) Zookeeper 來(lái)做服務(wù)發(fā)現的,所以如果 Zookeeper 集群變更,就需要變更它的配置文件中的 zookeeper.connect 配置項。

粗略的看了以上的幾個(gè)例子,大家對基礎設施服務(wù)的配置和集群的多樣性有了初步的體驗。
既然要微服務(wù)化,就需要設計服務(wù)的注冊發(fā)現以及配置變更方案。微服務(wù)理想中的方案是應用內部自發(fā)現,監聽(tīng)配置中心,自動(dòng)進(jìn)配置變更。但現實(shí)的狀況前面的例子也看到了,我們不可能等待這么多的服務(wù)逐漸都改造升級了再用,所以唯一可行的辦法就是通過(guò)非侵入的方式,進(jìn)行第三方的服務(wù)注冊,以及配置變更。
QingCloud 應用調度系統實(shí)踐
青云QingCloud 在 IaaS 調度系統之上構建了應用集群的調度系統,它知道集群里的 VM 節點(diǎn)的變化,然后將集群的基礎信息注冊到我們的元信息服務(wù) Metad 中。

每個(gè) VM 節點(diǎn)里面都運行一個(gè) Confd 進(jìn)程,監聽(tīng) Metad 的元信息,一旦發(fā)生變化,則變更本地的配置和服務(wù)。如上圖所示,應用集群依賴(lài)一個(gè) Zookeeper 集群,二者都關(guān)聯(lián)在 Metad 中。如果 Zookeeper 集群的節點(diǎn)發(fā)生變化,應用集群是可以通過(guò) Metad 感知到變化,并且通過(guò) Confd 進(jìn)行配置變更。
下面我分別介紹一下該過(guò)程使用到的一些組件。

Etcd 是一個(gè)開(kāi)源的分布式的一致性 KV 存儲,提供元信息的持久化,同時(shí)它支持 Watch 機制,可以實(shí)現變更推送。

Metad 是我們自己研發(fā)的一個(gè)開(kāi)源的元信息服務(wù)。它的后端對接 Etcd ,和 Etcd 實(shí)時(shí)同步數據。
前面也說(shuō)了,當前青云QingCloud 的方案是通過(guò)調度系統進(jìn)行服務(wù)注冊。這種注冊方式有一個(gè)問(wèn)題就是,節點(diǎn)啟動(dòng)的時(shí)候,它不清楚自己所處的角色,也就是不知道『我是誰(shuí)』。人生哲學(xué)的頭一個(gè)難題就是回答『我是誰(shuí)』,服務(wù)器也有這個(gè)困境。所以我們在 Metad 中保存了 IP 到元信息之間的映射關(guān)系,客戶(hù)端請求一個(gè)固定的接口 /self 就可以拿到自己本節點(diǎn)的信息,當前節點(diǎn)所處的集群的信息,以及當前集群依賴(lài)的其他集群的信息。它也支持 Watch 機制,實(shí)現變更推送。

Confd 是一個(gè)開(kāi)源的配置變更工具,我們在其基礎上進(jìn)行了二次開(kāi)發(fā),后端對接 Metad。
它通過(guò)監聽(tīng) Metad 的變更通知,同步元信息到本地緩存,然后根據模板渲染配置文件,如果發(fā)現配置不一樣,則進(jìn)行應用的配置變更,同時(shí)觸發(fā)腳本讓?xiě)贸绦蛑匦录虞d配置(Reload 或者 Restart)。
下面我們通過(guò)一個(gè)例子來(lái)說(shuō)明下。

還是一個(gè) Zookeeper 的例子,我們首先有個(gè)集群的編排配置文件,定義了其中每個(gè)節點(diǎn)的鏡像、CPU、內存、磁盤(pán)、節點(diǎn)數量,以及啟動(dòng)、停止等 Service 腳本。

我們給 zoo.cfg 定義了一個(gè)配置文件模板,這個(gè)模板是給 Confd 用來(lái)渲染配置文件的。模板的語(yǔ)法是 Go Template 語(yǔ)法,如果不熟悉這個(gè)模板語(yǔ)法也沒(méi)關(guān)系,大家可以看出這段腳本是在循環(huán)配置中心的 Hosts 列表,然后生成 Server ID 和 IP 之間的映射配置。

這是那個(gè) MyID 配置文件的模板,這個(gè)很簡(jiǎn)單,我們給集群的每個(gè)節點(diǎn)都會(huì )分配一個(gè) SID,直接將 SID 寫(xiě)到 MyID 配置文件就好。
對與 Docker 的支持

青云QingCloud 的集群編排支持 KVM 和 Docker 兩種鏡像,這樣就可以實(shí)現 KVM 和 Docker 的混排。如果應用有特殊需求 Docker 鏡像不能滿(mǎn)足,比如 Kernel 版本,則可以使用 KVM 鏡像。
當然我們這里的 Docker 鏡像不是標準的 Docker 鏡像方式,鏡像默認啟動(dòng)的進(jìn)程,也就是 Init 進(jìn)程必須是 Confd,然后應用通過(guò) Confd 來(lái)啟動(dòng)。這個(gè)方式和大家用 Docker 的習慣不一樣,比如 Zookeeper 的鏡像,習慣啟動(dòng)的第一個(gè)進(jìn)程就是 Zookeeper 服務(wù)。
那我們?yōu)槭裁床挥?Docker 默認的方式呢?

這是一個(gè)理想和現實(shí)相互妥協(xié)的方案。理想中 Docker 的應用配置,應該是靜態(tài)配置通過(guò)環(huán)境變量,動(dòng)態(tài)的通過(guò)配置中心。
比如 JVM 的啟動(dòng)內存設置就是靜態(tài)配置,通過(guò)環(huán)境變量傳遞,如果應用需要變更內存設置,直接銷(xiāo)毀舊的容器實(shí)例,重新啟動(dòng)新的并傳遞新的環(huán)境變量即可。但現實(shí)的狀況,大多數應用的配置還都是通過(guò)配置文件,無(wú)論動(dòng)態(tài)還是靜態(tài)。
我們?yōu)榱俗兏鼞玫呐渲梦募托枰ㄟ^(guò) Confd,所以我們的 Docker 鏡像默認先啟動(dòng) Confd。
下面我們和業(yè)界的其他一些方案做一些比較。

Ansible/Puppet/Salt/Chef 這一系列配置變更以及自動(dòng)化工具,本質(zhì)上都是純靜態(tài)的配置變更工具。
它們是把變量通過(guò)模板渲染成配置文件,這里的靜態(tài)指的是這些變量在編寫(xiě)配置規則時(shí)就是確定的,所以它們的服用機制并不通用。
比如有人寫(xiě)了一個(gè)自動(dòng)部署 Zookeeper 集群的 Ansible 模塊,但當你想用這個(gè)模塊部署自己服務(wù)的時(shí)候,會(huì )發(fā)現需要修改許多變量,比如網(wǎng)絡(luò )、存儲等等。
它們的靜態(tài)模式導致的另外一個(gè)問(wèn)題就是服務(wù)的依賴(lài)變更不好解決,比如 HAProxy 那個(gè)例子,當后端服務(wù)伸縮的時(shí)候,要變更 HAProxy 配置,還是得手動(dòng)修改變量,然后重新執行配置變更腳本。所以它們只能是半人工半自動(dòng)化工具,對動(dòng)態(tài)的故障遷移以及伸縮,容災也沒(méi)有好的辦法。

Kubernetes 的目標是通用的容器編排系統,做了很多通用的抽象,試圖通過(guò) DNS,虛擬 IP 這樣的通用機制來(lái)解決服務(wù)間的依賴(lài)問(wèn)題。
比如 MySQL 的例子,MySQL 從庫伸縮后應用如何感知?
它的解決方案是 MySQL Slave 可以作為一個(gè)獨立的服務(wù),會(huì )分配一個(gè) DNS Name,以及一個(gè) 虛擬 IP。應用連接的時(shí)候通過(guò) DNS 以及虛擬 IP 進(jìn)行,并不需要知道后面的每個(gè)從庫節點(diǎn) IP。
但這種方式的問(wèn)題就是有些場(chǎng)景滿(mǎn)足不了,比如 Zookeeper 集群中的每個(gè)節點(diǎn)都需要能和其他節點(diǎn)直接通信,類(lèi)似的還有 Elasticsearch。Kubernetes 的 Elasticsearch 解決方案是給 Elasticsearch 寫(xiě)一個(gè)插件,通過(guò) Kubernetes 提供的注冊中心接口來(lái)發(fā)現集群中的其他節點(diǎn)。
但如果應用不支持插件就比較麻煩,比如 Redis Cluster,Redis Cluster 運行在 Kubernetes 上,需要把每個(gè)節點(diǎn)都作為一個(gè) Service,如果要擴展節點(diǎn)的話(huà),必須新增 Kubernetes 的 Service 配置文件,然后節點(diǎn)運行之后再通過(guò)手動(dòng)調用命令進(jìn)行初始化。它支持全局的配置文件映射,但只是純靜態(tài)配置,不支持變更。

Mesos 的目標是通用的資源調度和分配系統。
如果把應用要放到 Mesos 之上,應用需要通過(guò)擴展 Framework 來(lái)實(shí)現,開(kāi)發(fā)成本比較高。如果用通用的容器方式,也有和 Kubernetes 類(lèi)似的問(wèn)題。
所以當前看來(lái),我們的方案是相對可行度比較高,容易實(shí)踐,對各種不同的集群應用的包容性也比較高的方案。
最后再介紹一下我們的新應用中心。

主要基于前面的應用調度系統,給企業(yè)提供應用標準化開(kāi)發(fā)平臺,可以快速將應用云化,實(shí)現應用的秒級部署和彈性伸縮。同時(shí)提供計費服務(wù)以及客服平臺,讓企業(yè)應用快速實(shí)現商業(yè)化。當前還在邀請內測階段。
那這個(gè)對開(kāi)發(fā)者有什么意義呢?
我覺(jué)得可能會(huì )帶來(lái)基礎研發(fā)運維部門(mén)在企業(yè)中的角色轉換。
因為當前基礎研發(fā)運維部門(mén)在企業(yè)中屬于業(yè)務(wù)支撐的部門(mén),基本上是成本部門(mén),并不是直接生產(chǎn)利潤的部門(mén)。但如果有了這樣的平臺,基礎研發(fā)運維部門(mén)可以通過(guò)企業(yè)應用市場(chǎng)將自己的基礎組件共享出來(lái),進(jìn)行售賣(mài)。
比如每個(gè)大一點(diǎn)的互聯(lián)網(wǎng)公司都會(huì )搞一套 MySQL 分布式的集群方案,進(jìn)行自動(dòng)的分庫分表,如果能在應用市場(chǎng)中找到這樣的工具,中小企業(yè)肯定也是愿意買(mǎi)單的,所以也可以說(shuō)服務(wù)器端研發(fā)人員的春天到了。
