總結使用 istio 常見的10個異常:node
Service 端口命名約束安全
流控規則下發順序問題網絡
請求中斷分析app
sidecar 和 user container 啓動順序負載均衡
Ingress Gateway 和 Service 端口聯動ide
VirtualService 做用域微服務
VirtualService 不支持 host fragmentspa
全鏈路跟蹤並不是徹底透明接入設計
mTLS 致使鏈接中斷3d
istio 支持多平臺,不過 Istio 和 k8s 的兼容性是最優的,不論是設計理念,核心團隊仍是社區, 都有一脈相承的意思。但 istio 和 k8s 的適配並不是徹底沒有衝突, 一個典型問題就是 istio 須要 k8s service 按照協議進行端口命名(port naming)。
端口命名不知足約束而致使的流量異常,是使用 mesh 過程當中最多見的問題,其現象是協議相關的流控規則不生效,這一般能夠經過檢查該 port LDS 中 filter 的類型來定位。
k8s 的網絡對應用層是無感知的,k8s 的主要流量轉發邏輯發生在 node 上,由 iptables/ipvs 來實現,這些規則並不關心應用層裏是什麼協議。
istio 的核心能力是對 7層流量進行管控,但前提條件是 istio 必須知道每一個受管控的服務是什麼協議,istio 會根據端口協議的不一樣,下發不一樣的流控功能(envoy filter),而 k8s 資源定義裏並不包括七層協議信息,因此 istio 須要用戶顯式提供。
協議嗅探概要:
CLIENT_HELLO
提取 SNI、ALPN、NPN 等信息Protocol sniffing 減小了新手使用 istio 所需的配置,可是可能會帶來不肯定的行爲。不肯定的行爲在生產環境中是應該儘可能避免的。
一些嗅探失效的例子:
0x0d 0x0a
), 可是大部分 http 類庫會使用並承認 LF (0x0a
)做爲分隔。建議生產環境不使用協議嗅探, 接入 mesh 的 service 應該按照約定使用協議前綴進行命名。
在批量更新流量規則的過程當中,偶爾會出現流量異常(503),envoy 日誌中 RESPONSE_FLAGS
包含「NR」標誌(No route configured),持續時間不長,會自動恢復。
當用戶使用 kubectl apply -f multiple-virtualservice-destinationrule.yaml
時,這些對象的傳播和生效前後順序是不保證的,所謂最終一致性,好比 VirtualService 中引用了某一個 DestinationRule 定義的子版本,可是這個 DestinationRule 資源的傳播和生效可能在時間上落後於 該 VirtualService 資源。
將更新過程從批量單步拆分爲多步驟,確保整個過程當中不會引用不存在的 subset:
當新增 DestinationRule subset 時,應該先 apply DestinationRule subset,等待 subset 生效後,再 apply 引用了該 subset 的 VirtualService。
當刪除 DestinationRule subset 時,應該先 刪除 VirtualService 中對 該 subset 的引用,等待 VirtualService 的修改生效後,在執行刪除 DestinationRule subset。
請求異常,究竟是 istio 流控規則致使,仍是業務應用的返回,流量斷點出如今哪一個具體的 pod?
這是使用 mesh 最多見的困境,在微服務中引入 envoy 做爲代理後,當流量訪問和預期行爲不符時,用戶很難快速肯定問題是出在哪一個環節。客戶端收到的異常響應,諸如 40三、40四、503 或者鏈接中斷等,多是鏈路中任一 sidecar 執行流量管控的結果, 但也有多是來自某個服務的合理邏輯響應。
Envoy 接受請求流量叫作 Downstream,Envoy 發出請求流量叫作Upstream。在處理Downstream 和 Upstream 過程當中, 分別會涉及2個流量端點,即請求的發起端和接收端:
在這個過程當中, envoy 會根據用戶規則,計算出符合條件的轉發目的主機集合,這個集合叫作 UPSTREAM_CLUSTER, 並根據負載均衡規則,從這個集合中選擇一個 host 做爲流量轉發的接收端點,這個 host 就是 UPSTREAM_HOST。
以上就是 envoy 請求處理的 流量五元組信息, 這是 envoy 日誌裏最重要的部分,經過這個五元組咱們能夠準確的觀測流量「從哪裏來」和「到哪裏去」。
經過日誌重點觀測 2 個信息:
示例一:一次正常的 client-server 請求
能夠看到 2 端日誌包含相同的 request ID,所以能夠將流量分析串聯起來。
示例二:no healthy upstream, 好比目標 deployment 健康副本數爲 0
日誌中 flag「UH」表示 upstream cluster 中沒有健康的 host。
示例三:No route configured , 好比 DestinationRule 缺少對應的 subset
日誌中 flag「NR」表示找不到路由。
示例四,Upstream connection failure,好比服務未正常監聽端口。
日誌中 flag「UF」表示 Upstream 鏈接失敗,據此能夠判斷出流量斷點位置。
Sidecar 模式在kubernetes 世界很流行,但對目前的 k8s (V1.17)來講,並無 sidecar 的概念,sidecar 容器的角色是用戶主觀賦予的。
對 Istio 用戶來講,一個常見的困擾是:sidecar 和用戶容器的啓動順序:
sidecar(envoy) 和用戶容器的啓動順序是不肯定的,若是用戶容器先啓動了,envoy 還未完成啓動,這時候用戶容器往外發送請求,請求仍然會被攔截,發往未啓動的 envoy,請求異常。
在 Pod 終止階段,也會有相似的異常,根源仍然是 sidecar 和普通容器的生命週期的不肯定性。
目前常規的規避方案主要是有這樣幾種:
127.0.0.1:15020/ healthz/ready
不管哪一種方案都顯得很蹩腳,爲了完全解決上述痛點,從 kubernets 1.18版本開始,k8s 內置的 Sidecar 功能將確保 sidecar 在正常業務流程開始以前就啓動並運行,即經過更改pod的啓動生命週期,在init容器完成後啓動sidecar容器,在sidecar容器就緒後啓動業務容器,從啓動流程上保證順序性。而 Pod 終止階段,只有當全部普通容器都已到達終止狀態, 纔會向sidecar 容器發送 SIGTERM 信號。
Ingress Gateway 規則不生效的一個常見緣由是:Gateway 的監聽端口在對應的 k8s Service 上沒有開啓,首先咱們須要理解 Istio Ingress Gateway 和 k8s Service 的關係:
上圖中,雖然 gateway 定義指望管控端口 b 和 c,可是它對應的 service (經過騰訊雲CLB)只開啓了端口 a 和 b,所以最終從 LB 端口 b 進來的流量才能被 istio gateway 管控。
VirtualService 包含了大部分 outbound 端的流量規則,它既能夠應用到網格內部數據面代理中, 也能夠應用到網格邊緣的代理中。
VirtualService 的屬性gateways
用於指定 VirtualService 的生效範圍:
VirtualService.gateways
爲空,則 istio 爲其賦默認值 mesh
, 表明生效範圍爲網格內部gateway-name1,gateway-name2...
mesh
值加入VirtualService.gateways
, 如 mesh,gateway-name1,gateway-name2...
一個常見的問題是以上的第三種狀況,VirtualService 最開始做用於網關內部,後續要將其規則擴展到邊緣網關上,用戶每每只會添加具體 gateway name,而遺漏 mesh
:Istio 自動給VirtualService.gateways
設置默認值, 本意是爲了簡化用戶的配置,可是每每會致使用戶應用不當,一個 feature 一不當心會被用成了 bug。
對某一 host 新增、修改 VirtualService,發現規則始終沒法生效,排查發現存在其餘 VirtualService 也對該 host 應用了其餘規則,規則內容可能不衝突,但仍是可能出現其中一些規則沒法生效的狀況。
目前 istio 對 cross-resource VirtualService 的支持狀況:
VirtualService 不能很好支持 host 規則分片,使得團隊的維護職責不能很好的解耦,配置人員須要知悉目標 host 的全部流控規則,纔有信心去修改 VirtualService。
Istio 計劃在 1.6 中支持 Virtual Service 代理鏈:
微服務接入後 service mesh 後,鏈路跟蹤數據沒有造成串聯。
service mesh 遙測系統中,對調用鏈跟蹤的實現,並不是徹底的零***,須要用戶業務做出少許的修改才能支持,具體地,在用戶發出(http/grpc) RPC 時, 須要主動將上游請求中存在的 B3 trace headers
寫入下游 RPC 請求頭中,這些 headers 包括:
有部分用戶難以理解:既然 inbound 流量和 outbound 流量已經徹底被攔截到 envoy,envoy 能夠實現徹底的流量管控和修改,爲何還須要應用顯示第傳遞 headers?
對於 envoy 來講,inbound 請求和 outbound 請求徹底是獨立的,envoy 沒法感知請求之間的關聯。實際上這些請求到底有無上下級關聯,徹底由應用本身決定。舉一個特殊的業務場景,若是 Pod X 接收到 請求 A,觸發的業務邏輯是:每隔 10 秒 發送一個請求到 Pod Y,如 B1,B2,B3,那麼這些扇出的請求 Bx(x=1,2,3...),和請求 A 是什麼關係?業務可能有不一樣的決策:認爲 A 是 Bx 的父請求,或者認爲 Bx 是獨立的頂層請求。
在開啓 istio mTLS 的用戶場景中,訪問出現 connection termination
是一個高頻的異常:
這個異常的緣由和 DestinationRule 中的 mTLS 配置有關,是 istio 中一個不健壯的接口設計。
connection termination
這種 istio mtls 用戶接口極度不友好,雖然 mtls 默認作到了全局透明, 業務感知不到 mtls 的存在, 可是一旦業務定義了 DestinationRule,就必需要知道當前 mtls 是否開啓,並做出調整。試想 mtls 配置交由安全團隊負責,而業務團隊負責各自的 DestinationRule,團隊間的耦合會很是嚴重。
若是用戶容器中業務進程監聽的地址是具體ip (pod ip),而不是0.0.0.0
, 該用戶容器沒法正常接入 istio,流量路由失敗。這是又一個挑戰 Istio 最大透明化(Maximize Transparency)設計目標 的場景。
Istio-proxy 中的一段 iptables:
其中,ISTIO_IN_REDIRECT 是 virtualInbound, 端口 15006;ISTIO_REDIRECT 是 virtualOutbound,端口 15001。
關鍵點是規則二:若是 destination 不是127.0.0.1/32, 轉給15006 (virtualInbound, envoy監聽),這裏致使了對 pod ip 的流量始終會回到 envoy。
對該規則的解釋:
# Redirect app calls back to itself via Envoy when using the service VIP or endpoint # address, e.g. appN => Envoy (client) => Envoy (server) => appN.
該規則是但願在這裏起做用: 假設當前Pod a屬於service A, Pod 中用戶容器經過服務名訪問服務A, envoy中負載均衡邏輯將此次訪問轉發到了當前的pod ip, istio 但願這種場景服務端仍然有流量管控能力. 如圖示:
建議應用在接入 istio 以前, 調整服務監聽地址,使用 0.0.0.0
而不是具體 IP。若是業務方認爲改造難度大,能夠參考以前分享的一個解決方案:服務監聽pod ip 在istio中路由異常分析