Solucionar problemas de conectividad en un clúster


En esta página se explica cómo solucionar problemas de conectividad en un clúster de Google Kubernetes Engine (GKE).

Problemas de conectividad relacionados con la captura de paquetes de red en GKE

En esta sección se describe cómo solucionar problemas de conectividad relacionados con la captura de paquetes de red, incluidos síntomas como tiempos de espera de conexión, errores de conexión rechazada o un comportamiento inesperado de la aplicación. Estos problemas de conectividad pueden producirse a nivel de nodo o de pod.

Los problemas de conectividad de la red de tu clúster suelen clasificarse en las siguientes categorías:

  • No se puede acceder a los pods: es posible que no se pueda acceder a un pod desde dentro o fuera del clúster debido a errores de configuración de la red.
  • Interrupciones del servicio: es posible que un Servicio esté experimentando interrupciones o retrasos.
  • Problemas de comunicación entre pods: es posible que los pods no puedan comunicarse entre sí de forma eficaz.

Los problemas de conectividad de tu clúster de GKE pueden deberse a varios motivos, entre los que se incluyen los siguientes:

  • Configuraciones de red incorrectas: políticas de red, reglas de cortafuegos o tablas de enrutamiento incorrectas.
  • Errores de la aplicación: errores en el código de la aplicación que afectan a las interacciones de red.
  • Problemas de infraestructura: congestión de la red, fallos de hardware o limitaciones de recursos.

En la siguiente sección se muestra cómo resolver el problema en los nodos o pods que lo tengan.

  1. Identifica el nodo en el que se está ejecutando el pod problemático con el siguiente comando:

    kubectl get pods POD_NAME -o=wide -n NAMESPACE
    

    Haz los cambios siguientes:

    • POD_NAME con el nombre del pod.
    • NAMESPACE con el espacio de nombres de Kubernetes.
  2. Conéctate al nodo:

    gcloud compute ssh NODE_NAME \
        --zone=ZONE
    

    Haz los cambios siguientes:

    • NODE_NAME: nombre del nodo.
    • ZONE: nombre de la zona en la que se ejecuta el nodo.
  3. Para depurar un pod específico, identifica la interfaz veth asociada al pod:

    ip route | grep POD_IP
    

    Sustituye POD_IP por la dirección IP del pod.

  4. Ejecuta los comandos de la caja de herramientas.

Comandos de toolbox

toolbox es una utilidad que proporciona un entorno en contenedores en tus nodos de GKE para depurar y solucionar problemas. En esta sección se describe cómo instalar la utilidad toolbox y usarla para solucionar problemas del nodo.

  1. Mientras estés conectado al nodo, inicia la herramienta toolbox:

    toolbox
    

    Se descargarán los archivos que facilitan la utilidad toolbox.

  2. En la petición de raíz toolbox, instala tcpdump:

    • En el caso de los clústeres con direcciones IP externas o Cloud NAT:

      apt update -y && apt install -y tcpdump
      
    • En el caso de los clústeres privados sin Cloud NAT:

      Si tienes un clúster privado sin Cloud NAT, no puedes instalar tcpdump con apt. En su lugar, descarga los archivos de lanzamiento libpcap y tcpdump del repositorio oficial y copia los archivos en la VM mediante gcloud compute scp o gcloud storage cp. A continuación, instala las bibliotecas manualmente siguiendo estos pasos:

      # Copy the downloaded packages to a staging area
      cp /media/root/home/USER_NAME/tcpdump-VERSION.tar.gz  /usr/sbin/
      cp /media/root/home/USER_NAME/libpcap-VERSION.tar.gz  /usr/sbin/
      cd /usr/sbin/
      
      # Extract the archives
      tar -xvzf tcpdump-VERSION.tar.gz
      tar -xvzf libpcap-VERSION.tar.gz
      
      # Build and install libpcap (a dependency for tcpdump)
      cd libpcap-VERSION
      ./configure ; make ; make install
      
      # Build and install tcpdump
      cd ../tcpdump-VERSION
      ./configure ; make ; make install
      
      # Verify tcpdump installation
      tcpdump --version
      

      Haz los cambios siguientes:

      • USER_NAME: tu nombre de usuario en el sistema en el que se encuentran los archivos.
      • VERSION: el número de versión específico de los paquetes tcpdump y libpcap.
  3. Inicia la captura de paquetes:

    tcpdump -i eth0 -s 100 "port PORT" \
    -w /media/root/mnt/stateful_partition/CAPTURE_FILE_NAME
    

    Haz los cambios siguientes:

    • PORT: nombre del número de puerto.
    • CAPTURE_FILE_NAME: nombre del archivo de captura.
  4. Detén la captura de paquetes e interrumpe tcpdump.

  5. Para salir de la caja de herramientas, escribe exit.

  6. Lista el archivo de captura de paquetes y comprueba su tamaño:

    ls -ltr /mnt/stateful_partition/CAPTURE_FILE_NAME
    
  7. Copia la captura de paquetes del nodo en el directorio de trabajo actual de tu ordenador:

    gcloud compute scp NODE_NAME:/mnt/stateful_partition/CAPTURE_FILE_NAME \
        --zone=ZONE
    

    Haz los cambios siguientes:

    • NODE_NAME: nombre del nodo.
    • CAPTURE_FILE_NAME: nombre del archivo de captura.
    • ZONE: nombre de la zona.

