• 网络问题排查
    • 请求被 Envoy 拒绝
    • 路由规则好像没有生效
    • 设置目标规则后出现 503 错误
    • 路由规则在 Ingress Gateway 请求中无效
    • Headless TCP 服务连接丢失
    • Envoy 在高压情况下会崩溃
    • Envoy 无法连接 HTTP/1.0 服务

    网络问题排查

    本节将介绍一些常用的工具和技能,用于解决流量管理方面的问题。

    请求被 Envoy 拒绝

    要了解拒绝请求的原因,最佳方式是检查 Envoy 的访问日志。默认情况下,访问日志输出到容器的标准输出。运行以下命令以查看日志:

    1. $ kubectl logs -it PODNAME -c istio-proxy -n NAMESPACE

    在默认访问日志格式中,Envoy响应标志和 Mixer 策略状态位于响应代码之后,如果您使用的是自定义日志格式,请确保包含%RESPONSE_FLAGS%%DYNAMIC_METADATA(istio.mixer:status)%

    详细的响应标志请参考 Envoy 响应标志

    常见的响应标志是:

    • NR: 没有配置路由, 检查你的 DestinationRuleVirtualService
    • UO: 上游溢出,熔断, 在 DestinationRule 中检查你的熔断器配置。
    • UF: 无法连接到上游, 如果你正在使用Istio身份验证,请检查双向 TLS 配置冲突。如果响应标志为 UAEX 且 Mixer 策略状态不是 -,则 Mixer 拒绝请求。

    通用 Mixer 策略状态为:

    • UNAVAILABLE: Envoy 无法连接到 Mixer,策略配置为关闭失败。
    • UNAUTHENTICATED: Mixer 身份验证拒绝该请求。
    • PERMISSION_DENIED: Mixer 授权拒绝该请求。
    • RESOURCE_EXHAUSTED: Mixer 配额拒绝该请求。
    • INTERNAL: 由于 Mixer 内部错误,请求被拒绝。

    路由规则好像没有生效

    在当前版本的 Envoy Sidecar 实现中,可能要 100 个请求才能观察到有权重版本的路由分发过程。

    如果一组路由规则能够完美的和 Bookinfo 配合,但是类似的版本路由功能在其它应用上无法生效,一个可能的解决方法就是对 Kubernetes Service 进行一点改动。Kubernetes Service 的定义必须符合一定规范,才能享受到 Istio 的七层路由特性。应该根据 Istio 对 Pod 和服务的要求对服务进行调整。

    还有个潜在问题就是路由的生效速度很慢。Istio 在 Kubernetes 上实现了一种最终一致性算法,这个算法用于保障所有 Envoy Sidecar 都能够获得正确的配置信息,其中就包含所有的路由规则。配置的变化需要一些时间来传递给所有的 Sidecar。在部署规模很大的情况下,这一传播过程会更长,可能会有数秒的延迟。

    设置目标规则后出现 503 错误

    在应用一个 DestinationRule 之后,如果对服务的请求突然开始生成 HTTP 503 错误,并且该错误会一直持续到删除或回滚 DestinationRule 为止,那么这个 DestinationRule 可能引起了服务的 TLS 冲突。

    举个例子来说,如果在集群上启用了全局的双向 TLS,那么 DestinationRule 必须包含下面的 trafficPolicy 定义:

    1. trafficPolicy:
    2. tls:
    3. mode: ISTIO_MUTUAL

    这一模式的缺省值是 DISABLE,会导致客户端 Sidecar 使用明文 HTTP 请求,而不是 TLS 加密请求;而服务端却又要求接收加密请求,因此就产生了冲突。

    可以执行 istioctl authn tls-check 命令来检查这一问题,查看该命令的返回内容中的 STATUS 字段是否为 CONFLICT,例如:

    1. $ istioctl authn tls-check httpbin.default.svc.cluster.local
    2. HOST:PORT STATUS SERVER CLIENT AUTHN POLICY DESTINATION RULE
    3. httpbin.default.svc.cluster.local:8000 CONFLICT mTLS HTTP default/ httpbin/default

    不论何时,在应用 DestinationRule 时都应该确认 trafficPolicy TLS 模式是否符合全局设置的要求。

    路由规则在 Ingress Gateway 请求中无效

    假设要使用 Ingress Gateway,结合 VirtualService 来访问一个内部服务。VirtualService 看起来大概是这样:

    1. apiVersion: networking.istio.io/v1alpha3
    2. kind: VirtualService
    3. metadata:
    4. name: myapp
    5. spec:
    6. hosts:
    7. - "myapp.com" # 如果在没有 DNS 的确情况下进行测试,使用 IP 来访问网关,也可以使用“*”
    8. gateways:
    9. - myapp-gateway
    10. http:
    11. - match:
    12. - uri:
    13. prefix: /hello
    14. route:
    15. - destination:
    16. host: helloworld.default.svc.cluster.local
    17. - match:
    18. ...

    另外还有一个 VirtualService ,会把发往 helloworld 服务的请求路由到某个 subset

    1. apiVersion: networking.istio.io/v1alpha3
    2. kind: VirtualService
    3. metadata:
    4. name: helloworld
    5. spec:
    6. hosts:
    7. - helloworld.default.svc.cluster.local
    8. http:
    9. - route:
    10. - destination:
    11. host: helloworld.default.svc.cluster.local
    12. subset: v1

    这种情况下会注意到,通过 Ingress 网关发出的对 helloworld 服务的请求并没有被重定向到 v1,而是继续使用缺省的轮询路由。

    Ingress 请求使用的是网关定义的主机(例如 myapp.com)进行访问的,因此会使用 myapp 这个 VirtualService,结果就是路由到 helloworld 服务的任意端点;只有在网格内部发往主机 helloworld.default.svc.cluster.local 的请求才会使用 helloworld 这个 VirtualService,这个配置的目的就是将流量分配到 v1

    要控制来自网关的流量,就需要把引流到 subset 的配置写入到 myapp 中:

    1. apiVersion: networking.istio.io/v1alpha3
    2. kind: VirtualService
    3. metadata:
    4. name: myapp
    5. spec:
    6. hosts:
    7. - "myapp.com" # 如果在没有 DNS 的确情况下进行测试,使用 IP 来访问网关,也可以使用“*” (例如 http://1.2.3.4/hello)
    8. gateways:
    9. - myapp-gateway
    10. http:
    11. - match:
    12. - uri:
    13. prefix: /hello
    14. route:
    15. - destination:
    16. host: helloworld.default.svc.cluster.local
    17. subset: v1
    18. - match:
    19. ...

    还有一个方式就是把两个 VirtualService 合而为一:

    1. apiVersion: networking.istio.io/v1alpha3
    2. kind: VirtualService
    3. metadata:
    4. name: myapp
    5. spec:
    6. hosts:
    7. - myapp.com # 因为有了网格内的服务主机的定义,这里就不能用 * 了
    8. - helloworld.default.svc.cluster.local
    9. gateways:
    10. - mesh # 对内部网关也生效
    11. - myapp-gateway
    12. http:
    13. - match:
    14. - uri:
    15. prefix: /hello
    16. gateways:
    17. - myapp-gateway # 限制只对 Ingress 网关的流量有效
    18. route:
    19. - destination:
    20. host: helloworld.default.svc.cluster.local
    21. subset: v1
    22. - match:
    23. - gateways:
    24. - mesh # 对所有网格内服务有效
    25. route:
    26. - destination:
    27. host: helloworld.default.svc.cluster.local
    28. subset: v1

    Headless TCP 服务连接丢失

    如果部署了 istio-citadel,Envoy 会每隔 15 分钟重启一次,来完成证书的刷新任务。这就造成了服务间的长连接以及 TCP 流的中断。

    建议提高应用的适应能力,来应对这种断流情况。如果要阻止这种情况的发生,就要禁止双向 TLS 和 istio-citadel 的部署。

    首先编辑 istio 配置来禁止双向 TLS:

    1. $ kubectl edit configmap -n istio-system istio
    2. $ kubectl delete pods -n istio-system -l istio=pilot

    接下来对 istio-citadel 进行缩容,来阻止 Envoy 的重启:

    1. $ kubectl scale --replicas=0 deploy/istio-citadel -n istio-system

    这样应该就能阻止 Istio 重启 Envoy,从而防止 TCP 连接的中断。

    Envoy 在高压情况下会崩溃

    检查一下 ulimit -a,很多系统缺省设置打开文件描述符数量上限为 1024,会导致 Envoy 断言失败引发崩溃:

    1. [2017-05-17 03:00:52.735][14236][critical][assert] assert failure: fd_ != -1: external/envoy/source/common/network/connection_impl.cc:58

    确认提高 ulimit 上限,例如 ulimit -n 16384

    Envoy 无法连接 HTTP/1.0 服务

    Envoy 要求上游服务提供 HTTP/1.1 或者 HTTP/2。例如当使用 NGINX 在 Envoy 之后提供服务时,就需要在 NGINX 配置文件中设置 proxy_http_version1.1,否则就会使用缺省值 1.0

    配置样例:

    1. upstream http_backend {
    2. server 127.0.0.1:8080;
    3. keepalive 16;
    4. }
    5. server {
    6. ...
    7. location /http/ {
    8. proxy_pass http://http_backend;
    9. proxy_http_version 1.1;
    10. proxy_set_header Connection "";
    11. ...
    12. }
    13. }