Error de "host desconocido" al llamar al backend en contenedores desde el frontend

Resuelto David Maze asked hace 1 año • 1 respuestas

Estoy creando una aplicación en contenedores donde tanto el frontend como el backend se sirven desde el mismo archivo de redacción:

version: '3.8'
services:
  frontend:
    build: ./frontend
    ports: ['8001:80']
  backend:
    build: ./backend
    ports: ['8002:5000']

Mi código de interfaz necesita solicitar un recurso JSON desde el servidor. Dado que tanto el frontend como el backend están en el mismo archivo de redacción, esperaría que la red Docker me permita usar el nombre del backend como nombre de host, como

fetch('http://backend:5000/hello')

Sin embargo, cuando hago esta llamada, aparece un error de Javascript.

TypeError: NetworkError when attempting to fetch resource.

y si miro en las herramientas de depuración del navegador en la pestaña "red", veo un código de error NS_ERROR_UNKNOWN_HOST.

¿Por qué el nombre del servicio Docker no funciona aquí como nombre de host?

David Maze avatar Sep 07 '23 20:09 David Maze
Aceptado

Cuando tienes una aplicación basada en navegador, el código realmente se ejecuta en el navegador. Incluso si tiene un contenedor que entrega su código, en realidad se ejecuta en el sistema host (o en un sistema remoto) y no en Docker. El navegador es la entidad que realiza la fetch()llamada y el navegador en sí no se ejecuta en un contenedor. Eso significa que el navegador está intentando resolver el backendnombre del host y produce ese error.

Si está ejecutando todo esto en el mismo sistema, la forma "fácil" de solucionarlo es publicar por separado un puerto para el servicio backend. En el archivo Redactar de la pregunta, se publica en el puerto 8002. Si cambia la fetchllamada a

fetch('http://localhost:8002/hello')

accederá al backend a través de este puerto publicado.

Sin embargo: esta configuración sólo funciona si el navegador y los contenedores se ejecutan en la misma máquina. Si va a implementar el servicio en otro lugar, este nombre de host también deberá ser diferente. Esta llamada también activa las reglas sobre el intercambio de recursos entre orígenes (CORS), y el servicio backend debe ser consciente de esto.

Un mejor enfoque es ejecutar un proxy inverso HTTP. El proxy se ejecuta en un contenedor, por lo que está "dentro de Docker" y puede resolver el backendnombre del host. El navegador utiliza una URL de solo ruta para solicitar el recurso adicional desde el mismo host y puerto que lo sirvió, por lo que no necesita conocer ningún nombre de host. Esto también resuelve los problemas de producción-implementación y CORS.

Esto implica un contenedor adicional en la configuración de Compose.

version: '3.8'
services:
  frontend: { ... }
  backend: { ... }
  proxy:
    build: ./proxy
    ports: ['8000:80']

El contenedor podría estar basado en Nginx, requiriendo sólo agregar su configuración.

# proxy/Dockerfile
FROM nginx:1.25
COPY default.conf /etc/nginx/conf.d

La configuración del proxy puede ser muy sencilla. Las rutas que comienzan con /api/se reenvían al backendcontenedor; cualquier otra cosa se reenvía al frontend.

server {
  listen 80;
  server_name localhost;
  
  location / {
    proxy_pass http://frontend:80/;
  }

  location /api/ {
    proxy_pass http://backend:5000/;
  }
}

Ahora en el código Javascript puedes cambiar la fetchllamada a

fetch('/api/hello');

Tenga en cuenta que la URL no contiene un httpesquema de URL ni ningún nombre de host o puerto; esta URL de solo ruta se resuelve en relación con la URL de la página.

Escribí una pequeña aplicación de demostración que muestra estos diferentes patrones de llamadas y qué URL funcionan en qué contextos.

David Maze avatar Sep 07 '2023 13:09 David Maze