Comandos alternativos

También puedes usar los siguientes métodos para solucionar problemas de conectividad en los pods que dan problemas:

  • Carga de trabajo de depuración efímera conectada al contenedor del pod.

  • Ejecuta un shell directamente en el pod de destino con kubectl exec y, a continuación, instala y ejecuta el comando tcpdump.

Problemas de conectividad de la red de pods

Como se ha mencionado en la sección Descripción general de la red, es importante saber cómo se conectan los pods desde sus espacios de nombres de red hasta el espacio de nombres raíz del nodo para solucionar problemas de forma eficaz. En la siguiente conversación, a menos que se indique lo contrario, se presupone que el clúster usa el CNI nativo de GKE en lugar del de Calico. Es decir, no se ha aplicado ninguna política de red.

Los pods de los nodos seleccionados no tienen disponibilidad

Si los pods de determinados nodos no tienen conectividad de red, asegúrate de que el puente Linux esté activo:

ip address show cbr0

Si el puente de Linux está inactivo, actívalo:

sudo ip link set cbr0 up

Comprueba que el nodo esté aprendiendo las direcciones MAC de los pods conectados a cbr0:

arp -an

Los pods de los nodos seleccionados tienen una conectividad mínima

Si los pods de determinados nodos tienen una conectividad mínima, primero debes confirmar si se han perdido paquetes ejecutando tcpdump en el contenedor de la caja de herramientas:

sudo toolbox bash

Instala tcpdump en la caja de herramientas si aún no lo has hecho:

apt install -y tcpdump

Ejecuta tcpdump en cbr0:

tcpdump -ni cbr0 host HOSTNAME and port PORT_NUMBER and [TCP|UDP|ICMP]

Si parece que se están descartando paquetes grandes en sentido descendente desde el puente (por ejemplo, se completa el handshake de TCP, pero no se reciben mensajes de saludo SSL), asegúrate de que el MTU de cada interfaz de pod de Linux esté configurado correctamente con el MTU de la red de VPC del clúster.

ip address show cbr0

Cuando se usan superposiciones (por ejemplo, Weave o Flannel), este MTU debe reducirse aún más para adaptarse a la sobrecarga de encapsulación de la superposición.

MTU de GKE

El valor de MTU seleccionado para una interfaz de Pod depende de la interfaz de red de contenedores (CNI) que usen los nodos del clúster y del valor de MTU de la VPC subyacente. Para obtener más información, consulta Pods.

El valor de MTU de la interfaz de Pod es 1460 o se hereda de la interfaz principal del nodo.

CNI MTU GKE Standard
kubenet 1460 Predeterminado
kubenet
(versión 1.26.1 de GKE y posteriores)
Heredada Predeterminado
Calico 1460

Se habilita mediante --enable-network-policy.

Para obtener más información, consulta el artículo Controlar la comunicación entre pods y servicios mediante políticas de red.

netd Heredada Se habilita de una de las siguientes formas:
GKE Dataplane V2 Heredada

Se habilita mediante --enable-dataplane-v2.

Para obtener más información, consulta Usar GKE Dataplane V2.

Errores de conexión intermitentes

iptables reenvía las conexiones a los pods y desde ellos. Los flujos se monitorizan como entradas en la tabla conntrack y, cuando hay muchas cargas de trabajo por nodo, el agotamiento de la tabla conntrack puede manifestarse como un fallo. Estos datos se pueden registrar en la consola serie del nodo. Por ejemplo:

nf_conntrack: table full, dropping packet

Si puedes determinar que los problemas intermitentes se deben al agotamiento de conntrack, puedes aumentar el tamaño del clúster (lo que reduce el número de cargas de trabajo y flujos por nodo) o aumentar nf_conntrack_max:

new_ct_max=$(awk '$1 == "MemTotal:" { printf "%d\n", $2/32; exit; }' /proc/meminfo)
sysctl -w net.netfilter.nf_conntrack_max="${new_ct_max:?}" \
  && echo "net.netfilter.nf_conntrack_max=${new_ct_max:?}" >> /etc/sysctl.conf

También puedes usar NodeLocal DNSCache para reducir las entradas de seguimiento de conexiones.

Se ha informado de "bind: Address already in use " en un contenedor

No se puede iniciar un contenedor en un pod porque, según los registros del contenedor, el puerto al que intenta enlazarse la aplicación ya está reservado. El contenedor está en un bucle de fallos. Por ejemplo, en Cloud Logging:

resource.type="container"
textPayload:"bind: Address already in use"
resource.labels.container_name="redis"

2018-10-16 07:06:47.000 CEST 16 Oct 05:06:47.533 # Creating Server TCP listening socket *:60250: bind: Address already in use
2018-10-16 07:07:35.000 CEST 16 Oct 05:07:35.753 # Creating Server TCP listening socket *:60250: bind: Address already in use

