Netflix OSS是由Netflix公司主持開發的一套代碼框架和庫,目的是解決上了規模以後的分佈式系統可能出現的一些有趣問題。對於當今時代的Java開發者們來講,Netflix OSS簡直就是在雲端開發微服務的代名詞。服務發現、負載均衡、容錯等對於可擴展的分佈式系統來講都是很是很是重要的概念,Netflix對這些問題都給出了很好的解決方案。在這裏Netflix要對那些在廣大的開源社區中爲這些代碼框架和庫作出過貢獻的人們簡單地說聲「謝謝」,還有許多互聯網公司也作出了貢獻,因此在這裏一併謝過。但是有一些比較大的互聯網公司卻爲本身作的一些東西申請了專利,還把代碼都保留起來沒有開源,這實在不太好。html
不過,Netflix OSS的許多內容都是在一個已通過去的年代寫出來的,那時全部東西都只能運行在AWS雲上而沒有其它選擇。關於那個年代的許多寶貴遺產和前提假設都已經被封裝到了Netflix的庫裏面,對於如今你運行的環境(好比Linux容器)已經不適用了。在Linux容器、Docker、容器管理系統等等出現以後,咱們愈來愈看到把咱們的微服務運行在Linux容器(公有云、私有云,或者都要,等等)裏的巨大價值。另外,由於這些容器都是直接把這些服務打包起來、對外不透明的,因此咱們傾向於不要過多關心在容器裏面運行的究竟是什麼技術(是Java?仍是Node.js?或者Go?)。Netflix OSS主要是爲Java開發者服務的,它是許多的庫、框架和配置的集合,你須要把它們包含在你的Java程序或服務代碼裏面。html5
這就帶來了第一個問題。java
種類衆多的微服務是能夠用各類不一樣的框架或語言實現的,但像服務發現、負載均衡、容錯等等功能仍是很是重要並且必不可少的。若是咱們在容器裏面運行這些服務,咱們能夠藉助於強大的語言無關的架構來作各類事情,好比構建、打包、部署、健康檢查、滾動升級、藍綠髮布、安全、還有其它等等各類事情。做爲一種類型的Kubernetes,OpenShift專一於爲企業用戶服務,它把全部事情都幫你作完了:沒有什麼東西必需要你的應用層程序去知道或者處理的了。你只要簡簡單單的實現你的應用程序和服務就好,只要專一於它們應該作的功能就好。nginx
這樣是否是說這些架構能夠幫忙把你們從服務發現、負載均衡、容錯等功能中解放出來?那爲何這些還要是應用層的事?git
若是你使用Kubernets(或者某些變種),那麼答案就是:是的。程序員
用Netflix OSS時一般你要創建一臺服務發現服務器,讓全部能夠被各類客戶端發現的服務註冊在這裏。好比你用Netflix Ribbon來與各類其它服務交互,就須要能找出來它們都運行在哪裏。各類服務可能下線,可能按它們本身的運行須要退出,也有可能咱們要向集羣中加入更多服務來作橫向擴展。這種集中式的服務發現註冊機制一般能夠幫助咱們跟蹤集羣中有哪些服務是可用的。github
問題之一是,作爲一個開發人員你須要作這些事情:算法
並且,你要找到對應你使用的編程語言的客戶端庫,才能與服務發現機制通訊。回到咱們剛纔討論的問題,微服務多是用許多不一樣種語言實現的,因此可能一個成熟的Java客戶端庫好找,但相應的Go或Node.js庫就沒有了,那你只好本身寫一個。可對每一種語言和每個程序員,他們均可能對怎麼實現這樣的客戶端庫有本身的想法,這樣你就會忙於維護各類不一樣的客戶端庫,作的事情功能都是同樣的但是實現邏輯卻各自不一樣。也有可能每種語言都會使用它本身的服務發現服務器並且也有它本身現成的客戶端庫呢?因此你就要爲每一種語言都管理和維護不一樣的服務發現服務器嗎?無論哪一種方式都是很是使人討厭的。spring
這樣就解決了客戶端庫的問題嗎?DNS是全部使用TCP/UDP的系統自帶的,無論你是部署在單機、雲、容器、Windows或是Solaris等等哪一種系統上。你的客戶端程序只須要指向一個域名(好比http://awesomefooservice/),而後底層框架就知道該把服務路由到DNS指向的地方了(也多是用VIP加負載均衡,或者輪詢DNS等等)。好!如今咱們沒必要再關心我須要使用什麼樣的客戶端程序來發現一個服務了,我只須要使用任意一種TCP客戶端就好。我也沒必要關心如何管理DNS集羣,它是網絡路由器自帶的,你們都用得很熟。docker
缺點:DNS對於彈性的、動態的服務集羣就表現很差了。要向集羣中加入新服務時該怎麼辦?要下線服務呢?服務的IP地址可能緩存在DNS服務器或路由器中(也許並不屬於你的管轄範圍),也可能在你本身的IP棧中。還有若是你的程序或者服務要偵聽非標準的80端口呢?DNS是默認使用標準的80端口的。要讓DNS使用非標準端口,你就要用到DNS SRV記錄,這樣就又回到了最初的問題,你須要在應用程序層有特殊的客戶端庫來發現這些記錄。
我們就用Kubernetes吧。反正咱們要在Docker或者Linux容器裏面運行東西,那就最好是把Docker容器(或者Rocket容器,或者Hyper.sh容器等等)運行在Kubernetes裏面了。
(看,個人技術其實不好,尤爲是對於那些看起來好象很「簡單」的技術——由於你不可能用很複雜的組件來搭建出一個很複雜的系統。你會想要簡單的組件。但寫出簡單的組件這事自己其實很複雜。要我理解象Google或Red Hat作的和Kubernetes相關的、來簡化分佈式系統部署、管理等等的事對於我來講實在是象是天方夜譚。:) )
使用Kubernetes咱們只要建立並使用Kubernetes服務就行了。咱們沒必要浪費時間去搭建服務發現服務器、寫定製化的客戶端庫、調校DNS等等。它直接就可用,咱們只要直接進入咱們微服務的下一個主題便可,即提供業務價值。
下面這些簡單的抽象都是Kubernetes實現了來支持這些功能的:
Pod很簡單,它們基本上就是你的Linux容器。標籤也很簡單,它們基本上就是一些用於標記你的Pod的鍵-值型字符串(好比Pod A有以下標籤:app=cassandra、tier=backend、version=1.0、language=java)。這些標籤能夠是任何你想起的名字。
最後一個概念是服務。也很簡單,一個服務就是一個固定集羣IP地址。這個IP地址是虛擬的,能夠用來指向真正提供服務的Pod或者容器。那這個IP地址怎麼知道該找哪個Pod或者容器呢?它是用「標籤選擇器」來找到全部符合你要找的標籤的Pod的。好比,假如咱們須要一個有「app=cassandra AND tier=backend」標籤的Kubernetes服務,它就會幫咱們找到一個VIP,指向任何一個同時有這兩個標籤的Pod。這個選擇器是動態工做的,任何加入集羣的Pods都會根據它所帶有的標籤馬上自動參與服務發現。
另外一個用Kubernetes做爲Pod選擇服務的好處是Kubernetes很是智能,它知道哪一個Pod是屬於哪一個服務的,還會考慮它的存活和健康狀況。Kubernetes會使用內置的存活和健康檢查機制來判斷一個Pod是否應該被加入集羣,依據是它是否存活和它是否在正常工做。對於那些不符合條件的它會直接剔除掉。
要注意的是,一個Kubernetes服務的實例不是一個什麼「東西」,也不是應用、Docker容器等等任何東西,它是個虛擬的東西,因此天然也不會有單點故障。它就是一個由Kubernetes路由的IP地址。
這對於程序員來講實在是使人難以置信的強大和簡單。如今若是一個應用想要使用一個Cassandra服務,它會直接使用固定IP地址來找到Cassandra數據庫。但寫死一個固定IP地址確定不是什麼好作法,由於當你想要把你的程序或者服務挪個地方時就會遇到麻煩。因此通常作法是改IP(或者加個配置項),可這樣又加劇了程序配置功能的負擔,最終解決方案一般就是DNS。
使用Kubernetes內的DNS集羣就能夠解決上述問題。由於對於一個特定的運行環境(開發、QA等)IP地址是固定的,那咱們就不介意直接使用固定IP,反正它也永遠不會變。但若是咱們是使用DNS的話,舉個例子,就能夠把程序配置成與http://awesomefooservice上的服務交互,這樣無論咱們的運行環境怎麼變,從開發改到QA再改到生產,咱們都會部署那些Kubernetes服務,業務程序就不須要改了。
咱們不須要作額外的配置,也不用費心DNS緩存或SRV記錄、定製客戶端庫或管理額外的服務發現框架等等。Pod能夠自動加入集羣或從集羣中剔除掉,Kubernetes服務的標籤選擇器會動態的依據標籤分組。業務程序只須要與http://awesomefooservice/交互便可,隨便你是個Java應用,或者是Python、Node.js、Perl、Go、.NET、Ruby、C++、Scala、Groovy等什麼語言寫的都行。這種服務發現機制就不強求必須使用什麼特定的客戶端,你隨便用就行了。
這樣服務發現功能就大大簡化了。
這事頗有趣。Netflix提供了Eureka和Ribbon兩個用於客戶端側的負載均衡,你也能夠組合使用。基本實現就是服務註冊(Eureka/Consul/ZooKeeper等等)功能在跟蹤集羣中都有什麼服務,而且會向關心這些信息的客戶端更新這些信息。這樣客戶端就知道了集羣中都有哪些節點,它只須要選一個(隨機,或者固定,或者任何它本身定製的算法)而後調用就好。等下一次再調用時,它想的話它也能夠換另外一個來調用。這樣的好處是咱們不須要那些可能會迅速成爲系統瓶頸的軟/硬負載均衡器。另外一個重要方面是,當客戶端知道服務在哪裏以後,它直接與服務交互便可,中間不須要再通過中轉。
但依我拙見,客戶端側的負載均衡案例只能佔實際狀況的5%。我來解釋一下。
咱們想要的是一種理想的、可擴展的負載均衡機制,並且不要有額外的設備和客戶端庫等。大多數狀況下咱們並不介意處理過程當中請求會多一跳到負載均衡器(想一想看,可能你99%的應用都是這麼作的)。咱們可能會碰到這樣的狀況:服務A要調用B,B還要調用C,而後D、E,等等,想像一下那條調用鏈。這種狀況下若是每次調用都要增長額外的一跳的話,咱們的總體延遲就會變得很大。因此可能的解決方案就是要「減掉這多餘的一跳」,但這一跳也可能不止是到負載均衡器的,更好的辦法是要減小那條調用鏈的層級。請參考我博客上事件驅動系統專題中關於「自治與集中」的討論,我已經考慮過這樣的問題了。
根據上文將Kubernetes服務用做服務發現一節所描述的辦法 ,咱們能夠有不錯的負載均衡機制(也不會有各類服務註冊、定製客戶端、DNS缺點等額外開銷)。當咱們經過DNS或IP來與Kubernetes服務交互時,Kubernetes會默認地就在集羣中的Pod之間作負載均衡(注意集羣是由標籤和標籤選擇器定義的)。若是你不想有負載均衡的額外一跳也不用擔憂,虛擬IP是直接指向Pod的,不會通過實際的物理網絡中轉。
那95%的案例就輕鬆搞定了!因此沒必要過分設計,簡單就好。
那剩下的5%的狀況怎麼辦?可能你會有這樣的狀況,你的程序要在運行時決定調用集羣中的哪一個具體終端節點。通常來講你可能會想用些複雜的定製算法,而不是經常使用的輪詢、隨機、固定某一個等等。這時就可使用客戶端側的負載均衡機制。你仍然能夠用Kubernetes的服務發現機制來找出集羣中有哪些Pod是可用的,再根據標籤來決定調用哪一個。fabric8.io社區的Kubeflix項目爲Ribbon提供了發現插件,好比經過Kubernetes的REST API得到一個服務的全部Pod,而後調用者能夠用代碼來根據業務選擇具體調用哪一個,不限編程語言。對於這些狀況,花些精力來實現根據不一樣客戶端狀況定製的發現機制代碼庫是值得的,更好的作法是把這些定製的邏輯模塊化,把依賴關係從業務程序中獨立出去。這樣使用Kubernetes時,就能夠把這些獨立的算法模塊也隨着你的程序和服務部署上去,就能夠方便的使用定製的負載均衡算法了。
我仍是那句話,這是5%的須要有額外複雜處理的狀況。對於95%的狀況,使用內置的機制就夠了。
在搭建有依賴關係的系統時要時刻記得每一個模塊要對別人提供什麼服務,就是說即便調用方不存在或者崩潰了,它也要記得本身的義務。Kubernetes在容錯方面又有什麼功能呢?
Kubernetes的確是有自愈功能的。若是一個Pod或者Pod中的一個容器掛掉了,Kubernetes能夠把它再拉起來以維持ReplicaSet的不變性。好比你配置想要有10個叫「foo」的Pod,那Kubernetes就會幫你一直維持這個數量。即便某個Pod掛了,它也會再拉起一個來保持住10這個總數。
自愈功能太強大了,並且是隨着Kubernetes原生提供的,但咱們討論這個的緣由是若是被依賴物(數據庫或其餘服務)掛掉了,依賴它的業務程序該怎麼樣?這徹底要靠業務程序本身決定怎麼處理了。舉例來講,若是你想在Netflix上看個電影,就會有個請求發送到受權服務上,校驗你是否有權限去看那個電影。可若是受權服務掛了該怎麼辦呢?就不給用戶看那個電影嗎?或者把出錯日誌打給用戶看?Netflix的作法是容許用戶看。當受權服務出錯時,容許一個無權限的用戶看某個電影,這種體驗比直接拒絕要好得多,也許人家有這個權限呢?
比較好的作法是優雅的降級,或者找出替代方案來持續提供服務。Netflix Hystrix就是一個很是好的Java解決方案,它實現了機制來作隔離、熔斷和回滾。每一種都是針對不一樣業務的具體實現,因此在這種狀況下,針對不一樣的編程語言有定製的客戶端庫也是很是合理的。
Kubernetes也有相似功能嗎?固然!
再看看強大的Kubeflix項目,你能夠用Netflix Turbine項目來累積而且將你的集羣中運行的全部斷路器可視化。Hystrix能夠把全部的服務器事件以數據流的形式發送出去,由Turbine消費掉。那Turbine又怎麼知道哪些Pod裏面有Hystrix呢?好問題,這個能夠用Kubernetes的標籤解決。若是給全部有Hystrix的Pod全都打上個「hystrix.enabled=true」的標籤,Kubeflix Turbine引擎就能夠自動發現每一個Hystrix斷路器的SSE流,而且把它們展示在Turbine的網頁上。太感謝你了,Kubernetes!
上圖由Joseph Wilk繪製,謝謝。
Netflix Archaius是用於處理雲上系統的分佈式配置管理的。用法與Eureka和Ribbon同樣,搭起一個配置服務器,再用一個Java庫去查出配置項的值就能夠了,它也支持動態更改配置等。請記住Netflix是爲了在AWS上構建系統實現的這個功能,可是屬於Netflix的。做爲CI/CD管道的一部分,他們要構建AMI而且部署,可構建AMI或任何VM鏡像都是很是耗時的,而且大多數時候都要有不少前置工做。有了Docker或Linux容器,事情就容易多了,接下來我將從配置的角度解釋一下。
仍是先說95%的狀況。咱們但願把環境相關的配置信息保存在咱們的業務程序以外,再在運行時從運行環境(開發、QA、生產等)中得到它們。這裏有個很是重要的區別,不是每一個配置都和環境相關,要隨着運行環境來改的。並且咱們也很是想要有與編程語言不相關的辦法來查找配置,避免強迫你們用Java,而後又要配一堆的Java庫和路徑等。
咱們能夠用Kubernetes來提供基於環境的配置管理:
咱們能夠把配置信息經過環境變量提供給Linux容器,這樣無論Java、Node.js、GO、Ruby仍是Python等等,大多數編程語言均可以很容易獲取。也能夠把配置信息保存在Git上,再把Git Repo和Pod捆綁起來,映射成Pod本地文件系統的文件,這樣任何編程框架均可以以獲取本地文件的方式來獲取配置信息了,這是個好方案。最後,也能夠經過Kubernetes的ConfigMap來把版本化的配置信息保存在ConfigMap中,它也是作爲文件系統加載到Pod上,這樣就能夠將Git Repo解耦出去了。獲取配置信息的方法還是從文件系統的配置文件中讀數據,你本身喜歡用什麼編程語言或者框架都好。
另外5%的狀況呢?
在剩下的5%的狀況下你可能想要在程序運行時動態更改配置信息。這一點Kubernetes能夠幫忙。你只須要在ConfigMap中更改配置文件,而後把那些改變更態的推送到加載了它的Pod上就行了。在這個方案裏,你要使用客戶端的庫來幫助你感知到這些配置的改動,而且把它們提交到業務程序中。Netflix Archais就提供了有這樣功能的客戶端。Java版的Spring Cloud Kubernetes在用了ConfigMap時處理這樣的事情更容易。
使用Java的程序員們在Spring下開發微服務時經常把Spring Cloud和Netflix OSS等同起來,由於它很大一部分就是基於Netflix OSS實現的。fabric8.io社區中也有不少用Kubernetes運行Spring Cloud的好東西,請查看https://github.com/fabric8io/spring-cloud-kubernetes。包括配置、日誌等在內的不少模式均可以用Kubernetes運行得很是好,不用藉助服務發現引擎、配置管理引擎等額外的、複雜的框架。
若是你正在構建本身的微服務,並且你也對Netflix OSS/Java/Spring/Spring Cloud等方案很感興趣,請必定提醒本身大家不是Netflix,因此沒必要直接調用AWS EC2的原語來把本身的程序搞得很是複雜。若是你在調查Docker的方案,那採用Kubernetes是個很是明智的選擇,它自己就自帶許多這樣的分佈式系統功能。請在合適的時候將業務級程序庫分層,這樣能夠從一開始就避免把你的服務搞的太複雜,由於Netflix在5年前就很是明智的開始這樣作了。事實上他們也是不得不這樣的,但想一想若是5年前他們有Kubernetes又會是怎樣?他們的Netflix OSS棧會看起來徹底不一樣的! :)
閱讀英文原文:Netflix OSS, Spring Cloud, or Kubernetes? How About All of Them!