问题形容

在生产环境,咱们最近部署了Istio Service Mesh,Istio管制立体会在每个服务Pod里主动注入一个sidecar。当各个服务都初始化istio-proxy,通过sidecar去实现服务间的调用时,利用和服务就会面临一个很广泛的问题:upstream 服务调用收到HTTP 503/504 response,这个报错信息通常都是由istio-proxy产生的。HTTP 503通常产生在利用和istio-proxy的inbound或者outbound调用,或者是服务网格内部的服务调用。影响面绝对重大的是通过网格拜访内部服务的outbound流量,绝对小的是网格外部的inbound流量。HTTP 504的问题绝对要少一些,只有在upstream service的接口response工夫超过15秒时才会产生,因为istio-proxy的默认response超时工夫就是15秒。

这个问题的景象跟实在的upstream组件失败谬误很类似,然而,通过景象剖析也有可能是istio自身的默认配置对于网格外部的利用不是那么强壮而问题引起的,对于那些影响面比拟大的outbound服务调用:在某一个工夫周期内,会有大量的503产生(通常访问量越大报错越多),过一段时间后本人会自愈,周而复始。

影响小一些的inbound调用,在利用监控面板上看不到报错信息,只有在istio-proxy的log里会发现一些503的痕迹,因为咱们通常会在客户端或者上游调用时配置重试规定,大部分异样调用都会在重试后胜利拿到数据。

对于outbound调用,504 response timeout谬误只会对组件依赖超过15秒的申请造成影响。
能够察看一下istio-proxy日志里的HTTP 503/504 upstream谬误,进一步的对于日志格局和response flags的信息能够点击链接查看。envoy log

在具体探索这个谬误的具体起因之前,咱们先看一些谬误一日信息的示例:
Inbound Call Error example

上面是一个istio-proxy日志里达到申请的response flag “UC”异样日志,含意是:“Upstream connection termination in addition to 503 response code”,因为某种原因在同一个pod里的istio-proxy和他的upstream容器之间的链接被断开了。

还有另外一种response flag “UF”异样日志, 含意是:“Upstream connection failure in addition to 503 response code”,因为某种原因使同一个pod内的istio-proxy和他的upstream容器没方法建设链接

[2022-04-18T09:29:59.633Z] "GET /service_a/v2.1/get?ids=123456 HTTP/1.1" 503 UC "-" "-" 0 95 11 - "-" "-" "bc6f9ba3-d8dc-4955-a092-64617d9277c8" "srv-a.xyz" "127.0.0.1:8080" inbound|80|http|srv-a.xyz.svc.cluster.local - 100.64.121.11:8080 100.64.254.9:35174 -[2022-04-18T08:38:43.976Z] "GET /service_b/v2.5/get?getIds=123456 HTTP/1.1" 503 UF "-" "-" 0 91 0 - "-" "-" "d0dbd0fe-90db-46d0-b614-7eaea3764f79" "srv-b.xyz.svc.cluster.local" "127.0.0.1:8080" inbound|80|http|srv-b.xyz.svc.cluster.local - 100.65.130.7:8080 100.64.187.3:53378 -

Outbound Call Error example

上面是一条拜访内部服务时记录在istio-proxy日志里response flag “UF”的错误信息,HTTP状态码为503,含意是:“Upstream connection failure in addition to 503 response code“,这个谬误跟下面一条有些相似,是因为某些起因没方法同内部host建设连贯。

[2022-04-18T17:27:02.903Z] "GET /External.WebService/v2/Ids HTTP/1.1" 503 UF "-" "-" 0 91 0 - "-" "Apache-HttpClient/4.5.5 (Java/11)" "c75a750d-9efd-49e7-a419-6ed97298b543" "external.service.com" "55.92.142.17:80" PassthroughCluster - 55.92.142.17:80 100.65.254.14:39140 -

