Netflix OSS是一組框架和庫,Netflix為大規(guī)模解決一些有趣的分布式系統(tǒng)問題而編寫的。今天,對(duì)于Java開發(fā)人員來說,它非常適合云環(huán)境中開發(fā)微服務(wù)。服務(wù)發(fā)現(xiàn)、負(fù)載平衡、容錯(cuò)等模式對(duì)于可擴(kuò)展的分布式系統(tǒng)來說是非常重要的概念,Netflix為此提供了很好的解決方案。
無論如何,很多Netflix OSS都是在AWS云上運(yùn)行的時(shí)候編寫的,沒有其他選擇。許多假設(shè)都被移植到了Netflix庫中,這些庫可能不再適用于當(dāng)前運(yùn)行的位置(如Linux容器等)。隨著Linux容器、Docker、容器管理系統(tǒng)等的出現(xiàn),我們開始看到在Linux容器中(在云中、在私有云中,兩者中)運(yùn)行我們的微服務(wù)有很多價(jià)值。此外,因?yàn)槲覀儾恢廊萜魃戏?wù)的運(yùn)行方式,我們往往不太關(guān)心哪種技術(shù)真正的在其中運(yùn)行(它是Java嗎?是node.js嗎?它是Go?),Netflix OSS主要面向Java開發(fā)人員。它們是需要包含在Java應(yīng)用程序/服務(wù)代碼中的庫/框架/配置集。
因此,我們得出第1點(diǎn)。
微服務(wù)可以在各種框架/語言中實(shí)現(xiàn),但服務(wù)發(fā)現(xiàn)、負(fù)載平衡、容錯(cuò)等仍然非常重要。如果我們?cè)谌萜髦羞\(yùn)行這些服務(wù),我們可以利用功能強(qiáng)大的與語言無關(guān)的基礎(chǔ)架構(gòu)來執(zhí)行如構(gòu)建、打包、部署、運(yùn)行狀況檢查、滾動(dòng)升級(jí)、安全性等操作。例如,openshift是一種Kubernetes的服務(wù),它考慮到了企業(yè)側(cè)需求,它為您做了所有這些事情:在應(yīng)用層中,您不需要強(qiáng)制任何人來了解或關(guān)心這些事情。讓你的應(yīng)用程序和服務(wù)保持簡(jiǎn)單!
那么基礎(chǔ)設(shè)施能否幫助提供服務(wù)發(fā)現(xiàn),負(fù)載平衡和容錯(cuò)?為什么這應(yīng)該是應(yīng)用程序級(jí)別的東西?
如果您使用kubernetes,答案是:是。
服務(wù)發(fā)現(xiàn)Kubernetes方式
使用Netflix OSS,您通常需要設(shè)置一個(gè)服務(wù)發(fā)現(xiàn)服務(wù)器,該服務(wù)器充當(dāng)端點(diǎn)的注冊(cè)表,這些端點(diǎn)可以通過各種客戶端發(fā)現(xiàn)。例如,您可能使用Netflix功能與其他服務(wù)進(jìn)行通信,并需要發(fā)現(xiàn)它們運(yùn)行的位置。服務(wù)可以被取消,我們可以向集群添加更多的服務(wù)來幫助擴(kuò)展。這個(gè)中央服務(wù)發(fā)現(xiàn)注冊(cè)表跟蹤集群中可用的服務(wù)。
其中一個(gè)問題是:作為開發(fā)人員,您需要執(zhí)行以下操作:
- 決定我是否需要AP系統(tǒng)(Consul、Eureka等)還是一個(gè)CP系統(tǒng)(ZooKeeper、etcd等)
- 了解如何大規(guī)模上運(yùn)行、管理和監(jiān)控這些系統(tǒng)(這不是一個(gè)簡(jiǎn)單的PET項(xiàng)目)
此外,您需要為所使用的編程語言找到客戶端,以便了解如何與服務(wù)發(fā)現(xiàn)機(jī)制對(duì)話。正如我們已經(jīng)說過的,微服務(wù)可以在許多不同類型的語言中實(shí)現(xiàn),所以一個(gè)完美的Java客戶端可能是可用的,但是如果GO或NoDEJS客戶端不存在,你必須自己編寫。語言和開發(fā)人員的每一種排列都可能以他們自己對(duì)如何實(shí)現(xiàn)這種客戶機(jī)的想法而告終,而現(xiàn)在,您不得不維護(hù)多個(gè)客戶端,這些客戶端試圖做相同的事情,但語義上可能不同?;蛘呙糠N語言都使用自己的服務(wù)發(fā)現(xiàn)服務(wù)器?那么,現(xiàn)在您是否按語言類型管理和維護(hù)服務(wù)發(fā)現(xiàn)服務(wù)器的基礎(chǔ)結(jié)構(gòu)?不管怎樣……有點(diǎn)小麻煩。
只使用DNS怎么辦?
好吧,這就解決了我們的客戶端庫問題,對(duì)吧?DNS可以融入到任何使用TCP/UDP的系統(tǒng)中,無論您在任何地方部署內(nèi)部部署、云、容器、Windows、Solaris等,都可以隨時(shí)使用DNS。您的客戶端只指向一個(gè)域名(即http://awsomefooservice/),基礎(chǔ)設(shè)施知道如何路由到DNS指向的服務(wù)(可以是帶負(fù)載平衡器的)。哎呀!現(xiàn)在我們不需要弄清楚需要使用什么客戶端來發(fā)現(xiàn)服務(wù),我只需要使用我想要的任何TCP客戶機(jī)。我也不需要弄清楚如何管理一個(gè)DNS集群,它已經(jīng)融入到網(wǎng)絡(luò)路由器中。
Kubernetes服務(wù)
我們就用Kubernetes。無論如何,我們將在docker/linux容器中運(yùn)行,而kubernetes是運(yùn)行docker容器的最佳方案。
有了kubernetes,我們只需創(chuàng)建和使用kubernetes服務(wù),就完成了!我們不必浪費(fèi)時(shí)間設(shè)置發(fā)現(xiàn)服務(wù)器、編寫自定義客戶端、使用DNS等。我們將繼續(xù)討論提供業(yè)務(wù)價(jià)值的微服務(wù)的下一個(gè)部分。
如何工作的呢
下面是Kubernetes為實(shí)現(xiàn)這一點(diǎn)而帶來的簡(jiǎn)單抽象:
- Pods
- 標(biāo)簽/標(biāo)簽選擇器
- 服務(wù)
Pods很簡(jiǎn)單。它們基本上就是您的Linux容器。標(biāo)簽很簡(jiǎn)單。它們基本上是鍵值字符串,可以用來標(biāo)記您的pod(例如,pod有標(biāo)簽app=cassandra、tier=backend、version=1.0,language=java)。這些標(biāo)簽可以是你心中想要的任何東西。
最后一個(gè)概念是服務(wù)。也很簡(jiǎn)單。服務(wù)是固定的群集IP地址。這個(gè)IP地址是一個(gè)虛擬IP地址,可以用來發(fā)現(xiàn)/調(diào)用pods/容器中的實(shí)際端點(diǎn)。這個(gè)IP地址如何知道哪些pods/容器有資格被發(fā)現(xiàn)?它使用一個(gè)標(biāo)簽選擇器來選擇具有您定義的標(biāo)簽的pods。例如,假設(shè)我們想要一個(gè)Kubernetes服務(wù),其選擇器為“app = cassandra AND tier = backend”。這將為我提供一個(gè)帶有虛擬IP的服務(wù),它可以發(fā)現(xiàn)任何與該標(biāo)簽匹配的pods(同時(shí)具有app=cassandra和tier=backend)。此選擇器被主動(dòng)評(píng)估,以便離開集群的任何pods或加入集群的任何pods(基于它們擁有的標(biāo)簽)都將自動(dòng)開始參與服務(wù)發(fā)現(xiàn)。

使用Kubernetes服務(wù)選擇與服務(wù)相關(guān)的容器的另一個(gè)好處是,Kubernetes很聰明哪個(gè)容器屬于哪個(gè)服務(wù)和健康狀況。Kubernetes可以使用內(nèi)置的活躍性和健康檢查來確定是否應(yīng)該根據(jù)特定服務(wù)的活動(dòng)和/或正常運(yùn)行,將pod包含在pod的集群中。
注意,kubernetes服務(wù)的一個(gè)實(shí)例不是一個(gè)“東西”,也不是一個(gè)設(shè)備,或者一個(gè)docker容器,或者任何東西……它是一個(gè)虛擬的“東西”…所以沒有單一的故障點(diǎn)。
對(duì)于開發(fā)人員來說,這是非常強(qiáng)大和簡(jiǎn)單的?,F(xiàn)在,一個(gè)希望使用Cassandra后端的應(yīng)用程序只使用這個(gè)固定的IP地址與Cassandra數(shù)據(jù)庫通信。但是硬編碼一個(gè)固定的IP通常不是一個(gè)好辦法,因?yàn)槿绻阆氚涯愕膽?yīng)用程序/服務(wù)轉(zhuǎn)移到一個(gè)不同的環(huán)境(如QA、Prod等)?,F(xiàn)在您必須更改那個(gè)IP(或注入一些配置),現(xiàn)在您已經(jīng)增加了配置負(fù)擔(dān)。所以我們只使用dns:)
在Kubernetes中使用集群DNS是正確的答案。由于IP是為給定環(huán)境(dev、QA等)固定的,所以我們不關(guān)心緩存它:它永遠(yuǎn)不會(huì)改變?,F(xiàn)在,如果我們使用DNS,我們的應(yīng)用程序可以配置為在http://awsomefooseservice/上與服務(wù)通信,即使我們從dev到QA再到prod,到目前為止,我們?cè)诿總€(gè)環(huán)境中都有這些kubernetes服務(wù),我們的應(yīng)用程序不需要更改。

