Error de "host desconocido" al llamar al backend en contenedores desde el frontend
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?
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 backend
nombre 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 fetch
llamada 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 backend
nombre 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 backend
contenedor; 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 fetch
llamada a
fetch('/api/hello');
Tenga en cuenta que la URL no contiene un http
esquema 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.