服务网格中常见的503报错

本文介绍服务网格中常见的503报错场景及解决方案。

偶发503

场景一:使用自定义监控指标功能进行个性化配置,每当配置变更时,日志监控发现少量请求出现503

问题原因

自定义指标功能的逻辑是通过生成一个对应的EnvoyFiter来进行istio.stats的配置更新。该配置在Envoy Listener级别生效,即通过LDS同步生效。Envoy在应用Listener级别的配置时,需要断开已有连接。对应的在途请求因为连接被Reset或Close导致出现503。

解决方案

上游Server主动Close连接时,您看到的503并非上游Server发送,而是客户端Sidecar因为上游连接主动断开,由本地返回的响应。

Istio默认的重试配置中未包含“上游Server主动Close连接”的情况。EnvoyProxy的重试条件中,Reset符合这种情况对应的触发条件。因此,您需要为对应服务的路由配置retry Policy。在VirtualService下的retry Policy配置包含Reset的触发条件。

配置示例如下。该配置仅针对Ratings服务生效。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: ratings-route
spec:
  hosts:
  - ratings.prod.svc.cluster.local
  http:
  - route:
    - destination:
        host: ratings.prod.svc.cluster.local
        subset: v1
    retries:
      attempts: 2
      retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes,reset,503

相关FAQ

为什么Istio默认的重试机制未生效?

Istio默认重试发生的条件如下。默认会重试2次。该场景未在重试条件内,因此未生效。

           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts"
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [
             503
            ]
           }
  • connect-failure:连接失败。

  • refused-stream:当HTTP2 Stream流返回REFUSED_STREAM错误码

  • unavailable:当gRPC请求返回unavailable错误码。

  • cancelled:当gRPC请求返回cancelled错误码。

  • retriable-status-codes:当请求返回的status_coderetriable_status_codes配置下定义的错误码匹配。

关于最新版本的EnvoyProxy完整的重试条件,请参见以下文档。

场景二:偶发503,没有规律,在此过程中并未发生配置变更

503偶尔出现,但是在流量密集时,会持续出现。通常出现在Sidecar的Inbound侧。

问题原因

Envoy的空闲连接保持时间和应用不匹配。Envoy空闲连接时间默认为1小时。

  • Envoy空闲连接时间过长,应用相对较短:

    应用已经结束空闲连接,但是Envoy认为没有结束。此时如果有新的连接,就会报503UC。Dingtalk_20230510151540..png

  • Envoy空闲连接时间过短,应用相对较长:

    这种情况不会导致503。Envoy认为之前的连接已经被关闭,因此会直接新建一个连接。Dingtalk_20230510151639..png

解决方案

方案一:在DestinationRule中配置idleTimeout

造成该问题的原因就是idleTimeout不匹配,因此在DestinationRule中配置idleTimeout属于比较根本的解决办法。

如果配置了idelTimeout,在Outbound和Inbound两侧都会生效,即Outbound和Inbound的Sidecar都会存在idleTimeout的配置。如果客户端没有Sidecar,idleTimeout也会生效,并能够有效减少503。

配置建议:此配置与业务相关,太短会导致连接数过高。建议您配置为略短于业务应用真正的idleTimeout时间。

方案二:在VirtualService中配置重试

重试会重建连接,可以解决此问题。具体操作,请参见场景一的解决方案。

重要

该操作为非幂等的请求,重试存在较大的风险,请谨慎操作。

场景三:Sidecar生命周期相关

问题原因

Sidecar和业务容器生命周期导致,常发生于Pod重启。

解决方案

具体操作,请参见Sidecar生命周期

必定503

场景一:应用监听localhost

问题原因

当集群中的应用监听localhost网络地址时,如果localhost是本地地址,会导致集群中的其他Pod无法对其进行正常访问。

解决方案

您可以通过对外暴露应用服务解决此问题。具体操作,请参见如何使集群中监听localhost的应用被其他Pod访问

场景二:启用Sidecar后,健康检查总是失败,报错503

问题原因

服务网格开启mTLS后,kubelet向Pod发送的健康检查请求被Sidecar拦截,而kubelet没有对应的TLS证书,导致健康检查失败。

解决方案

您可以通过配置端口健康检查流量免于经过Sidecar代理解决此问题。具体操作,请参见为什么注入Sidecar后,健康检查总失败或者无效