我們不需要額外的配置,也不需要擔(dān)心DNS緩存/SRV記錄、自定義庫客戶端和管理其他的服務(wù)發(fā)現(xiàn)基礎(chǔ)結(jié)構(gòu)。pods現(xiàn)在可以添加到集群(或從集群中取出),kubernetes服務(wù)的標(biāo)簽選擇器將根據(jù)標(biāo)簽主動(dòng)對(duì)集群進(jìn)行分組。你的應(yīng)用程序只與http://aWeMESFoService通信,無論你是Java應(yīng)用程序、Python、NoDE.JS、Perl、Go、.NET、Ruby、C++、Scala、Groovy,等等。這個(gè)服務(wù)發(fā)現(xiàn)機(jī)制不強(qiáng)制使用特定的客戶端。
服務(wù)發(fā)現(xiàn)變得簡(jiǎn)單多了。
這個(gè)很有趣。Netflix編寫了Eureka和Ribbon,通過這些組合,您可以啟用客戶端負(fù)載平衡?;旧?,服務(wù)注冊(cè)中心(eureka/consul/zookeeper/etc)會(huì)跟蹤集群中存在哪些服務(wù),并將這些數(shù)據(jù)發(fā)送給對(duì)此感興趣的客戶端。然后,由于客戶機(jī)具有集群中節(jié)點(diǎn)的信息,所以它可以選擇一個(gè)節(jié)點(diǎn)(隨機(jī)、粘性或一些自定義算法),然后調(diào)用它。在下一個(gè)調(diào)用中,如果需要,它可以在集群中選擇不同的服務(wù)。這里的優(yōu)點(diǎn)是我們不需要物理/軟件負(fù)載平衡器,這可能很快就會(huì)成為瓶頸。另一個(gè)重要方面:由于客戶機(jī)知道服務(wù)在哪里,客戶機(jī)可以直接聯(lián)系服務(wù)提供商,而不需要額外的跳轉(zhuǎn)。
客戶端負(fù)載平衡是5%的用例。讓我解釋一下。
我們想要的是一種在沒有任何附加設(shè)備和客戶端庫的情況下實(shí)現(xiàn)可擴(kuò)展負(fù)載平衡的理想方法。在大多數(shù)情況下,我們可能不關(guān)心中間有負(fù)載均衡器的額外跳轉(zhuǎn)(想想看..可能99%的應(yīng)用程序都是以這種方式部署的)。我們可能會(huì)遇到這樣一種情況,即服務(wù)A的呼叫服務(wù)B,它呼叫服務(wù)C、D和E,然后您就可以看到結(jié)果了。在這種情況下,如果每個(gè)人多跳轉(zhuǎn)一次,我們會(huì)產(chǎn)生很多額外的延遲。所以一個(gè)可能的解決方案是“去掉多余的跳轉(zhuǎn)”。它是……但不僅僅是在跳轉(zhuǎn)中加載平衡程序:更多了您必須對(duì)下游服務(wù)進(jìn)行的調(diào)用數(shù)量:)。
使用Kubernetes服務(wù),正如我們?cè)谏厦娴姆?wù)發(fā)現(xiàn)部分所做的那樣,我們實(shí)現(xiàn)了正確的負(fù)載平衡(同樣,沒有服務(wù)注冊(cè)表、自定義客戶端、DNS缺點(diǎn)等的所有開銷)。當(dāng)我們通過其DNS(或IP)與kubernetes服務(wù)交互時(shí),kubernetes將默認(rèn)地在集群中的pods之間實(shí)現(xiàn)負(fù)載平衡(記住,集群是由標(biāo)簽和標(biāo)簽選擇器定義的)。如果您不希望在負(fù)載平衡中有額外的跳轉(zhuǎn),不用擔(dān)心;這個(gè)虛擬IP直接路由到pods,它不會(huì)影響物理網(wǎng)絡(luò)。
哎呀!對(duì)于95%的用例來說很容易。很有可能,你在95%的用例分布中,所以不需要過度設(shè)計(jì)。
那5%的情況怎么樣?您可能遇到這樣的情況:您必須在運(yùn)行時(shí)做出一些業(yè)務(wù)決策,確定您真正要調(diào)用的集群中的哪個(gè)確切的后端端點(diǎn)?;旧?,您需要使用一些比“循環(huán)”、“隨機(jī)”、“粘性會(huì)話”更復(fù)雜的自定義算法,并且這些算法是特定于您的應(yīng)用程序的。為此,請(qǐng)使用客戶端負(fù)載平衡。在這個(gè)模型中,您仍然可以利用Kubernetes的服務(wù)發(fā)現(xiàn)來找出集群中的哪些pod,然后使用您自己的代碼來決定直接調(diào)用哪個(gè)pod(基于標(biāo)簽等)。對(duì)于其他語言也可以這樣做,只需使用kubernetes rest API查詢pods等。對(duì)于這些用例,更合適的方法是將這個(gè)自定義邏輯分解成自己的模塊,這樣它的依賴關(guān)系就與應(yīng)用程序分離了。使用kubernetes,您可以將這個(gè)單獨(dú)的模塊部署為應(yīng)用程序/服務(wù),并在那里保留定制的負(fù)載平衡邏輯。

