Istio is the future!基本上,我相信對雲原生技術趨勢有些微判斷的同窗,都會有這個覺悟。其背後的邏輯實際上是比較簡單的:當容器集羣,特別是K8S成爲事實上的標準以後,應用必然會不斷的複雜化,服務治理確定會成爲強需求。nginx
Istio的現狀是,聊的人不少,用的人其實不多。因此致使咱們能看到的文章,講道理的不少,講實際踩坑經驗的極少。docker
阿里雲售後團隊做爲一線踩坑團隊,分享問題排查經驗,咱們義不容辭。這篇文章,我就跟你們聊一個簡單Istio問題的排查過程,權當拋磚。後端
問題是這樣的,用戶在本身的測試集羣裏安裝了Istio,並依照官方文檔部署bookinfo應用來上手Istio。部署以後,用戶執行kubectl get pods命令,發現全部的pods都只有二分之一個容器是READY的。bash
# kubectl get pods NAME READY STATUS RESTARTS AGE details-v1-68868454f5-94hzd 1/2 Running 0 1m productpage-v1-5cb458d74f-28nlz 1/2 Running 0 1m ratings-v1-76f4c9765f-gjjsc 1/2 Running 0 1m reviews-v1-56f6855586-dplsf 1/2 Running 0 1m reviews-v2-65c9df47f8-zdgbw 1/2 Running 0 1m reviews-v3-6cf47594fd-cvrtf 1/2 Running 0 1m
若是歷來都沒有注意過READY這一列的話,咱們大概會有兩個疑惑:2在這裏是什麼意思,以及1/2到底意味着什麼。服務器
簡單來說,這裏的READY列,給出的是每一個pod內部容器的readiness,即就緒狀態。每一個集羣節點上的kubelet會根據容器自己readiness規則的定義,分別是tcp、http或exec的方式,來確認對應容器的readiness狀況。網絡
更具體一點,kubelet做爲運行在每一個節點上的進程,以tcp/http的方式(節點網絡命名空間到pod網絡命名空間)訪問容器定義的接口,或者在容器的namespace裏執行exec定義的命令,來肯定容器是否就緒。負載均衡
這裏的2說明這些pod裏都有兩個容器,1/2則表示,每一個pod裏只有一個容器是就緒的,即經過readiness測試的。關於2這一點,咱們下一節會深刻講,這裏咱們先看一下,爲何全部的pod裏,都有一個容器沒有就緒。dom
使用kubectl工具拉取第一個details pod的編排模板,能夠看到這個pod裏兩個容器,只有一個定義了readiness probe。對於未定義readiness probe的容器,kubelet認爲,只要容器裏的進程開始運行,容器就進入就緒狀態了。因此1/2個就緒pod,意味着,有定義readiness probe的容器,沒有經過kubelet的測試。curl
沒有經過readiness probe測試的是istio-proxy這個容器。它的readiness probe規則定義以下。tcp
readinessProbe: failureThreshold: 30 httpGet: path: /healthz/ready port: 15020 scheme: HTTP initialDelaySeconds: 1 periodSeconds: 2 successThreshold: 1 timeoutSeconds: 1
咱們登陸這個pod所在的節點,用curl工具來模擬kubelet訪問下邊的uri,測試istio-proxy的就緒狀態。
# curl http://172.16.3.43:15020/healthz/ready -v * About to connect() to 172.16.3.43 port 15020 (#0) * Trying 172.16.3.43... * Connected to 172.16.3.43 (172.16.3.43) port 15020 (#0) > GET /healthz/ready HTTP/1.1 > User-Agent: curl/7.29.0 > Host: 172.16.3.43:15020 > Accept: */*> < HTTP/1.1 503 Service Unavailable< Date: Fri, 30 Aug 2019 16:43:50 GMT < Content-Length: 0 < * Connection #0 to host 172.16.3.43 left intact
上一節咱們描述了問題現象,可是留下一個問題,就是pod裏的容器個數爲何是2。雖然每一個pod本質上至少有兩個容器,一個是佔位符容器pause,另外一個是真正的工做容器,可是咱們在使用kubectl命令獲取pod列表的時候,READY列是不包括pause容器的。
這裏的另一個容器,其實就是服務網格的核心概念sidercar。其實把這個容器叫作sidecar,某種意義上是不能反映這個容器的本質的。Sidecar容器本質上是反向代理,它原本是一個pod訪問其餘服務後端pod的負載均衡。
然而,當咱們爲集羣中的每個pod,都「隨身」攜帶一個反向代理的時候,pod和反向代理就變成了服務網格。正以下邊這張經典大圖所示。這張圖實在有點難畫,因此只能借用,繞不過去。
因此sidecar模式,實際上是「自帶通訊員」模式。這裏比較有趣的是,在咱們把sidecar和pod綁定在一塊的時候,sidecar在出流量轉發時扮演着反向代理的角色,而在入流量接收的時候,能夠作超過反向代理職責的一些事情。這點咱們會在其餘文章裏討論。
Istio在K8S基礎上實現了服務網格,Isito使用的sidecar容器就是第一節提到的,沒有就緒的容器。因此這個問題,其實就是服務網格內部,全部的sidecar容器都沒有就緒。
上一節咱們看到,istio中的每一個pod,都自帶了反向代理sidecar。咱們遇到的問題是,全部的sidecar都沒有就緒。咱們也看到readiness probe定義的,判斷sidecar容器就緒的方式就是訪問下邊這個接口。
http://<pod ip>:15020/healthz/ready
接下來,咱們深刻看下pod,以及其sidecar的組成及原理。在服務網格里,一個pod內部除了自己處理業務的容器以外,還有istio-proxy這個sidecar容器。正常狀況下,istio-proxy會啓動兩個進程,pilot-agent和envoy。
以下圖,envoy是實際上負責流量管理等功能的代理,從業務容器出、入的數據流,都必需要通過envoy;而pilot-agent負責維護envoy的靜態配置,以及管理envoy的生命週期。這裏的動態配置部分,咱們在下一節會展開來說。
咱們可使用下邊的命令進入pod的istio-proxy容器作進一步排查。這裏的一個小技巧,是咱們能夠以用戶1337,使用特權模式進入istio-proxy容器,如此就可使用iptables等只能在特權模式下運行的命令。
docker exec -ti -u 1337 --privileged <istio-proxy container id> bash
這裏的1337用戶,實際上是sidecar鏡像裏定義的一個同名用戶istio-proxy,默認sidecar容器使用這個用戶。若是咱們在以上命令中,不使用用戶選項u,則特權模式其實是賦予root用戶的,因此咱們在進入容器以後,需切換到root用戶執行特權命令。
進入容器以後,咱們使用netstat命令查看監聽,咱們會發現,監聽readiness probe端口15020的,實際上是pilot-agent進程。
istio-proxy@details-v1-68868454f5-94hzd:/$ netstat -lnpt Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 19/envoy tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 19/envoy tcp 0 0 0.0.0.0:9080 0.0.0.0:* LISTEN - tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
咱們在istio-proxy內部訪問readiness probe接口,同樣會獲得503的錯誤。
瞭解了sidecar的代理,以及管理代理生命週期的pilot-agent進程,咱們能夠稍微思考一下pilot-agent應該怎麼去實現healthz/ready這個接口。顯然,若是這個接口返回OK的話,那不只意味着pilot-agent是就緒的,而必須確保代理是工做的。
實際上pilot-agent就緒檢查接口的實現正是如此。這個接口在收到請求以後,會去調用代理envoy的server_info接口。調用所使用的的IP是localhost。這個很是好理解,由於這是同一個pod內部進程通訊。使用的端口是envoy的proxyAdminPort,即15000。
有了以上的知識準備以後,咱們來看下istio-proxy這個容器的日誌。實際上,在容器日誌裏,一直在重複輸出一個報錯,這句報錯分爲兩部分,其中Envoy proxy is NOT ready這部分是pilot agent在響應healthz/ready接口的時候輸出的信息,即Envoy代理沒有就緒;而剩下的config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected這部分,是pilot-agent經過proxyAdminPort訪問server_info的時候帶回的信息,看起來是envoy沒有辦法從Pilot獲取配置。
Envoy proxy is NOT ready: config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected.
到這裏,建議你們回退看下上一節的插圖,在上一節咱們選擇性的忽略是Pilot到envoy這條虛線,即動態配置。這裏的報錯,其實是envoy從控制面Pilot獲取動態配置失敗。
目前爲止,這個問題其實已經很清楚了。在進一步分析問題以前,我聊一下我對控制面和數據面的理解。控制面數據面模式,能夠說無處不在。咱們這裏舉兩個極端的例子。
第一個例子,是dhcp服務器。咱們都知道,在局域網中的電腦,能夠經過配置dhcp來獲取ip地址,這個例子中,dhcp服務器統一管理,動態分配ip地址給網絡中的電腦,這裏的dhcp服務器就是控制面,而每一個動態獲取ip的電腦就是數據面。
第二個例子,是電影劇本,和電影的演出。劇本能夠認爲是控制面,而電影的演出,包括演員的每一句對白,電影場景佈置等,均可以看作是數據面。
我之因此認爲這是兩個極端,是由於在第一個例子中,控制面僅僅影響了電腦的一個屬性,而第二個例子,控制面幾乎是數據面的一個完整的抽象和拷貝,影響數據面的方方面面。Istio服務網格的控制面是比較靠近第二個例子的狀況,以下圖。
Istio的控制面Pilot使用grpc協議對外暴露接口istio-pilot.istio-system:15010,而envoy沒法從Pilot處獲取動態配置的緣由,是在全部的pod中,集羣dns都沒法使用。
這個問題的緣由其實比較簡單,在sidecar容器istio-proxy裏,envoy不能訪問Pilot的緣由是集羣dns沒法解析istio-pilot.istio-system這個服務名字。在容器裏看到resolv.conf配置的dns服務器是172.19.0.10,這個是集羣默認的kube-dns服務地址。
istio-proxy@details-v1-68868454f5-94hzd:/$ cat /etc/resolv.conf nameserver 172.19.0.10 search default.svc.cluster.local svc.cluster.local cluster.local localdomain
可是客戶刪除重建了kube-dns服務,且沒有指定服務IP,這致使,實際上集羣dns的地址改變了,這也是爲何全部的sidecar都沒法訪問Pilot。
# kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 172.19.9.54 <none> 53/UDP,53/TCP 5d
最後,經過修改kube-dns服務,指定IP地址,sidecar恢復正常。
# kubectl get pods NAME READY STATUS RESTARTS AGE details-v1-68868454f5-94hzd 2/2 Running 0 6d nginx-647d5bf6c5-gfvkm 2/2 Running 0 2d nginx-647d5bf6c5-wvfpd 2/2 Running 0 2d productpage-v1-5cb458d74f-28nlz 2/2 Running 0 6d ratings-v1-76f4c9765f-gjjsc 2/2 Running 0 6d reviews-v1-56f6855586-dplsf 2/2 Running 0 6d reviews-v2-65c9df47f8-zdgbw 2/2 Running 0 6d reviews-v3-6cf47594fd-cvrtf 2/2 Running 0 6d
這實際上是一個比較簡單的問題,排查過程其實也就幾分鐘。可是寫這篇文章,有點感受是在看長安十二時辰,短短几分鐘的排查過程,寫完整背後的原理,來龍去脈,卻花了幾個小時。這是Istio文章的第一篇,但願在你們排查問題的時候,有所幫助。
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。