轉(zhuǎn)載:使用 Istio 實(shí)現(xiàn)基于 Kubernetes 的微服務(wù)應(yīng)用
近兩年雇卷,隨著容器、Kubernetes 等技術(shù)的興起郭宝,微服務(wù)被廣泛提及并被大量使用蝇裤。本文旨在讓讀者了解 Istio,通過(guò)它與 Kubernetes 相結(jié)合秦士,大幅降低微服務(wù)的復(fù)雜度缺厉,以便讓開發(fā)人員更關(guān)注于代碼本身。
Istio 的架構(gòu)分析
Istio 介紹
Istio 被稱為 Service Mesh 架構(gòu)隧土,該開源項(xiàng)目由 Google 和 IBM 主導(dǎo)提针,根據(jù) http://stackalytics.com 網(wǎng)站的統(tǒng)計(jì),該社區(qū)代碼 Commits 廠商排名如下:?
圖 1. Istio 各廠商代碼貢獻(xiàn)量圖示
圖 2. Istio 各廠商代碼貢獻(xiàn)量排名
在 GitHub 上曹傀,Istio 項(xiàng)目受關(guān)注的程度非常高辐脖,您可進(jìn)一步了解。接下來(lái)皆愉,我們?cè)敿?xì)介紹 Istio 的技術(shù)架構(gòu)嗜价。
Istio 的架構(gòu)
Istio 分為兩個(gè)平面:數(shù)據(jù)平面和控制平面。
數(shù)據(jù)平面:
數(shù)據(jù)平面由一組 Sidecar 的代理(Envoy)組成幕庐。這些代理調(diào)解和控制微服務(wù)之間的所有網(wǎng)絡(luò)通信久锥,并且與控制平面的 Mixer 通訊,接受調(diào)度策略异剥。
控制平面:
控制平面通過(guò)管理和配置 Envoy 來(lái)管理流量瑟由。此外,控制平面配置 Mixers 來(lái)實(shí)施路由策略并收集檢測(cè)到的監(jiān)控?cái)?shù)據(jù)届吁。?
圖 3. Istio 的架構(gòu)圖
在介紹了 Istio 的兩個(gè)平面以后错妖,我們?cè)敿?xì)介紹 Istio 的各個(gè)組件绿鸣。
Envoy 是一個(gè)用 C ++開發(fā)的高性能代理疚沐,用于管理 Service Mesh 中所有服務(wù)的所有入站和出站流量。 Istio 利用 Envoy 的許多內(nèi)置功能潮模,例如:
動(dòng)態(tài)服務(wù)發(fā)現(xiàn)
負(fù)載均衡
TLS 終止
HTTP / 2 和 gRPC 代理
斷路器
健康檢查
流量分割
故障注入
監(jiān)控指標(biāo)
我們知道亮蛔,在 Kubernetes 中集群中,Pod 是最小的計(jì)算資源調(diào)度單位擎厢。一個(gè) Pod 可以包含一個(gè)或者多個(gè)容器究流,但通常是一個(gè)。而使用 Istio 的時(shí)候动遭,需要在 Pod 中主容器旁注入一個(gè) Sidecar芬探,也就是上面提到的代理(Envoy)。
舉一個(gè)例子厘惦,我們查看一個(gè)被注入了 Envoy 的 Pod偷仿,從下圖結(jié)果可以看到,這個(gè) Pod 包含兩個(gè)容器:?
圖 4. 查看 Pod 中的 Container
在 Istio 中,每一個(gè) Pod 中都必須要部署一個(gè) Sidecar酝静。
Mixer 是一個(gè)獨(dú)立于平臺(tái)的組件节榜,負(fù)責(zé)在整個(gè) Service Mesh 中執(zhí)行訪問(wèn)控制和使用策略,并從 Envoy 代理和其他服務(wù)收集監(jiān)控到的數(shù)據(jù)别智。
Pilot 為 Envoy 提供服務(wù)發(fā)現(xiàn)宗苍;為高級(jí)路由(例如,A / B 測(cè)試薄榛,金絲雀部署等)提供流量管理功能讳窟;以及異常控制敞恋,如:超時(shí)挪钓,重試,斷路器等耳舅。
Citadel 通過(guò)內(nèi)置身份和憑證管理碌上,提供強(qiáng)大的服務(wù)到服務(wù)和最終用戶身份驗(yàn)證。我們可以使用 Citadel 升級(jí) Service Mesh 中的未加密流量浦徊。我們可以使用 Istio 的授權(quán)功能來(lái)控制誰(shuí)可以訪問(wèn)服務(wù)馏予。
Istio 路由規(guī)則的實(shí)現(xiàn)
在 Istio 中,和路由相關(guān)的有四個(gè)概念:Virtual Services 盔性、Destination Rules霞丧、ServiceEntry、Gateways冕香。
Virtual Services 的作用是:定義了針對(duì) Istio 中的一個(gè)微服務(wù)的請(qǐng)求的路由規(guī)則蛹尝。Virtual Services 既可以將請(qǐng)求路由到一個(gè)應(yīng)用的不同版本,也可以將請(qǐng)求路由到完全不同的應(yīng)用悉尾。
在如下的示例配置中突那,發(fā)給微服務(wù)的請(qǐng)求,將會(huì)被路由到 Productpage构眯,端口號(hào)為 9080愕难。
route:
? ?- destination:
? ? ? ?host: productpage
? ? ? ?port:
? ? ? ? ?number: 9080
清單 1. Virtual Services 規(guī)則
在下面的示例配置中,定義了熔斷策略惫霸。
spec:
?host: productpage
?subsets:
?- labels:
? ? ?version: v1
? ?name: v1
?trafficPolicy:
? ?connectionPool:
? ? ?http:
? ? ? ?http1MaxPendingRequests: 1
? ? ? ?maxRequestsPerConnection: 1
? ? ?tcp:
? ? ? ?maxConnections: 1
? ?tls:
清單 2. Destination 規(guī)則
ServiceEntry 用于將 Istio 外部的服務(wù)注冊(cè)到 Istio 的內(nèi)部服務(wù)注冊(cè)表猫缭,以便 Istio 內(nèi)部的服務(wù)可以訪問(wèn)這些外部的服務(wù),如 Istio 外部的 Web API壹店。
在如下的示例配置中猜丹,定義了 Istio 外部的 Mongo Cluster 與 Istio 內(nèi)部的訪問(wèn)規(guī)則。
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
?name: external-svc-mongocluster
spec:
?hosts:
?- mymongodb.somedomain # not used
?addresses:
?- 192.192.192.192/24 # VIPs
?ports:
?- number: 27018
? ?name: mongodb
? ?protocol: MONGO
?location: MESH_INTERNAL
?resolution: STATIC
?endpoints:
?- address: 2.2.2.2
?- address: 3.3.3.3
清單 3. ServiceEntry 規(guī)則
Gateway:定義了 Istio 邊緣的負(fù)載均衡器硅卢。所謂邊緣射窒,就是 Istio 的入口和出口妖混。這個(gè)負(fù)載均衡器用于接收傳入或傳出 Istio 的 HTTP / TCP 連接。在 Istio 中會(huì)有 Ingress Gateway 和 Egress Gateway轮洋,前者負(fù)責(zé)入口流量制市,后者負(fù)責(zé)出口流量。
在如下的示例配置中弊予,定義了 Istio 的入口流量祥楣。
spec:
?selector:
? ?istio: ingressgateway
?servers:
?- hosts:
? ?- '*'
? ?port:
? ? ?name: http
? ? ?number: 80
? ? ?protocol: HTTP
清單 4. Gateway 規(guī)則
Istio 的技術(shù)實(shí)現(xiàn)
基于 Kubernetes 部署的 Istio
在本文中,我們基于 Kubernetes 1.11 部署 Istio 1.04汉柒。由于篇幅有限误褪,具體的部署步驟可以參考 Quick Start with Kubernetes(https://istio.io/docs/setup/Kubernetes/quick-start/)。
查看 Kubernetes 版本:?
圖 5. 查看 Kubernetes 集群的版本
查看 Kubernetes 集群:?
圖 6. 查看 Kubernetes 集群的節(jié)點(diǎn)
查看部署好的 Istio碾褂。
Istio 以一個(gè)項(xiàng)目的形式部署到 Kubernetes 集群中兽间。我們可以看到,部署好的 Pod 中正塌,除了有 istio-citadel嘀略、istio-egressgateway、istio-ingressgateway乓诽、istio-pilot 等 Istio 本身的功能組件帜羊,還集成了微服務(wù)相關(guān)的監(jiān)控工具,如:Grafana鸠天、jaeger-agent讼育、Kiali、Prometheus稠集。正是這些功能豐富且強(qiáng)大的監(jiān)控工具奶段,幫助 Istio 實(shí)現(xiàn)了微服務(wù)的可視化管理。
圖 7. 查看 Kubernetes 中的 Istio
查看 istio 版本:1.0.4:?
圖 8. 查看 Istio 版本
接下來(lái)剥纷,我們將會(huì)對(duì) Isito 集成的工具進(jìn)行介紹痹籍。本文最后的實(shí)驗(yàn)展現(xiàn)環(huán)節(jié),我們將會(huì)使用這些工具進(jìn)行微服務(wù)的監(jiān)控和管理筷畦。
Istio 的工具集:Grafana
Grafana 是一個(gè)非常著名的開源項(xiàng)目词裤。它是一個(gè) Web 應(yīng)用刺洒,可以提供豐富的監(jiān)控儀表盤鳖宾。它的后端支持 Graphite、 InfluxDB 或 OpenTSDB逆航。
通過(guò)瀏覽器訪問(wèn) Istio 中部署好的 Grafana鼎文。
登錄 Grafana 后,首頁(yè)面如下:?
圖 9. Grafana 首頁(yè)面
查看已有的 Dashboard:?
圖 10. Grafana 上的 Istio Dashboard
我們查看 Pilot Dashboard因俐,可以看到豐富的資源統(tǒng)計(jì)拇惋。?
圖 11. Grafana 上的 Istio Dashboard 查看
Istio 的工具集:Prometheus
Prometheus 是一個(gè)開源監(jiān)控系統(tǒng)周偎。它具有維度數(shù)據(jù)模型;具備靈活的查詢語(yǔ)言撑帖、高效的時(shí)間序列數(shù)據(jù)庫(kù)蓉坎,并提供靈活的警報(bào)方法。
在 Istio 中胡嘿,Prometheus 收到的數(shù)據(jù)蛉艾,會(huì)被匯總到 Grafana 進(jìn)行統(tǒng)一展現(xiàn)。
訪問(wèn) Istio 中部署好的 Prometheus:?
圖 12. Prometheus 的 UI
我們可以看到有多達(dá)上百個(gè)監(jiān)測(cè)點(diǎn):?
圖 13. Prometheus 的監(jiān)測(cè)點(diǎn)
例如我們選擇 Containermemorycache衷敌,點(diǎn)擊 Execute勿侯。
圖 14. 執(zhí)行監(jiān)測(cè)點(diǎn)
然后可以生成圖形化界面展示,并且我們也可以調(diào)整時(shí)間間隔(圖中是 60 分鐘)缴罗。?
圖 15. 監(jiān)控圖
Istio 的工具集:Kiali
Kiali 作為一個(gè)開源項(xiàng)目助琐,可以為 Istio 提供可視化服務(wù)網(wǎng)格拓?fù)洹嗦菲骰蛘?qǐng)求率等功能面氓。此外 Kiali 還包括 Jaeger Tracing兵钮,可以提供開箱即用的分布式跟蹤功能。
我們看一下 Istio 中部署的 Kiali:?
圖 16. Kiali 的 UI 首頁(yè)
它可以查看在 Istio 上部署的微服務(wù)的拓?fù)浣Y(jié)構(gòu):?
圖 17. Kiali 查看微服務(wù)的拓?fù)?/p>
Istio 的工具集:Jaeger
Jaeger 是一個(gè)開源項(xiàng)目舌界,用于微服務(wù)的分布式跟蹤矢空。它實(shí)現(xiàn)的功能有:
分布式事務(wù)監(jiān)控
服務(wù)調(diào)用問(wèn)題根因分析
服務(wù)依賴性分析
性能/延遲優(yōu)化
Jaeger 工具已經(jīng)集成到 Istio 中,部署以后可以通過(guò)瀏覽器訪問(wèn)禀横。
下圖是 Jeager 追蹤 Productpage 這個(gè)服務(wù)在過(guò)去三個(gè)小時(shí)的所有調(diào)用:?
圖 18. Jaeger 查看 API 調(diào)用
我們可以展開看詳細(xì)的調(diào)用層級(jí):?
圖 19. Jaeger 查看 API 詳細(xì)調(diào)用
Istio 管理微服務(wù)的實(shí)驗(yàn)展現(xiàn)
在本小節(jié)中屁药,我們將在 Istio 上部署一個(gè)名為 bookinfo 的微服務(wù)應(yīng)用。為了方便讀者理解柏锄,我們先分析這個(gè)應(yīng)用的源代碼酿箭。然后展示 Istio 如何管理這套微服務(wù)。
bookinfo 微服務(wù)源碼分析
bookinfo 應(yīng)用程序顯示的有關(guān)書籍的信息趾娃,類似于在線書店的單個(gè)商品缭嫡。應(yīng)用頁(yè)面上顯示的是書籍的描述、書籍詳細(xì)信息(ISBN抬闷,頁(yè)數(shù)等)以及書評(píng)妇蛀。
bookinfo 應(yīng)用一共包含四個(gè)微服務(wù):Productpage、Details笤成、Reviews评架、Ratings。
Productpage 使用 Python 開發(fā)炕泳,負(fù)責(zé)展示書籍的名稱和書籍的簡(jiǎn)介纵诞。
Details 使用 Ruby 開發(fā),負(fù)責(zé)展示書籍的詳細(xì)信息培遵。
Reviews 使用 Java 開發(fā)浙芙,負(fù)責(zé)顯示書評(píng)登刺。
Ratings 使用 Node.js 開發(fā),負(fù)責(zé)顯示書籍的評(píng)星。
其拓?fù)潢P(guān)系見下圖嗡呼。?
圖 20. bookinfo 應(yīng)用拓?fù)浼軜?gòu)
我們看一下 bookinfo 微服務(wù)部署完畢的展示效果:?
圖 21. bookinfo 應(yīng)用頁(yè)面展示效果
Productpage 微服務(wù)包含兩部分內(nèi)容:
書籍的名稱:"The Comedy of Errors"纸俭,翻譯成中文是《錯(cuò)誤的喜劇》。
書籍的簡(jiǎn)介:Summary南窗。翻譯成中文是:《錯(cuò)誤的喜劇》是威廉·莎士比亞早期劇作之一掉蔬。這是他最短的、也是他最喜歡的喜劇之一矾瘾,除了雙關(guān)語(yǔ)和文字游戲之外女轿,幽默的主要部分來(lái)自于打鬧和錯(cuò)誤的身份。
Details 微服務(wù)包含的內(nèi)容是書籍的詳細(xì)信息壕翩,內(nèi)容如下:
Type:
paperback
Pages:
200
Publisher:
PublisherA
Language:
English
ISBN-10:
1234567890
ISBN-13:
123-1234567890
清單 5. Details 微服務(wù)顯示內(nèi)容
Reviews 微服務(wù)包含的信息是書評(píng)內(nèi)容:
An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!
Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.
清單 6. 書評(píng)內(nèi)容
Ratings 微服務(wù)包含的內(nèi)容將展示為評(píng)星部分(下圖黑框中的部分)蛉迹。
圖 22. 評(píng)星圖
接下來(lái),我們?cè)L問(wèn) GitHub 上查看 bookinfo 源碼放妈,通過(guò)源碼分析北救,理解業(yè)務(wù)的實(shí)現(xiàn)邏輯。
首先查看 Productpage(https://github.com/istio/istio/tree/master/samples/bookinfo/src/productpage)的源碼(部分內(nèi)容):
def getProducts():
? ? ? ?return [
? ? ? ? ? ?{
? ? ? ? ? ? ? ?'id': 0,
? ? ? ? ? ? ? ?'title': 'The Comedy of Errors',
? ? ? ? ? ? ? ?'descriptionHtml': '<a >Wikipedia Summary</a>: The Comedy of Errors is one of < b>William Shakespeare\'s< /b> early plays. It is his shortest and one of his most farcical comedies, with a major part of the humour coming from slapstick and mistaken identity, in addition to puns and word play.'
? ? ? ? ? ?}
清單 7. Productpage 源碼
我們可以很明顯看出芜抒,以上代碼就是 bookinfo 頁(yè)面顯示的書籍的名稱和簡(jiǎn)介珍策。
查看 Productpage 的另外一部分源碼。
details = {
"name" : "http://details{0}:9080".format(servicesDomain),
"endpoint" : "details",
"children" : []
}
ratings = {
"name" : "http://ratings{0}:9080".format(servicesDomain),
"endpoint" : "ratings",
"children" : []
}
reviews = {
"name" : "http://reviews{0}:9080".format(servicesDomain),
"endpoint" : "reviews",
"children" : [ratings]
}
productpage = {
"name" : "http://details{0}:9080".format(servicesDomain),
"endpoint" : "details",
"children" : [details, reviews]
}
service_dict = {
"productpage" : productpage,
"details" : details,
"reviews" : reviews,
}
清單 8. Productpage 源碼
上面代碼定義了四個(gè)微服務(wù)的 name宅倒、endpoint攘宙、children。endpoint 代表這個(gè)微服務(wù)后端的 Kubernetes 集群中 service 名稱拐迁、children 代表本微服務(wù)調(diào)用的 Kubernetes 集群中的微服務(wù) service 名稱蹭劈。
以 Productpage 舉例,它的 endpoint 是 details线召、children 是 details 和 reviews铺韧。所以,被發(fā)送到 Productpage 請(qǐng)求缓淹,將會(huì)調(diào)用 details哈打、reviews 這兩個(gè)服務(wù)。
接下來(lái)讯壶,查看 reviews 微服務(wù)的源碼料仗,代碼使用 Java 編寫的。
private String getJsonResponse (String productId, int starsReviewer1, int starsReviewer2) {
? ?String result = "{";
? ?result += "\"id\": \"" + productId + "\",";
? ?result += "\"reviews\": [";
? ?// reviewer 1:
? ?result += "{";
? ?result += " ?\"reviewer\": \"Reviewer1\",";
? ?result += " ?\"text\": \"An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!\"";
?if (ratings_enabled) {
? ?if (starsReviewer1 != -1) {
? ? ?result += ", \"rating\": {\"stars\": " + starsReviewer1 + ", \"color\": \"" + star_color + "\"}";
? ?}
? ?else {
? ? ?result += ", \"rating\": {\"error\": \"Ratings service is currently unavailable\"}";
? ?}
?}
? ?result += "},";
? ?// reviewer 2:
? ?result += "{";
? ?result += " ?\"reviewer\": \"Reviewer2\",";
? ?result += " ?\"text\": \"Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare.\"";
?if (ratings_enabled) {
? ?if (starsReviewer2 != -1) {
? ? ?result += ", \"rating\": {\"stars\": " + starsReviewer2 + ", \"color\": \"" + star_color + "\"}";
? ?}
? ?else {
? ? ?result += ", \"rating\": {\"error\": \"Ratings service is currently unavailable\"}";
? ?}
?}
? ?result += "}";
? ?result += "]";
? ?result += "}";
? ?return result;
}
清單 9. reviews 服務(wù)源碼
上面的這段代碼鹏溯,定義的是兩個(gè) Reviewer罢维,以及書評(píng)的內(nèi)容。書評(píng)的內(nèi)容正是 bookinfo 頁(yè)面展示的內(nèi)容丙挽。
在上面的代碼中肺孵,我們注意到有兩個(gè)重要的變量 starcolor 和 ratingsenabled钝凶。
star_color 表示評(píng)星的顏色(黑色和紅色)害淤。
ratings_enabled 表示是否啟用評(píng)星。
查看 reviews 微服務(wù)的源碼的另外一部分內(nèi)容:
private final static String star_color = System.getenv("STAR_COLOR") == null ? "black" : System.getenv("STAR_COLOR");
清單 10. reviews 服務(wù)源碼
上面代碼顯示:在應(yīng)用構(gòu)建時(shí):
如果不指定 STARCOLOR 變量且 ratingsenabled 為 true跌榔,那么評(píng)星默認(rèn)為黑色凳怨。
如果指定 STARCOLOR 變量且 ratingsenabled 為 true瑰艘,那么評(píng)星顏色為傳入的顏色。
如果不指定 ratings_enabled 為 true肤舞,那么將不會(huì)顯示評(píng)星紫新。
那么,STAR_COLOR 這個(gè)變量李剖,在應(yīng)用構(gòu)建時(shí)芒率,有沒有傳入呢?我們查看:build-services.sh
#java build the app.
docker run --rm -v "$(pwd)":/home/gradle/project -w /home/gradle/project gradle:4.8.1 gradle clean build
pushd reviews-wlpcfg
?#plain build -- no ratings
?docker build -t "istio/examples-bookinfo-reviews-v1:${VERSION}" -t istio/examples-bookinfo-reviews-v1:latest --build-arg service_version=v1 .
?#with ratings black stars
?docker build -t "istio/examples-bookinfo-reviews-v2:${VERSION}" -t istio/examples-bookinfo-reviews-v2:latest --build-arg service_version=v2 \
? ? --build-arg enable_ratings=true .
?#with ratings red stars
?docker build -t "istio/examples-bookinfo-reviews-v3:${VERSION}" -t istio/examples-bookinfo-reviews-v3:latest --build-arg service_version=v3 \
? ? --build-arg enable_ratings=true --build-arg star_color=red .
清單 11. build-services.sh 內(nèi)容
上面代碼顯示篙顺,運(yùn)行該腳本是偶芍,將會(huì)構(gòu)建三個(gè)版本 Reviews 的 docker image:
V1:沒有評(píng)星(未指定 enable_ratings=true)。
V2:評(píng)星為黑色(指定 enableratings=true德玫;未指定 starcolor 的變量匪蟀,代碼中默認(rèn)的顏色為黑色)。
V3:評(píng)星為紅色(指定 enableratings=true宰僧;指定 starcolor 的變量為 red)材彪。
在 bookinfo 的源代碼中,還有兩個(gè)數(shù)據(jù)庫(kù)的定義 MongoDB 和 MySQL琴儿。
接下來(lái)查刻,我們看這個(gè)應(yīng)用中兩個(gè)數(shù)據(jù)庫(kù)的內(nèi)容。
先看 MongoDB 的 script.sh凤类,內(nèi)容如下:
#!/bin/sh
? ?set -e
? ?mongoimport --host localhost --db test --collection ratings --drop --file /app/data/ratings_data.json
清單 12. MongoDB 的 script.sh
也就是說(shuō)穗泵,MongoDB 數(shù)據(jù)庫(kù)在初始化時(shí),會(huì)將 ratings_data.json 文件中的信息導(dǎo)入到數(shù)據(jù)庫(kù)中谜疤。
再看 ratings_data.json:
{rating: 5}
{rating: 4}
清單 13. ratings_data.json 文件
也就是說(shuō)佃延,當(dāng)應(yīng)用部署完畢后,MongoDB 將包含五星和四星夷磕。
查看 MySQL 的初始化文件:mysqldb-init.sql履肃。
# Initialize a mysql db with a 'test' db and be able test productpage with it.
# mysql -h 127.0.0.1 -ppassword < mysqldb-init.sql
CREATE DATABASE test;
USE test;
CREATE TABLE `ratings` (
?`ReviewID` INT NOT NULL,
?`Rating` INT,
?PRIMARY KEY (`ReviewID`)
);
INSERT INTO ratings (ReviewID, Rating) VALUES (1, 5);
INSERT INTO ratings (ReviewID, Rating) VALUES (2, 4);
清單 14. mysqldb-init.sql 內(nèi)容
我們可以看出,上面的初始化腳本是創(chuàng)建一個(gè)名為 Ratings 的數(shù)據(jù)庫(kù)表坐桩,插入的數(shù)據(jù)效果如下:
ReviewIDRating
15
24
表 1. 數(shù)據(jù)庫(kù)表示意
查看 Ratings 的源代碼尺棋,該代碼使用 Node.JS 書寫。
* We default to using mongodb, if DB_TYPE is not set to mysql.
*/
if (process.env.SERVICE_VERSION === 'v2') {
?if (process.env.DB_TYPE === 'mysql') {
? ?var mysql = require('mysql')
? ?var hostName = process.env.MYSQL_DB_HOST
? ?var portNumber = process.env.MYSQL_DB_PORT
? ?var username = process.env.MYSQL_DB_USER
? ?var password = process.env.MYSQL_DB_PASSWORD
?} else {
? ?var MongoClient = require('mongodb').MongoClient
? ?var url = process.env.MONGO_DB_URL
?}
}
dispatcher.onGet(/^\/ratings\/[0-9]*/, function (req, res) {
?var productIdStr = req.url.split('/').pop()
?var productId = parseInt(productIdStr)
?if (Number.isNaN(productId)) {
? ?res.writeHead(400, {'Content-type': 'application/json'})
? ?res.end(JSON.stringify({error: 'please provide numeric product ID'}))
?} else if (process.env.SERVICE_VERSION === 'v2') {
? ?var firstRating = 0
? ?var secondRating = 0
? ?if (process.env.DB_TYPE === 'mysql') {
? ? ?var connection = mysql.createConnection({
? ? ? ?host: hostName,
? ? ? ?port: portNumber,
? ? ? ?user: username,
? ? ? ?password: password,
? ? ? ?database: 'test'
? ? ?})
? ? ?connection.connect()
? ? ?connection.query('SELECT Rating FROM ratings', function (err, results, fields) {
? ? ? ?if (err) {
? ? ? ? ?res.writeHead(500, {'Content-type': 'application/json'})
? ? ? ? ?res.end(JSON.stringify({error: 'could not connect to ratings database'}))
? ? ? ?} else {
? ? ? ? ?if (results[0]) {
? ? ? ? ? ?firstRating = results[0].Rating
? ? ? ? ?}
? ? ? ? ?if (results[1]) {
? ? ? ? ? ?secondRating = results[1].Rating
? ? ? ? ?}
? ? ? ? ?var result = {
? ? ? ? ? ?id: productId,
? ? ? ? ? ?ratings: {
? ? ? ? ? ? ?Reviewer1: firstRating,
? ? ? ? ? ? ?Reviewer2: secondRating
? ? ? ? ? ?}
? ? ? ? ?}
? ? ? ? ?res.writeHead(200, {'Content-type': 'application/json'})
? ? ? ? ?res.end(JSON.stringify(result))
? ? ? ?}
? ? ?})
清單 15. Ratings 的源代碼
以上代碼主要實(shí)現(xiàn):如果不指定 DB_TYPE 的變量绵跷,將默認(rèn)使用 MongoDB 數(shù)據(jù)庫(kù)膘螟。
當(dāng)微服務(wù) Reviews 的版本是 V2 時(shí)成福,將連接數(shù)據(jù)庫(kù) MySQL 或 MongoDB(根據(jù)環(huán)境變量傳入的 DB_TYPE)。當(dāng) Reviews 的版本是 V3 時(shí)荆残,訪問(wèn) MongoDB 數(shù)據(jù)庫(kù)奴艾。
但從上面的數(shù)據(jù)庫(kù)分析我們可以知道,無(wú)論 Reviews 連接哪個(gè)數(shù)據(jù)庫(kù)内斯,得到的數(shù)據(jù)都是第一個(gè)評(píng)論者五星蕴潦、第二個(gè)評(píng)論者四星。也就是說(shuō)俘闯,只要是 Reviews 的 V2 和 V3 版本潭苞,訪問(wèn)數(shù)據(jù)庫(kù)得到的評(píng)星結(jié)果是一樣的;只不過(guò) Reviews 為 V2 時(shí)評(píng)星為黑色真朗、Reviews 為 V3 時(shí)評(píng)星為紅色此疹。
微服務(wù)的部署
我們?cè)?Kubernetes 集群中部署 bookinfo 應(yīng)用。
圖 23. 在 Kubernetes 集群部署 bookinfo
Pod 創(chuàng)建成功:?
圖 24. 查看 bookinfo 的 Pod
接下來(lái)蜜猾,通過(guò)瀏覽器對(duì) bookinfo 發(fā)起多次訪問(wèn)秀菱,頁(yè)面呈現(xiàn)三種顯示。
第一種:訪問(wèn) bookinfo 時(shí)(Productpage 調(diào)用的 Review V1)蹭睡,頁(yè)面沒有評(píng)星衍菱;
第二種:訪問(wèn) bookinfo 時(shí)(Productpage 調(diào)用的 Review V2),頁(yè)面是黑色的評(píng)星肩豁;
第三種:訪問(wèn) bookinfo 時(shí)(Productpage 調(diào)用的 Review V3)脊串,頁(yè)面是紅色的評(píng)星。
圖 25. bookinfo 第一種展現(xiàn)
圖 26. bookinfo 第二種展現(xiàn)
圖 27. bookinfo 第三種展現(xiàn)
過(guò)了幾秒后:Kiali 收集到之前幾次對(duì) bookinfo 的訪問(wèn)流量清钥,并進(jìn)行動(dòng)態(tài)展示琼锋。我們可以看到,Productpage 隨機(jī)訪問(wèn) Reviews V1祟昭、V2缕坎、V3。
圖 28. Kiali 展示微服務(wù)流量
Productpage 輪詢?cè)L問(wèn) Review V1 和 V2 的原因是:我們沒有設(shè)置針對(duì) Reviews 的特定策略篡悟,而 Productpage 的源碼中谜叹,指定了 Product 服務(wù)調(diào)用 Reviews 服務(wù)的業(yè)務(wù)邏輯,但并未指定版本搬葬。因此荷腊,Product 服務(wù)會(huì)隨機(jī)訪問(wèn) Reviews 的三個(gè)版本。?
圖 29. 查看 virtualservice
接下來(lái)急凰,我們查看 Virtualservice 的配置文件女仰。
圖30. 查看 Virtualservice 配置文件列表
查看 virtual-service-reviews-v3.yaml 內(nèi)容。該文件定義發(fā)向 Reviews 的請(qǐng)求,全部到 V3 版本疾忍。
[root@master networking]# cat virtual-service-reviews-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
?name: reviews
spec:
?hosts:
? ?- reviews
?http:
?- route:
? ?- destination:
? ? ? ?host: reviews
? ? ? ?subset: v3 ? ? ?version: v3
清單 16. 查看 virtual-service-reviews-v3.yaml 內(nèi)容
接下來(lái)乔外,應(yīng)用 Virtualservice 配置。
圖 31. 應(yīng)用配置
查看生效的 Virtualservice锭碳,reviews 的配置生效袁稽。
圖 32. 查看生效的 Virtualservice
接下來(lái)勿璃,我們?cè)俅螌?duì) bookinfo 發(fā)起多次訪問(wèn)擒抛,可以看到,頁(yè)面的評(píng)星均為紅色补疑。?
圖 33. 訪問(wèn) bookinfo 應(yīng)用
通過(guò) Kiali 查看流量歧沪,可以看到,Productpage 的流量全部訪問(wèn) Review V3莲组。
圖 34. Kiali 查看 bookinfo 應(yīng)用訪問(wèn)流量
接下來(lái)诊胞,我們繼續(xù)調(diào)整策略,讓 Productpage 對(duì) Reviews 的訪問(wèn)锹杈,以 V1 和 V2 按照 8:2 比率進(jìn)行:
[root@master networking]# cat virtual-service-reviews-80-20.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
?name: reviews
spec:
?hosts:
? ?- reviews
?http:
?- route:
? ?- destination:
? ? ? ?host: reviews
? ? ? ?subset: v1
? ? ?weight: 80
? ?- destination:
? ? ? ?host: reviews
? ? ? ?subset: v2
? ? ?weight: 20
清單 17. 查看 virtual-service-reviews-80-20.yaml 內(nèi)容
圖 35. 替換之前的 Virtual Services 策略
圖 36. Kiali 查看 bookinfo 應(yīng)用訪問(wèn)流量
Istio 的限速
在了解了 Istio 微服務(wù)的路由策略后撵孤,接下來(lái)我們對(duì)微服務(wù)組件進(jìn)行限速的設(shè)置。
在默認(rèn)情況下竭望,Productpage 隨機(jī)訪問(wèn) reviews 三個(gè)版本邪码,沒有流量限制。我們編寫兩個(gè)配置文件咬清,限制 Productpage 到 reviews v3 的訪問(wèn)速度闭专。
speedrule.yaml 限制了從 Productpage 到 reviews v3 的訪問(wèn)速度,最多每秒一個(gè)請(qǐng)求旧烧。
[root@master ~]# cat speedrule.yaml
apiVersion: "config.istio.io/v1alpha2"
kind: memquota
metadata:
?name: handler
?namespace: myproject
spec:
?quotas:
?- name: requestcount.quota.myproject
? ?# default rate limit is 5000qps
? ?maxAmount: 5000
? ?validDuration: 1s
? ?# The first matching override is applied.
? ?# A requestcount instance is checked against override dimensions.
? ?overrides:
? ?- dimensions:
? ? ? ?destination: reviews
? ? ? ?source: productpage
? ? ? ?destinationVersion: v3
? ? ?maxAmount: 1
? ? ?validDuration: 1s
recommendation_rate_limit_handler.yml 文件聲明了 requestcount quota影钉。
清單 18. 查看 speedrule.yaml
[root@master ~]# cat recommendation_rate_limit_handler.yml
apiVersion: "config.istio.io/v1alpha2"
kind: quota
metadata:
?name: requestcount
?namespace: myproject
spec:
?dimensions:
? ?source: source.labels["app"] | source.service | "unknown"
? ?sourceVersion: source.labels["version"] | "unknown"
? ?destination: destination.labels["app"] | destination.service | "unknown"
? ?destinationVersion: destination.labels["version"] | "unknown"
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
?name: quota
?namespace: myproject
spec:
?actions:
?- handler: handler.memquota
? ?instances:
? ?- requestcount.quota
---
apiVersion: config.istio.io/v1alpha2
kind: QuotaSpec
metadata:
?creationTimestamp: null
?name: request-count
?namespace: myproject
spec:
?rules:
?- quotas:
? ?- charge: 1
? ? ?quota: RequestCount
---
apiVersion: config.istio.io/v1alpha2
kind: QuotaSpecBinding
metadata:
?creationTimestamp: null
?name: request-count
?namespace: myproject
spec:
?quotaSpecs:
?- name: request-count
? ?namespace: myproject
?services:
?- name: productpage
? ?namespace: myproject
?- name: details
? ?namespace: myproject
?- name: reviews
? ?namespace: myproject
清單 19. 查看 recommendation_rate_limit_handler.yml
應(yīng)用兩個(gè)配置:
圖 37. 應(yīng)用配置
圖 38. 應(yīng)用配置
然后,對(duì) bookinfo 發(fā)起高頻度請(qǐng)求掘剪,訪問(wèn)請(qǐng)求頻率為 10 次/秒平委。
while true; do curl http://istio-ingressgateway-istio-system.apps.example.com/productpage; sleep .1; done
清單 20. 發(fā)起高頻度訪問(wèn)請(qǐng)求
通過(guò) Kiali,可以看到:?
圖 39. Kiali 顯示微服務(wù)流量
我們查看 Grafana夺谁,可以看到這段時(shí)間內(nèi)廉赔,reviews v3 的流量,遠(yuǎn)低于同期 reviews v1 和 v2 的流量予权。
圖 40. Grafana 顯示微服務(wù)流量統(tǒng)計(jì)
Istio 的熔斷
熔斷技術(shù)昂勉,是為了避免"雪崩效應(yīng)"的產(chǎn)生而出現(xiàn)的。我們都知道"雪球越滾越大"的現(xiàn)象扫腺。應(yīng)用中的雪崩指的由于應(yīng)用第一個(gè)組件的出現(xiàn)問(wèn)題岗照,造成調(diào)用這個(gè)組件的第二個(gè)組件有無(wú)無(wú)法調(diào)用第一個(gè)組件,無(wú)法實(shí)現(xiàn)業(yè)務(wù)邏輯,也出現(xiàn)問(wèn)題攒至;而調(diào)用第二個(gè)組件第三個(gè)組件因此也出現(xiàn)問(wèn)題厚者,問(wèn)題迅速傳播,從而造成整個(gè)應(yīng)用的癱瘓迫吐,我們稱之為應(yīng)用的雪崩效應(yīng)库菲。
在單體應(yīng)用中,多個(gè)業(yè)務(wù)的功能模塊放在一個(gè)應(yīng)用中志膀,且由于各個(gè)功能模塊之前是緊耦合熙宇,因此不容易出現(xiàn)雪崩情況。但由于微服務(wù)松耦合溉浙、各個(gè)組件調(diào)用關(guān)系復(fù)雜的特點(diǎn)烫止,雪崩現(xiàn)象就較為容易出現(xiàn)。為了避免雪崩情況的發(fā)生戳稽,就需要有熔斷機(jī)制馆蠕,采用斷路模式。熔斷機(jī)制相當(dāng)于為每個(gè)微服務(wù)前面加一個(gè)"保險(xiǎn)絲"惊奇。當(dāng)電流負(fù)載過(guò)大的時(shí)候(如服務(wù)訪出現(xiàn)故障問(wèn)超時(shí)互躬,并且超過(guò)設(shè)定的重試次數(shù)),保險(xiǎn)絲燒斷颂郎,中斷客戶端對(duì)該應(yīng)用的訪問(wèn)吼渡,而不影響客戶端訪問(wèn)其他正常運(yùn)行的組件。
Spring Cloud 中熔斷的實(shí)現(xiàn)祖秒,需要調(diào)用 Hystrix诞吱。而 Istio 本身自帶熔斷的功能。下面竭缝,我們進(jìn)行實(shí)現(xiàn)展現(xiàn)房维。
在初始情況下,未配置熔斷抬纸。?
圖 41. 配置熔斷之前正常訪問(wèn)應(yīng)用流量
接下來(lái)咙俩,我們?cè)?Productpage 的 destination rule 中配置熔斷策略(trafficPolicy):每個(gè)鏈接最多的請(qǐng)求數(shù)量是一個(gè);最多 pending 的 request 是一個(gè)湿故、最多的連接數(shù)是一個(gè)阿趁。
spec:
?host: productpage
?subsets:
?- labels:
? ? ?version: v1
? ?name: v1
?trafficPolicy:
? ?connectionPool:
? ? ?http:
? ? ? ?http1MaxPendingRequests: 1
? ? ? ?maxRequestsPerConnection: 1
? ? ?tcp:
? ? ? ?maxConnections: 1
? ?tls:
? ? ?mode: ISTIO_MUTUAL
清單 21. 在 destination rule 中配置熔斷(open.yaml 配置文件部分內(nèi)容)
接下來(lái),我們先刪除舊的配置坛猪,應(yīng)用熔斷的配置脖阵。?
圖 42. 應(yīng)用新配置
圖 43. 發(fā)起大并發(fā)流量后的熔斷
過(guò)一會(huì),當(dāng)問(wèn)題熔斷器打開后墅茉,業(yè)務(wù)恢復(fù)正常:?
圖 44. 熔斷后的應(yīng)用訪問(wèn)頁(yè)面
Istio 的訪問(wèn)控制
Istio 中的訪問(wèn)控制有白名單和黑名單命黔。白名單是允許從哪個(gè)服務(wù)到哪個(gè)服務(wù)的訪問(wèn)呜呐;黑名單是不允許從哪個(gè)服務(wù)到哪個(gè)服務(wù)之間的訪問(wèn)。兩種實(shí)現(xiàn)的效果展現(xiàn)是一樣的悍募,由于篇幅有限蘑辑,本小節(jié)展示黑名單。
我們將在 details 服務(wù)上創(chuàng)建一個(gè)黑名單坠宴,從 Productpage 發(fā)往 details 請(qǐng)求將會(huì)返回 403 錯(cuò)誤碼洋魂。
[root@master ~]# cat acl-blacklist.yml
apiVersion: "config.istio.io/v1alpha2"
kind: denier
metadata:
?name: denycustomerhandler
spec:
?status:
? ?code: 7
? ?message: Not allowed
---
apiVersion: "config.istio.io/v1alpha2"
kind: checknothing
metadata:
?name: denycustomerrequests
spec:
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
?name: denycustomer
spec:
?match: destination.labels["app"] == "details" && source.labels["app"]=="productpage"
?actions:
?- handler: denycustomerhandler.denier
instances: [ denycustomerrequests.checknothing ]
清單 22. 黑名單配置文件
圖 45. 應(yīng)用黑名單
接下來(lái),對(duì) Producepage 發(fā)起流量訪問(wèn)喜鼓。從下圖可以看到副砍,從 Productpage 到 details 之間的訪問(wèn)是被拒絕的。?
圖 46. Kali 顯示應(yīng)用流量訪問(wèn)
此時(shí)颠通,通過(guò)瀏覽器訪問(wèn) bookinfo址晕,界面無(wú)法顯示產(chǎn)品的詳細(xì)信息膀懈,但其他微服務(wù)顯示正常顿锰。?
圖 47. 訪問(wèn) bookinfo 應(yīng)用
我們刪除黑名單,再訪問(wèn) bookinfo启搂,對(duì) details 微服務(wù)的訪問(wèn)馬上正常硼控。
圖 48. 刪除黑名單策略
圖 49. Kali 顯示應(yīng)用流量訪問(wèn)
圖 50. 訪問(wèn) bookinfo 應(yīng)用
總結(jié)
通過(guò)本文,相信讀者對(duì)微服務(wù)的概念和 Istio 的架構(gòu)有了一定程度的理解胳赌。在微服務(wù)領(lǐng)域牢撼,正是由于 Istio 強(qiáng)大的功能、豐富的界面疑苫、可視化的監(jiān)控熏版,Istio 的使用將會(huì)越來(lái)越廣泛。
原文鏈接:https://www.ibm.com/developerworks/cn/cloud/library/cl-lo-implementing-kubernetes-microservice-using-istio/index.html