這是5%的用例,并帶來了額外的復(fù)雜性。對(duì)于95%的用例,只需使用內(nèi)置的內(nèi)容而無需任何特定于語言的特定客戶端。
容錯(cuò)怎么樣呢?
具有依賴性的系統(tǒng)應(yīng)該始終考慮到promises。這意味著,即使依賴系統(tǒng)不可用或崩潰的情況下,他們也應(yīng)該始終知道他們的義務(wù)是什么。我們應(yīng)該問Kubernetes它在容錯(cuò)方面有什么作用嗎?
好吧,Kubernetes確實(shí)有自我修復(fù)能力。如果一個(gè)pod中的pod或容器出現(xiàn)故障,
Kubernetes可以將其恢復(fù)以維護(hù)其ReplicaSet不變量(基本上,如果你告訴Kubernetes我想要10個(gè)“foo”的pod,它總是會(huì)嘗試保持該狀態(tài))。
自我修復(fù)的基礎(chǔ)設(shè)施是非常棒的,它與Kubernetes一起推出,但是我們對(duì)這個(gè)討論感興趣的是,當(dāng)應(yīng)用程序的依賴關(guān)系(數(shù)據(jù)庫、其他服務(wù)等)失效時(shí),它會(huì)發(fā)生什么?好吧,這取決于應(yīng)用程序如何處理。例如,在Netflix,如果您嘗試觀看特定的電影,則會(huì)向“授權(quán)”服務(wù)發(fā)出服務(wù)呼叫,該服務(wù)知道您在觀看電影時(shí)擁有哪些特權(quán)。如果服務(wù)中斷,我們應(yīng)該阻止用戶觀看那部電影嗎?我們應(yīng)該顯示異常堆棧跟蹤嗎?Netflix的方法就是讓用戶看電影。
我們想要的是尋找其他方法來履行我們對(duì)服務(wù)合同的承諾。Netflix HySrx是Java開發(fā)人員的一個(gè)很好的解決方案。Netflix Hystrix實(shí)現(xiàn)了一種方法來執(zhí)行“Bulkheading”、“Circuit Breaking”和“Fallbacks”。其中每一個(gè)都是特定于應(yīng)用程序的命令,因此在本例中,為不同的語言提供特定的客戶端庫是合理的。
Kubernetes可以幫助解決這個(gè)問題嗎?是!
再次,看一下很棒的Kubeflix項(xiàng)目,您可以使用Netflix Turbine項(xiàng)目來聚合和可視化集群中運(yùn)行的所有斷路器。Hystrix可以將服務(wù)器端事件公開為可由Turbine使用的流。但Turbine如何發(fā)現(xiàn)哪些pods中有hystrix?)我們可以使用Kubernetes標(biāo)簽。如果我們標(biāo)記使用Hystrix的標(biāo)簽為hystrix.enabled=true那么Kubeflix可以自動(dòng)發(fā)現(xiàn)每個(gè)hystrix斷路器的SSE流并將其顯示在Turbine網(wǎng)頁上。

