【问题标题】:GKE basic-ingress intermittently returns 502 when backend returns 404/422当后端返回 404/422 时,GKE basic-ingress 间歇性返回 502
【发布时间】:2020-03-04 08:44:56
【问题描述】:

我有一个入口为 GKE 上运行的两个微服务提供路由,当微服务间歇性地返回 404/422 时,入口返回 502。

这是我的入口定义:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: basic-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: develop-static-ip
    ingress.gcp.kubernetes.io/pre-shared-cert: dev-ssl-cert
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: srv
          servicePort: 80
      - path: /c/*
        backend:
          serviceName: collection
          servicePort: 80
      - path: /w/*
        backend:
          serviceName: collection
          servicePort: 80

我运行的测试命中了srv 后端,我期望在该后端得到 404 或 422 响应。当我直接点击srv 后端(绕过入口)时,我已经验证该服务使用 404/422 正确响应。

当我通过入口发出相同的请求时,入口将间歇性地以 502 响应,而不是来自后端的 404/422。

如何让入口只从后端返回 404/422 响应?

这里有一些示例代码来演示我看到的行为(预期状态是 404):

>>> for i in range(10):
        resp = requests.get('https://<server>/a/v0.11/accounts/junk', cookies=<token>)
        print(resp.status_code)

502
502
404
502
502
404
404
502
404
404

这是从 pod 中的 python 提示发出的相同请求,即绕过入口:

>>> for i in range(10):
...     resp = requests.get('http://0.0.0.0/a/v0.11/accounts/junk', cookies=<token>)
...     print(resp.status_code)
...
404
404
404
404
404
404
404
404
404
404

这是 kubectl 命令的输出,用于证明负载均衡器设置正确(我从未收到来自微服务的 2xx/3xx 响应的 502):

$ kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP          NODE                                     NOMINATED NODE   READINESS GATES
srv-799976fbcb-4dxs7          2/2     Running   0          19m   10.24.3.8   gke-develop-default-pool-ea507abc-43h7   <none>           <none>
srv-799976fbcb-5lh9m          2/2     Running   0          19m   10.24.1.7   gke-develop-default-pool-ea507abc-q0j3   <none>           <none>
srv-799976fbcb-5zvmv          2/2     Running   0          19m   10.24.2.9   gke-develop-default-pool-ea507abc-jjzg   <none>           <none>
collection-5d9f8586d8-4zngz   2/2     Running   0          19m   10.24.1.6   gke-develop-default-pool-ea507abc-q0j3   <none>           <none>
collection-5d9f8586d8-cxvgb   2/2     Running   0          19m   10.24.2.7   gke-develop-default-pool-ea507abc-jjzg   <none>           <none>
collection-5d9f8586d8-tzwjc   2/2     Running   0          19m   10.24.2.8   gke-develop-default-pool-ea507abc-jjzg   <none>           <none>
parser-7df86f57bb-9qzpn       1/1     Running   0          19m   10.24.0.8   gke-develop-parser-pool-5931b06f-6mcq    <none>           <none>
parser-7df86f57bb-g6d4q       1/1     Running   0          19m   10.24.5.5   gke-develop-parser-pool-5931b06f-9xd5    <none>           <none>
parser-7df86f57bb-jchjv       1/1     Running   0          19m   10.24.0.9   gke-develop-parser-pool-5931b06f-6mcq    <none>           <none>

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
srv          NodePort    10.0.2.110   <none>        80:30141/TCP   129d
collection   NodePort    10.0.4.237   <none>        80:30270/TCP   129d
kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP        130d

$ kubectl get endpoints
NAME         ENDPOINTS                                AGE
srv          10.24.1.7:80,10.24.2.9:80,10.24.3.8:80   129d
collection   10.24.1.6:80,10.24.2.7:80,10.24.2.8:80   129d
kubernetes   35.237.239.186:443                       130d

【问题讨论】:

  • 当您收到 502 http 错误响应时,您是否检查过堆栈驱动程序日志中的 502?根据堆栈驱动程序,这个 502 的原因是什么?此外,您提到您间歇性地收到 502,我很想知道当它不是 502 时您会收到什么其他响应代码。
  • stackdriver 日志没有与这些请求对应的条目。当我没有得到 502 时,我会得到预期的 404(或 422)。我添加了一个我看到的行为示例。

标签: kubernetes google-cloud-platform google-kubernetes-engine kubernetes-ingress http-response-codes


【解决方案1】:

tl;dr:如果来自后端的 404/422 没有响应正文,则 GCP LoadBalancer/GKE Ingress 将为 502。

查看 LoadBalancer 日志,我会看到以下错误:

502: backend_connection_closed_before_data_sent_to_client
404: backend_connection_closed_after_partial_response_sent

由于一切都配置正确(甚至 LoadBalancer 都说后端是健康的)——后端按预期工作并且没有失败的健康检查——我尝试了一些东西,发现我所有的 404 响应都是空的。

太好了,我在我的 404 和 422 响应中添加了一个正文,你瞧,再也没有 502 了!

【讨论】:

    【解决方案2】:

    502 是一个棘手的状态代码,它可能意味着客户端取消了上下文,或者只是来自您尝试访问的服务器的错误网关。在 kubernetes 中,502 通常意味着您无法访问该服务。因此,我会去调试您的服务和部署doc

    使用kubectl get pods -o wide 获取您的srv pod;检查其clusterIP IP。然后确保服务正在对srv 部署进行负载平衡。为此,请运行kubectl get svc 并查找srv 服务。最后运行kubectl get endpoints,获取分配给srv 端点的IP,并将其与您从Pod 获得的IP 匹配。如果一切正常,那么您正确地对后端进行了负载平衡。

    【讨论】:

    • 来自 srv 部署的 2xx/3xx 响应都按预期通过入口。该行为似乎与 404/422 响应无关。我添加了一个示例运行以显示间歇性 502,并添加了 kubectl 命令的输出。据我所知,一切都设置正确。
    • Intermittents 502s 我以前在 stackoverflow 和 medium 帖子中看到过这个。您可以尝试在吊舱内做同样的事情吗?不要使用该服务。这样我们就可以判断是部署还是服务。
    • 添加了在 pod 内运行相同请求的输出,响应都是 404 和预期的一样。我尝试在 SO 和 medium 中找到类似的东西,但我发现的所有内容都对应于后端无响应的问题,这显然不是。
    【解决方案3】:

    502 errors are expected when your backend service is returning 4xx errors。如果后端返回 4xx,则健康检查将失败。如果所有后端都出现故障,负载均衡器将没有可用的后端来发送流量并返回 502。

    对于从负载均衡器返回的任何 502 错误,我强烈建议检查 HTTP 负载均衡器的堆栈驱动程序日志。任何 502 错误都将包含消息输出以及 502 响应。该消息应说明为什么 502 被重新发送(原因有很多)。

    在您当前的情况下,502 error log should mention "failed_to_pick_backend" 或 "failed_to_connect_to_backend" 就是这样。如果您使用的是 nginx ingress,则可以看到类似的行为,但 502 错误消息可能会有所不同。

    【讨论】:

    • 负载均衡器日志中有 502 的代码:backend_connection_closed_before_data_sent_to_client。这种行为对我来说没有意义。如果客户端请求的 URL 不存在,后端应该返回 404。
    • 你是绝对正确的,这个错误对于 404 错误毫无意义。该错误通常是由于 LB 和后端的 TTL 不匹配造成的
    • 决定也看看从 404 中返回的内容:backend_connection_closed_after_partial_response_sent。原来 LB 不喜欢空的响应体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2017-08-15
    • 2021-11-06
    相关资源
    最近更新 更多