乐趣区

关于istio:关于-istioproxy-503504等5xx问题排查

问题形容

在生产环境,咱们最近部署了 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 external
55.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 = -1

for 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/v1alpha3
kind: ServiceEntry
metadata:
  name: ext-service-se
spec:
  hosts:
  - external.service.com
  location: MESH_EXTERNAL
  ports:
  - number: 80
    name: service-http
    protocol: HTTP
  resolution: DNS

VirtualService

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ext-service-vs
spec:
  hosts:
    - external.service.com
http:
- 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 也是非常重要的。

退出移动版