istio上線一段時間后發現部分高並發的業務會頻繁告警503,於是開始排查:
業務架構:
在我們的環境中istio只攔截inbound的流量,不攔截outbound的流量
1.查看apisix 端的envoy日志發現日志中有大量response_code:503 ,response_flags: UC
2.查看業務容器的envoy 日志並未發現503日志,由此猜測是apisix 到業務容器中間鏈路發生503,請求未到達業務容器
3.在業務容器中tcpdump抓包,發現業務端口大量業務返回的 reset包,waht?業務主動斷開了連接?
於是讓業務梳理了業務場景,檢查了相關代碼邏輯,確定是否有主動關閉tcp連接的操作,業務給出了否定答案,確定沒有相關邏輯,gogle了相關問題,發現有很多人遇到了類似的問題,按照網上的方法設置了一下參數:
1.istio 增加503 retry
2.禁用業務容器envoy的keepalive
3.設置envoy連接池空閑緩存時間(讓envoy主動斷開空閑連接,而不是讓業務容器斷開envoy的長鏈接)
apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: xthk-live-api-pub-vs namespace: prod-main spec: hosts: - xthk-live-api-pub http: - match: - port: 80 retries: attempts: 3 perTryTimeout: 2s retryOn: 'gateway-error,connect-failure,refused-stream,503' route: - destination: host: xthk-live-api-pub port: number: 80 subset: v1 - match: - port: 443 route: - destination: host: xthk-live-api-pub port: number: 443 subset: v1 --- apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: xthk-live-api-pub-dr namespace: prod-main spec: host: xthk-live-api-pub subsets: - labels: version: v1 name: v1 trafficPolicy: connectionPool: http: idleTimeout: 1s maxRequestsPerConnection: 1 tls: mode: ISTIO_MUTUAL
經過一段時間的操作,發現然並卵,錯誤依舊
再次仔細查看envoy日志,發現了異常
upstream_cluster為PassthroughCluster,調用k8s集群內的服務,為什么upstream_cluster不是服務名稱的cluster呢?(當cluster為PassthroughCluster,證明istio沒有通過k8s發現該服務,這樣的話就不會走istio的路由,而是通過k8s的svc轉發,就會多走一層kube-proxy轉發)
為了排查 upstream_cluster為PassthroughCluster的問題,將apisix envoy的日志設置為debug,有兩種設置方式:
1.直接在進入envoy 調用envoy接口配置,這種方式實時生效,且不重啟envoy,envoy重啟后失效 curl -X POST localhost:15000/logging?level=debug
2.設置pod annoations: sidecar.istio.io/logLevel: debug,這種方式pod會重建,但是重啟后debug模式依舊生效
查看envoy 日志:
通過日志我們發現authority 為一個外網域名,即istio向后轉發時使用的host 是一個外網域名而不是集群內的service name,而istio中的VirtualService以及DestinationRule都是靠其中的host字段來匹配的,所以導致istio向pod轉發流量時走的istio的passthroughCluster,而不是對應集群內服務(Outbound Cluster),所以沒有走istio的路由直接轉發給pod(istio的轉發是不經過kube-proxy),而是走了k8s 的service ip,通過kube-proxy轉發到pod,這中間相當於就多了一層轉發,那為什么會造成這個原因呢,看了apisix的配置,發現了問題
apisix 向后轉發時可以配置轉發到后端時主機名的保持,有三種方式:
1.保持與客戶端請求一致的主機名:他的意思是客戶端請求的是什么域名,轉發到k8s pod時 host 就是客戶端請求的域名,如:客戶端請求的是 www.baidu.com,這時候轉發到集群時,hostName 就是 www.baidul.com,就會導致本次訪問的host 與istio vs以及dr規則匹配不到,就會當成不是集群內的服務,會走passthroughCluster,不會走istio的路由,而是通過k8s的kube-proxy最終轉發到pod
2.使用目標節點列表中的主機名或IP,這個選項的意思是無論客戶端通過什么域名來請求,apisix轉發到k8s集群時都是用我們自己配置的主機名,如上游主機我們配置的是k8s的svc name,這時轉發到后端時就會使用k8s的svc name而不是客戶端訪問的域名
3.自定義Host請求頭:這個的意思和第二個選項類似,只不過我們可以自定義向后攜帶的hostName
我把apisix修改成了上述第二個選項使用目標節點列表中的主機名或IP,再次查看apisix 的envoy日志:
這時發現upstream_cluster變成了對一個k8s服務的outbond,說明istio已經識別了目標服務為k8s集群內的服務,這時候就會走istio自己的路由,不走kube-proxy.
一天后查看對應業務的訪問日志
查看了一天的日志,發現503消失
相關參考鏈接:
https://blog.csdn.net/luo15242208310/article/details/96480095
https://zhuanlan.zhihu.com/p/412262801
https://jishuin.proginn.com/p/763bfbd310c3