配置怎樣呢?
Netflix Archius是為處理云中服務(wù)的分布式配置管理而編寫的。要做到這一點(diǎn),就像使用Eureka和Ribbon一樣,您可以設(shè)置配置服務(wù)器并使用Java庫查找配置值。它還支持動(dòng)態(tài)配置更改等(請(qǐng)記住,Netflix是為AWS構(gòu)建的)。他們是Netflix;作為他們的CI/CD管道的一部分,他們將構(gòu)建AMI并部署它們。在大多數(shù)情況下,構(gòu)建AMI或任何虛擬機(jī)鏡像都是耗時(shí)的,并且有很多不必要的開銷……使用docker/linux容器,事情會(huì)更加靈活,正如我將從配置的角度解釋的那樣)。
那95%的用例呢?我們希望將特定于環(huán)境的配置(這是一個(gè)重要的區(qū)別……不是每個(gè)配置都是特定于環(huán)境的配置,需要根據(jù)我們運(yùn)行的環(huán)境進(jìn)行更改)存儲(chǔ)在應(yīng)用程序外部,并根據(jù)我們運(yùn)行的環(huán)境(dev、qa、prod等)將其注入。但是,我們確實(shí)希望有一種語言無關(guān)的方式來查找配置,而不是強(qiáng)迫每個(gè)人使用Java和/或使用其他Java庫將其類路徑復(fù)雜化以進(jìn)行配置。
在Kubernetes中,我們有三種用于注入基于環(huán)境的配置的構(gòu)造。
環(huán)境變量
Gitrepo卷
配置映射
基本上,我們可以通過環(huán)境變量(Java、NodeJS、GO、Ruby、Python等可以輕松讀取)向Linux容器注入配置數(shù)據(jù),大多數(shù)語言都可以讀取這些配置數(shù)據(jù)。我們可以將配置存儲(chǔ)在Git中(這是一個(gè)好主意),并且可以將配置報(bào)告直接綁定到POD(作為文件系統(tǒng)上的文件),然后使用任何框架工具來使用應(yīng)用程序配置文件。最后,我們可以使用kubernetes configmap將我們的版本化配置存儲(chǔ)到configmap中,然后將其作為文件系統(tǒng)裝載到pod中,從而稍微分離git repo。同樣,您使用配置文件的方式與使用來自文件系統(tǒng)的任何配置文件的方式相同,它們使用您各自的語言/框架。
那5%的用例呢?
在5%的用例中,您可能希望在運(yùn)行時(shí)動(dòng)態(tài)更改配置。Kubernetes對(duì)此有所幫助。您可以在configmap中更改配置文件,并將這些更改動(dòng)態(tài)地應(yīng)用到裝載它們的pods中。在這種情況下,您需要有一個(gè)客戶端庫,它能夠檢測(cè)這些配置更改并將它們公開給您的應(yīng)用程序。Netflix Archais有一個(gè)客戶機(jī)可以做到這一點(diǎn)。Java的Spring Cube KubNETes使Kubernetes更容易做到這一點(diǎn)。
那么Spring Cloud呢?
使用Spring開發(fā)微服務(wù)的Java用戶通常把Spring Cloud與Netflix OSS等同起來,因?yàn)楹芏喽际腔贜etflix OSS的。
如果您正在研究構(gòu)建微服務(wù),并且已經(jīng)傾向于使用Netflix OSS/Java/Spring/Spring Cloud,請(qǐng)注意,您不是Netflix,您不必直接使用AWS EC2原語,并通過這樣做使應(yīng)用程序復(fù)雜化。如果您希望使用Docker,那么采用Kubernetes是一個(gè)明智的選擇,它附帶了許多現(xiàn)成的“分布式系統(tǒng)特性”。在需要的地方分層,并從一開始就避免過度復(fù)雜化服務(wù),因?yàn)镹etflix五年前就提出了這種方法(因?yàn)樗麄儽仨氝@樣做!我敢打賭,如果他們5年前發(fā)現(xiàn)有Kubernetes,他們的Netflix OSS堆棧看起來會(huì)大不一樣?。?。