解决 Cloud Service Mesh 中的工作负载启动问题

本文档介绍了常见的 Cloud Service Mesh 问题以及如何解决这些问题。如果您需要其他帮助,请参阅获取支持

在公开特权端口的情况下,网关无法使用无发行版代理启动

默认情况下,无发行版代理会以非 root 权限启动,这在某些情况下可能会导致特权端口绑定失败。如果您在代理启动期间看到类似于以下内容的错误,则需要为网关部署应用额外的 securityContext。

  Error adding/updating listener(s) 0.0.0.0_80: cannot bind '0.0.0.0:80': Permission denied

以下示例是出站网关部署的 yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-egressgateway
spec:
  selector:
    matchLabels:
      app: istio-egressgateway
      istio: egressgateway
  template:
    metadata:
      annotations:
        # This is required to tell Anthos Service Mesh to inject the gateway with the
        # required configuration.
        inject.istio.io/templates: gateway
      labels:
        app: istio-egressgateway
        istio: egressgateway
    spec:
      containers:
      - name: istio-proxy
        image: auto # The image will automatically update each time the pod starts.
        resources:
          limits:
            cpu: 2000m
            memory: 1024Mi
          requests:
            cpu: 100m
            memory: 128Mi
      # Allow binding to all ports (such as 80 and 443)
      securityContext:
        sysctls:
        - name: net.ipv4.ip_unprivileged_port_start
          value: "0"
      serviceAccountName: istio-egressgateway 

访问 Cloud Service Mesh 端点时连接被拒

从集群到端点的通信时,您可能会遇到间歇性连接遭拒 (ECONNREFUSED) 错误,例如 Memorystore Redis、Cloud SQL 或应用工作负载需要访问的任何外部服务。

当应用工作负载启动速度快于 istio-proxy (Envoy) 容器并尝试访问外部端点时,可能会发生这种情况。由于此阶段 istio-init (initContainer) 已执行,因此存在的 iptables 规则会将所有传出流量重定向到 Envoy。由于 istio-proxy 尚未准备就绪,iptables 规则会将流量重定向到尚未启动的边车代理并因此应用会收到 ECONNREFUSED 错误。

以下步骤详细介绍了如何检查是否遇到此错误:

  1. 使用以下过滤条件检查 Stackdriver 日志,以识别出现问题的 pod。

    以下示例展示了典型的错误消息:

    Error: failed to create connection to feature-store redis, err=dial tcp   192.168.9.16:19209: connect: connection refused
    [ioredis] Unhandled error event: Error: connect ECONNREFUSED
    
  2. 搜索问题发生实例。如果您使用的是旧版 Stackdriver,请使用 resource.type="container"

    resource.type="k8s_container"
    textPayload:"$ERROR_MESSAGE$"
    
  3. 展开最近的发生实例以获取 pod 的名称,然后记下 resource.labels 下的 pod_name

  4. 获取该 pod 的第一次问题:

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    输出示例:

    E 2020-03-31T10:41:15.552128897Z
    post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
    connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
    connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb
    
  5. 记下此 pod 的第一个错误的时间戳。

  6. 使用以下过滤条件查看 pod 启动事件。

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    输出示例:

    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Container image "docker.io/istio/proxyv2:1.3.3" already present on machine  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Created container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Started container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{APP-CONTAINER-NAME} Created container  spec.containers{APP-CONTAINER-NAME}
    W 2020-03-31T10:41:17Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:26Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:28Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:31Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:58Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    
  7. 使用错误的时间戳和 istio-proxy 启动事件,以确认 Envoy 未准备就绪时发生的错误。

    如果 istio-proxy 容器尚未准备就绪时发生错误,则获取连接遭拒错误是正常的。在前面的示例中,pod 一旦 2020-03-31T10:41:15.552128897Z 就尝试连接到 Redis,但 2020-03-31T10:41:58Z istio-proxy 仍未通过就绪性探测。

    即使 istio-proxy 容器首先启动,它也可能在应用尝试连接到外部端点之前尚未足够快。

    如果您遇到此问题,请继续执行以下问题排查步骤。

  8. 在 pod 级层为配置添加注释。这仅在 Pod 级层提供,在全局级层不可用。

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. 修改应用代码,使其在尝试向外部服务发出任何其他请求之前检查 Envoy 是否已准备就绪。例如,在应用启动时,启动向 istio-proxy 运行状况端点发出请求的循环,并且仅在获得 200 时继续。istio-proxy 运行状况端点如下所示:

    http://localhost:15020/healthz/ready
    

Vault 和 Cloud Service Mesh 之间的边车注入期间的竞争条件

使用 vault 进行 Secret 管理时,有时 vault 会在 istio 之前注入边车,从而导致 Pod 卡在 Init 状态。发生这种情况时,创建的 Pod 会在重启任何部署或部署新部署后卡在 Init 状态。例如:

E 2020-03-31T10:41:15.552128897Z
post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb

此问题是由竞态条件引起的,Istio 和 vault 都会注入边车,Istio 必须是最后执行此操作,istio 代理在 init 容器期间未运行。istio init 容器设置 iptables 规则以将所有流量重定向到代理。由于这些规则尚未运行,因此这些规则不会重定向到任何规则,从而阻止所有流量。这就是 init 容器必须位于最后的原因,因此在设置 iptables 规则后,代理会立即启动并运行。遗憾的是,顺序是不确定的,因此如果先注入 Istio,则会中断。

如需排查这种情况,请允许 vault 的 IP 地址,以便流向 Vault IP 的流量尚未重定向到 Envoy 代理,而 Envoy 代理尚未准备就绪,因此阻止通信。为此,应该添加一个名为 excludeOutboundIPRanges 的新注释。

对于代管式 Cloud Service Mesh,只能在 Deployment 或 Pod 级层的 spec.template.metadata.annotations 下执行此操作,例如:

apiVersion: apps/v1
kind: Deployment
...
...
...
spec:
  template:
    metadata:
      annotations:
        traffic.sidecar.istio.io/excludeOutboundIPRanges:

对于集群内 Cloud Service Mesh,您可以选择在 spec.values.global.proxy.excludeIPRanges 下将其 IstioOperator 设置为全局项,例如:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      proxy:
        excludeIPRanges: ""

添加注释后,重启工作负载。