接下来在看一下因为upstream response timeout引起的response flag “UT”HTTP 504的异样问题,“Upstream request timeout in addition to 504 response code“,这时istio-proxy 期待了15000毫秒,直到连贯断开。

[2022-04-18T07:18:17.609Z] "POST /ext_svc/1111000222333/add?currency=GBP&locale=en_GB HTTP/1.1" 504 UT "-" "-" 150 24 15004 - "-" "Jersey/2.27 (HttpUrlConnection 11.0.4)" "7e9a9207-690f-45b1-b574-fc806b117a84" "testservice.prod.com" "10.186.199.78:80" PassthroughCluster - 10.186.199.78:80 100.65.161.7:37302 -[2022-04-18T23:05:06.499Z] "GET /api/1234567 HTTP/1.1" 504 UT "-" "-" 0 24 15001 - "-" "ABC.3.1.449" "4403f657-f324-4c60-befd-343a23e9520a" "apisvc.prod.com" "10.186.199.78:80" PassthroughCluster - 10.186.199.78:80 100.66.91.11:50288 -

问题产生的起因

Inbound 调用

所有利用的inbound调用都会被istio-proxy通过iptables规定劫持,无论是inbound或者是outbound流量,istio-proxy都会通过localhost的15001端口同利用容器建设一个新的链接。因为istio-proxy提供的是L7代理,须要通过在集群内各个pod间接维持的长链接实现申请负载平衡。问题就来了,因为在K8S外部默认的TCP L4链接负载平衡的限度,应用服务器例如Tomcat和Finagle在回收利用链接时都有maxKeepAliveRequests和MaxLifeTime的参数设置,链接维持一段时间之后都会在服务了肯定数量的申请或者通过一个固定工夫周期后被回收。

在下图中,红色箭头示意的链接被应用服务器终止了,然而istio-proxy依然在为另一个申请复用这个链接,此时这个upstream链接就会收到503 error code (response flag UC),这个问题在高并发场景就绝对比拟常见。

inbound申请还有另外一种503谬误,response flag为 UF (upstream connection failure),这通常是因为利用容器本人crash掉了引起的。

outbound调用

在规范的istio网格环境里,一个申请通过istio-proxy申请一个内部服务,如果没有为这个内部服务设置具体的Istio配置(例如ServiceEntry),那么就不会有路由或者流量治理规定能够被应用,它就是一个简略的维持在指标服务之间的HTTP1.1协定的TCP链接。正在初始化outbound申请的利用在来到本地网络接口之前须要解析内部域名的DNS协定,IPtable规定劫持这个申请并转发到istio-proxy,而后istio-proxy会敞开这个链接并开启一个执行远端主机IP的新链接。

istio-proxy不会被动敞开这个长链接,除非启动该链接的利用想要被动敞开它。因而,该利用发动的新申请不会再进行DNS解析,因为从利用视角来看曾经建设了链接。随着工夫流逝,istio-proxy将会返回一个503谬误。

具体起因:
1、通过平安组件拜访内部服务
如果通过平安组件(egress防火墙)实现外部网络到内部服务的网络调用,当一个DNS申请到来时将会做以下两件事:(1)同白名单列表进行比对,查看这个内部服务是否被容许拜访。(2)如果被容许拜访,平安组件将会把模板IP增加到IPSET,默认过期工夫是3600秒,防火墙host IPSET样例如下:

[ec2-user@egress-proxy-4 ~]$ ipset list | grep external55.92.142.17,tcp:80 timeout 3697 comment "external.service.com."55.92.142.17,tcp:443 timeout 3697 comment "external.service.com."

一单上述规定超期,对于那些istio-proxy 应用雷同IP发动而没有DNS解析的新申请将收到如下谬误:“upstream connect error or disconnect/reset before headers. reset reason: connection failureHTTP/1.1 503” 。这个起因引发的异样,如果有其余利用拜访雷同的内部服务,而且恰好是第一次发动申请,IPSET规定将会被更新,以后利用的503异样将会自愈。

