¿Qué significa el error getaddrinfo EAI_AGAIN en NodeJS?

¿Qué significa el error getaddrinfo EAI_AGAIN en NodeJS?

Las aplicaciones en la nube, como las desplegadas en AWS, requieren una gestión y diagnóstico meticuloso, especialmente cuando se enfrentan a errores aparentemente esquivos. Uno de estos errores es el getaddrinfo EAI_AGAIN que encontramos en Node.js. Aunque puede presentarse como un error de aplicación, su causa yace mucho más profundo en la configuración del sistema y en cómo se gestiona la resolución DNS.

Descripción del problema

Recientemente mientras operaba una de mis aplicación, noté un delay bastante extraño el cual producía un error 502 a través de nginx que aparentemente sugería que la aplicación que debería responder estaba colgada, y al revisar los logs de esta aplicación, encontré el siguiente error:

Error: getaddrinfo EAI_AGAIN app.prod.private
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:71:26) {
  errno: -3001,
  code: 'EAI_AGAIN',
  syscall: 'getaddrinfo',
  hostname: 'app.prod.private'
}

Este error sugiere que el sistema no pudo resolver el dominio dentro de un tiempo específico. Las causas potenciales abarcan desde problemas de conectividad, configuraciones DNS erróneas, hasta congestiones en el tráfico de red.

Arquitectura y diseño

Es fundamental conocer la arquitectura que uso en AWS:

  • Contenedores: Utilizo contenedores para alojar y aislar las aplicaciones.
  • Service Discovery: Esta característica permite a los contenedores descubrir y comunicarse entre sí de manera dinámica.
  • Dominios .local y .private: Cada contenedor se comunica con otros mediante un dominio .local gracias al servicio de descubrimiento. Sin embargo, para las comunicaciones externas, utilizo un dominio .private que actúa como alias del dominio .local.

Ejemplificado en un diagrama, se vería como lo siguiente:

graph TD
  A[Contenedor 1 - app1.local] --> C[Hosted Zone - AWS Route 53]
  B[Contenedor 2 - app2.local] --> C
  C --> D{Resolver DNS}
  D --> E[Alias - app1.prod.private]
  D --> F[Alias - app2.prod.private]

Investigando posibles soluciones

Cuando intenté resolver el dominio con nslookup app.prod.private, encontré un problema: no se resolvía. Sin embargo, al especificar explícitamente el servidor DNS de AWS 169.254.169.253 de la forma nslookup app.prod.private 169.254.169.253, la resolución fue exitosa. ¿Por qué? El primer comando usa el servidor DNS predeterminado del sistema, que en este caso estaba mal configurado. El segundo comando omite el DNS predeterminado y apunta directamente al servidor DNS de AWS, que tiene la correcta configuración para resolver el dominio.

Dentro de AWS:

  • VPC Peering: Esta es una característica que utilizo en AWS y que me permite utilizar dos VPCs en AWS como si estuvieran en la misma red, esto es necesario porque utilizo diferentes organizaciones para separar los ambientes de desarrollo, pruebas y producción.

Pues hice un dig para obtener más detalles:

dig app.prod.private @169.254.169.253

La respuesta fue similar a:

;; ANSWER SECTION:
app.prod.private.   117 IN  CNAME   app.prod.local.
app.prod.local. 117 IN  A   192.0.2.123

Esto confirmó que el dominio .private era un alias del dominio .local.

Pasos para resolver el problema

  1. Abre el archivo de configuración del resolver:
   sudo nano /etc/resolv.conf
  1. Modifica la dirección del nameserver a 169.254.169.253:
   nameserver 169.254.169.253
  1. Guarda y cierra el archivo.
  2. Reinicia los servicios de red para aplicar los cambios.
  3. Vuelve a probar con nslookup app.prod.private para confirmar que la resolución DNS ahora funciona correctamente.

Yo particularmente no tuve problemas modificando el archivo resolve.conf, pero sé que dependiendo de los OS y las versiones, esto puede ser una tarea un poco más compleja que quizás involucre que debas desactivar la configuración automática de la red (o algunas de sus características) y tengas que especificar manualmente la dirección del DNS a través del archivo.

Algunas técnicas involucran:

  1. Usando NetworkManager (común en muchas distribuciones):

    1. Edita el archivo de configuración correspondiente en /etc/NetworkManager/system-connections/.
    2. Busca la sección [ipv4] y agrega o modifica la línea con dns=169.254.169.253;.
    3. Reinicia NetworkManager con sudo systemctl restart NetworkManager.
  2. Usando resolvconf (si tu sistema utiliza resolvconf):

    1. Edita o crea el archivo /etc/resolvconf/resolv.conf.d/base.
    2. Añade nameserver 169.254.169.253.
    3. Reinicia el servicio con sudo systemctl restart resolvconf.
  3. Usando systemd-resolved (en sistemas que usan systemd):

    1. Edita el archivo /etc/systemd/resolved.conf.
    2. Añade o modifica la línea con DNS=169.254.169.253 bajo la sección [Resolve].
    3. Reinicia el servicio con sudo systemctl restart systemd-resolved.

Y quizás hay más, éstas son algunas de las que pude encontrar, pero que no realmente probé.

La dirección IP 169.254.169.253 es una dirección IP especial reservada por AWS para el servidor DNS dentro de las VPCs. No es algo que configures específicamente en tu VPC; es una dirección estándar proporcionada por AWS en todas las VPCs para la resolución de nombres.

Conclusión

El error getaddrinfo EAI_AGAIN no fue, después de todo, un problema exclusivo de Node.js. Era un reflejo de desafíos en la infraestructura de AWS y la configuración de DNS en el sistema operativo. Con una combinación de herramientas como nslookup, dig y un entendimiento claro de nuestra arquitectura, pude diagnosticar y resolver el problema. Esta experiencia subraya la importancia de tener un enfoque holístico hacia el debugging, mirando más allá del código y considerando la infraestructura subyacente.

Finalmente se abrió un ticket en AWS para averiguar la naturaleza de este cambio sin razón aparente, porque esto no es algo que debería ocurrir sin aviso.

Update

AWS respondió y finalmente terminamos modificando el DNS namesesrver de acuerdo a como lo explico en el artículo, pero me compartieron un enlace donde se explica el porque no se deberia de utilizar dominios .local, ya que está reservado para uso exclusivo de Multicast DNS según el RFC 6762, lo que significa que hay que hacer algunas modificaciones en mi arquitectura.

Lo que no supieron explicar es porque funcionaba antes por casi 4 años y de pronto dejó de funcionar.

Happy coding! :D


Photo by Alessio Furlan on Unsplash

Jack Fiallos

Jack Fiallos

Te gustó este artículo?