Como resolver problemas de inicialização de cargas de trabalho no Cloud Service Mesh

Este documento explica problemas comuns do Cloud Service Mesh e como resolvê-los. Se você precisar de mais ajuda, consulte Como receber suporte.

O gateway não é iniciado com o proxy sem distribuição quando uma porta privilegiada é exposta.

Por padrão, o proxy sem distro começa com permissões que não são raiz, o que, em alguns casos, pode causar falhas de vinculação em portas privilegiadas. Se você encontrar erros semelhantes a estes durante a inicialização do proxy, será necessário aplicar mais securityContext para uma implantação de gateway.

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

O exemplo a seguir é o yaml para uma implantação de gateway de saída:

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 

Conexão recusada ao alcançar um endpoint do Cloud Service Mesh

Podem ocorrer, de modo intermitente, erros de conexão recusada (ECONNREFUSED) na comunicação dos clusters com os endpoints, como Memorystore Redis, Cloud SQL ou qualquer serviço externo que a carga de trabalho do aplicativo precise alcançar.

Isso pode ocorrer quando a carga de trabalho do aplicativo é iniciada mais rapidamente que o contêiner istio-proxy (Envoy) e tenta alcançar um endpoint externo. Como o istio-init (initContainer) já foi executado nesta etapa, por isso, há regras em vigor que redirecionam todo o tráfego de saída para Envoy. Como o istio-proxy ainda não está pronto, as regras do iptables redirecionam o tráfego para um proxy secundário que ainda não foi iniciado e, portanto, o aplicativo recebe o erro ECONNREFUSED.

As etapas a seguir detalham como verificar se esse é o erro que você está enfrentando:

  1. Verifique os registros do Stackdriver com o seguinte filtro para identificar quais pods tiveram o problema.

    O exemplo a seguir mostra uma mensagem de erro típica:

    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. Pesquise uma ocorrência do problema. Se você estiver usando o Stackdriver legado, use resource.type="container".

    resource.type="k8s_container"
    textPayload:"$ERROR_MESSAGE$"
    
  3. Expanda a ocorrência mais recente para ver o nome do pod e anote pod_name em resource.labels.

  4. Encontre a primeira ocorrência do problema para esse pod:

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

    Exemplo de saída:

    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. Anote o carimbo de data/hora do primeiro erro desse pod.

  6. Use o filtro a seguir para ver os eventos de inicialização do pod.

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

    Exemplo de saída:

    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. Use os carimbos de data/hora dos erros e eventos de inicialização do istio-proxy para confirmar se os erros estão acontecendo quando o Envoy não está pronto.

    Se os erros ocorrem enquanto o contêiner istio-proxy ainda não está pronto, é normal receber erros de conexão recusada. No exemplo anterior, o pod tentava se conectar ao Redis assim que 2020-03-31T10:41:15.552128897Z, mas por 2020-03-31T10:41:58Z o istio-proxy ainda falhava nas sondagens de prontidão.

    Mesmo que o contêiner istio-proxy tenha sido iniciado primeiro, é possível que ele não tenha ficado pronto rápido o suficiente antes de o app já estar tentando se conectar ao endpoint externo.

    Se esse for o problema que você está enfrentando, continue com as seguintes etapas de solução de problemas.

  8. Anote a configuração no nível do pod. Isso está disponível apenas no nível do pod e não em nível global.

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. Modifique o código do aplicativo para que ele verifique se o Envoy está pronto antes de tentar fazer outras solicitações para serviços externos. Por exemplo, na inicialização do aplicativo, inicie um loop que faça solicitações para o endpoint de integridade do istio-proxy e só continue depois que um número 200 for recebido. O endpoint de integridade do istio-proxy é este:

    http://localhost:15020/healthz/ready
    

Disputa durante a injeção de sidecar entre o Vault e o Cloud Service Mesh

Ao usar vault para gerenciamento de secrets, às vezes vault injeta o arquivo secundário antes de istio, fazendo com que os pods fiquem travados no status Init. Quando isso acontece, os pods criados ficam travados no status Init depois de reiniciar qualquer implantação ou implantar uma nova. Exemplo:

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

Esse problema é causado por uma disputa. Tanto o Istio quanto vault injetam o arquivo secundário, e o Istio precisa ser o último a fazer isso. O proxy istio não está em execução durante os contêineres init. O contêiner init istio configura as regras do AndroidManifest para redirecionar todo o tráfego para o proxy. Como ela ainda não está em execução, essas regras redirecionam para nada, bloqueando todo o tráfego. É por isso que o contêiner init precisa ser o último, para que o proxy entre em execução imediatamente após as regras do Dockerfile serem configuradas. Infelizmente, a ordem não é determinista. Portanto, se o Istio for injetado primeiro, ele falhará.

Para resolver essa condição, permita o endereço IP de vault para que o tráfego que vai para o IP do Vault não seja redirecionado para o proxy Envoy, que ainda não está pronto e, portanto, bloqueando a comunicação. Para isso, uma nova anotação chamada excludeOutboundIPRanges precisa ser adicionada.

Para o Cloud Service Mesh gerenciado, isso só é possível no nível da implantação ou do pod em spec.template.metadata.annotations. Por exemplo:

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

Para o Cloud Service Mesh no cluster, há uma opção para defini-lo como global com um IstioOperator em spec.values.global.proxy.excludeIPRanges. Por exemplo:

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

Depois de adicionar a anotação, reinicie suas cargas de trabalho.