跳转至

服务网格中常见的 503 报错

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

偶发 503

使用自定义监控指标,每当配置变更时,日志监控发现少量请求出现 503

问题原因

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

解决办法

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

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 偶尔出现,但是在流量密集时,会持续出现。通常出现在边车的 Inbound 侧。

问题原因

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

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

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

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

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

解决办法

方案一:在 DestinationRule 中配置 idleTimeout

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

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

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

方案二:在 VirtualService 中配置重试

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

Important

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

边车生命周期相关

问题原因

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

解决办法

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

必定 503

应用监听 localhost

问题原因

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

解决办法

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

启用边车后,健康检查总是失败,报错 503

问题原因

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

解决办法

您可以通过配置端口健康检查流量免于经过边车代理解决此问题。

评论