1、内部服务的Host是云服务ELB提供的动静IP
这种状况,503谬误是比拟常见地,因为Host IP不会比集群内组件部署更频繁的发生变化。然而当ELB更新时,istio-proxy应用长链接拜访内部IP时仍然会产生503谬误。

可能的解决方案

inbound调用

在现实情况下,同一pod内的istio-proxy应该可能捕捉到上游应用服务器的上游链接断开事件。然而在istio后续版本设计解决这个问题之前,有一个简略的变通办法能够解决这个问题:对于inbound调用,针对链接,设置一个有限的申请管道,只须要超时工夫用来解决链接回收。

for Tomcat app server   maxKeepAliveRequests = -1for Finagle app server   # Don't set any value, then it defaults to unbounded.    maxLifeTime(timeout: Duration)

在K8S集群外部,集群内的申请是通过ServiceIp实现的,ServiceIp是通过L3/L4实现的,仅仅实现了链接的负载平衡。因而,它须要在服务肯定数量的申请之后敞开这些链接,以便正确的、平衡的散发申请到后端多个实例。只管频繁的敞开链接会影响提早,然而与跨实例的申请负载不平衡相比,这是十分必要的,因为这将引起某些实例负载过高。通过Istio,不须要回收链接,因为istio-proxy工作在7层,通过长链接来实现的申请负载平衡,只有闲暇工夫超时之后才会去敞开链接,客户端和服务端都须要实现闲暇链接的清理工作。

outbound调用

当初咱们晓得了应用istio-proxy outbound调用产生503的谬误起因,须要为内部服务主机周期性的进行DNS解析。上面这些选项可能会对解决问题提供帮忙
(1)outbound调动尽量不要应用长链接-- 尽管能解决问题,然而如果服务的QPS很高,这将影响扩展性。
(2)定期进行DNS解析-- 批改利用代码去定期敞开链接并从新开发一个链接以便更新DNS解析,能够解决问题,然而须要批改代码。
(3)配置istio定期进行DNS解析--这是最好的解决方案,不须要批改利用代码而且还部署简略。istio流量规定容许为一个内部申请配置一个 ServiceEntry 和VirtualService,通过配置这些规定能够防止503问题。
(4)配置istio减少超时工夫--为了防止504response超时问题和适应上游服务调用的response 超时工夫长于15秒,配置VirtualService的超时工夫长于15秒。

如下是ServiceEntry 和VirtualService的样例,定期进行DNS解析和设置response timeout为30秒:

ServiceEntry

apiVersion: networking.istio.io/v1alpha3kind: ServiceEntrymetadata:  name: ext-service-sespec:  hosts:  - external.service.com  location: MESH_EXTERNAL  ports:  - number: 80    name: service-http    protocol: HTTP  resolution: DNS

VirtualService

apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: ext-service-vsspec:  hosts:    - external.service.comhttp:- timeout: 30s  route:    - destination:        host: external.service.com      weight: 100

上述ServiceEntry 默认300秒刷新一次DNS解析,能够通过istio-proxy配置看到如下配置信息:

"dns_refresh_rate": "300s",      "dns_lookup_family": "V4_ONLY",      "load_assignment": {       "cluster_name": "outbound|80||external.service.com",       "endpoints": [        {         "lb_endpoints": [          {           "endpoint": {            "address": {             "socket_address": {              "address": "external.service.com",              "port_value": 80             }            }           },           "load_balancing_weight": 1      

能够通过“global.proxy.dnsRefreshRate”参数批改默认DNS刷新频率,然而须要留神的是这是一个全局配置,一旦批改将会影响到整个网格,肯定要审慎批改。

总结

利用接入istio-proxy边车,一个5xx问题意味着有某些起因导致了服务调用呈现问题,须要认真的通过利用监控、上游服务监控、istio-proxy日志和istio配置仔细分析和定位问题。正当的设置ServiceEntry 和VirtualService,服务端的keep-alive链接配置,对于解决http 503和504也是非常重要的。