Cuando Docker falla, a veces se queda un contenedor en ejecución y se vuelve obsoleto. El proceso sigue ejecutándose en el espacio de nombres de red asignado al pod y escuchando en su puerto. Como Docker y kubelet no saben que el contenedor está obsoleto, intentan iniciar un contenedor nuevo con un proceso nuevo, que no puede enlazarse al puerto porque se añade al espacio de nombres de red que ya está asociado al pod.

Para diagnosticar este problema, sigue estos pasos:

  1. Necesitará el UUID del pod en el campo .metadata.uuid:

    kubectl get pod -o custom-columns="name:.metadata.name,UUID:.metadata.uid" ubuntu-6948dd5657-4gsgg
    
    name                      UUID
    ubuntu-6948dd5657-4gsgg   db9ed086-edba-11e8-bdd6-42010a800164
    
  2. Obtén la salida de los siguientes comandos del nodo:

    docker ps -a
    ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
    
  3. Comprueba los procesos en ejecución de este pod. Como los UUID de los espacios de nombres de cgroup contienen el UUID del pod, puedes buscar el UUID del pod en la salida de ps. También puedes buscar la línea anterior para ver los procesos docker-containerd-shim que tienen el ID del contenedor en el argumento. Corta el resto de la columna cgroup para obtener un resultado más sencillo:

    # ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep -B 1 db9ed086-edba-11e8-bdd6-42010a800164 | sed s/'blkio:.*'/''/
    1283089     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 276e173b0846e24b704d4 12:
    1283107 1283089 Ss   sys_pause            4026532393         pause           /pause                                     12:
    1283150     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim ab4c7762f5abf40951770 12:
    1283169 1283150 Ss   do_wait              4026532393         sh              /bin/sh -c echo hello && sleep 6000000     12:
    1283185 1283169 S    hrtimer_nanosleep    4026532393           sleep           sleep 6000000                            12:
    1283244     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 44e76e50e5ef4156fd5d3 12:
    1283263 1283244 Ss   sigsuspend           4026532393         nginx           nginx: master process nginx -g daemon off; 12:
    1283282 1283263 S    ep_poll              4026532393           nginx           nginx: worker process
    
  4. En esta lista, puedes ver los IDs de contenedor, que también deberían aparecer en docker ps.

    En ese caso, ocurrirá lo siguiente:

    • docker-containerd-shim 276e173b0846e24b704d4 para pausar
    • docker-containerd-shim ab4c7762f5abf40951770 para sh con sueño (sleep-ctr)
    • docker-containerd-shim 44e76e50e5ef4156fd5d3 para nginx (echoserver-ctr)
  5. Comprueba los elementos de la salida docker ps:

    # docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3'
    44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f   gcr.io/google_containers/echoserver@sha256:3e7b182372b398d97b747bbe6cb7595e5ffaaae9a62506c725656966d36643cc                   "nginx -g 'daemon off;'"                                                                                                                                                                                                                                                                                                                                                                     14 hours ago        Up 14 hours                             k8s_echoserver-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    ab4c7762f5abf40951770d3e247fa2559a2d1f8c8834e5412bdcec7df37f8475   ubuntu@sha256:acd85db6e4b18aafa7fcde5480872909bd8e6d5fbd4e5e790ecc09acc06a8b78                                                "/bin/sh -c 'echo hello && sleep 6000000'"                                                                                                                                                                                                                                                                                                                                                   14 hours ago        Up 14 hours                             k8s_sleep-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    276e173b0846e24b704d41cf4fbb950bfa5d0f59c304827349f4cf5091be3327   registry.k8s.io/pause-amd64:3.1
    

    En circunstancias normales, todos los IDs de contenedor de ps se muestran en docker ps. Si no ves alguno, significa que el contenedor está obsoleto y es probable que veas un proceso secundario de docker-containerd-shim process escuchando en el puerto TCP que indica que ya está en uso.

    Para verificarlo, ejecuta netstat en el espacio de nombres de red del contenedor. Obtén el PID de cualquier proceso de contenedor (NO docker-containerd-shim) del pod.

    En el ejemplo anterior:

    • 1283107 - pause
    • 1283169 - sh
    • 1283185 - sueño
    • 1283263 - nginx master
    • 1283282 - nginx worker
    # nsenter -t 1283107 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # nsenter -t 1283169 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    

    También puedes ejecutar netstat con ip netns, pero debes vincular manualmente el espacio de nombres de red del proceso, ya que Docker no lo hace:

    # ln -s /proc/1283169/ns/net /var/run/netns/1283169
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns list
    1283169 (id: 2)
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns exec 1283169 netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # rm /var/run/netns/1283169
    

Solución:

La medida de mitigación a corto plazo consiste en identificar los procesos obsoletos mediante el método descrito anteriormente y finalizar los procesos con el comando kill [PID].

La mitigación a largo plazo implica identificar por qué falla Docker y solucionarlo. Se puede deber a los siguientes motivos:

  • Se acumulan procesos zombi, por lo que se agotan los espacios de nombres de PID
  • Error en Docker
  • Presión de recursos/OOM

Siguientes pasos