featured image

Los servidores cada vez más están compuestos de servicios y microservicios heterogéneos. Muchos de ellos incluso son conglomerados accesibles a través de subdominios pero los frontales y las direcciones IP por donde accedemos a estos servicios de forma pública son solo una. ¿Cómo conseguimos esto?

Pongamos como ejemplo la configuración de un dominio: juegos.com. En este dominio vamos a levantar varios juegos. Cada uno de estos juegos tiene su propio sistema. Hemos desarrollado uno con Node.js, otro con Elixir y otro con un sistema PHP FPM. Todos conviven en el mismo servidor cada uno escuchando en un puerto distinto:

  • cuatro.juego.com en el puerto 4000 en Elixir implementa el juego de Conecta Cuatro.
  • nibbles.juego.com en el puerto 1300 en Node.js implementa el juego Nibbles (serpientes).
  • bingo.juego.com en el puerto 8080 en PHP FPM implementa un juego de Bingo.

Todos ellos están bajo la misma dirección IP. Por eso el hecho de tener que escuchar de un puerto diferente. Pero claro, la URL para acceder a estos servicios se hace rara, difícil de recordar. Es fea. Vamos a implementar el siguiente sistema para solucionar esta situación:

Proxy Inverso

Como vemos en el gráfico cada petición llega de Internet a un servidor web en el puerto 80 y este servidor accede de forma local al puerto específico donde se encuentra el otro servicio. Los juegos reciben la petición de nuestro servidor proxy según la configuración empleada y retornan la información devuelta por el servidor.

De esta forma podemos conseguir tener en la misma dirección IP diferentes subdominios y diferentes servicios funcionando sin problemas.

Si tienes dudas de cómo funciona el protocolo HTTP puedes revisar este artículo que muestra cómo funciona.

¿Cómo hacemos esto en Apache?

Siguiendo la configuración de más arriba. Vamos a considerar la configuración de los tres juegos y considerar que escuchan en esos puertos pero en la dirección IP local (127.0.0.1). De esta forma esos servicios son solo accesibles a través de esos puertos desde dentro de la máquina.

Empleando Apache deberíamos activar el uso de mod_proxy (en Debian y Ubuntu basta con ejecutar a2enmod mod_proxy y seguir las instrucciones de pantalla). Después solo nos queda agregar en la configuración de nuestro servidor estas entradas:

<VirtualHost *:80>
    ServerName         cuatro.juego.com
    ProxyPreserveHost  On
    ProxyPass          "/" "http://127.0.0.1:4000/"
    ProxyPassReverse   "/" "http://127.0.0.1:4000/"
</VirtualHost>

<VirtualHost *:80>
    ServerName         nibbles.juego.com
    ProxyPreserveHost  On
    ProxyPass          "/" "http://127.0.0.1:1300/"
    ProxyPassReverse   "/" "http://127.0.0.1:1300/"
</VirtualHost>

<VirtualHost *:80>
    ServerName         bing.juego.com
    ProxyPreserveHost  On
    ProxyPass          "/" "http://127.0.0.1:8080/"
    ProxyPassReverse   "/" "http://127.0.0.1:8080/"
</VirtualHost>

Esta configuración es bastante minimalista, obviamente puedes agregar otras directivas como DocumentRoot dentro de los VirtualHost e incluso recomiendo agregar configuración específica para los logs del sistema.

¿Cómo hacemos esto en Nginx?

Al igual que hemos hecho con Apache también podemos realizar la configuración con Nginx. Este servidor promete ser más ligero y rápido para realizar este tipo de tareas por lo que se recomienda más. Su configuración es la siguiente:

server {
    listen 80;
    server_name cuatro.juego.com;
    location / {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

server {
    listen 80;
    server_name nibbles.juego.com;
    location / {
        proxy_pass http://127.0.0.1:1300;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

server {
    listen 80;
    server_name bingo.juego.com;
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

En esta configuración debemos incluir algunas directivas extra que agregan información a las cabeceras HTTP. Si tienes dudas sobre cómo funciona el protocolo HTTP te recomiendo revisar este artículo sobre cómo funciona HTTP.

¿Cómo hacemos esto en Yaws?

Al igual que en este otro artículo yo siempre recomiendo el uso de Yaws sobre las otras dos opciones anteriores. No obstante, la madurez de la opción de proxy inverso en Yaws no es tanta como en las otras dos opciones por lo que hay que emplearla con sumo cuidado.

Dicho esto pasamos a la configuración. En Yaws debes agregar lo siguiente al fichero de configuración:

<server cuatro.juego.com>
    port = 80
    revproxy = / http://127.0.0.1:4000/
</server>

<server nibbles.juego.com>
    port = 80
    revproxy = / http://127.0.0.1:1300/
</server>

<server bingo.juego.com>
    port = 80
    revproxy = / http://127.0.0.1:8080/
</server>

Al igual que los casos anteriores cada petición es redireccionada a la URL definida dentro de cada bloque y conseguimos nuestro objetivo.

Hay un detalle importante a tener en cuenta con Yaws. Al instalar el servidor de paquetería por defecto escucha de los puertos 8000 y 8080. Esto es debido a que los puertos del 1 al 1024 están reservados para uso únicamente de root. No es posible para otros usuarios establecer un servicio que escuche de estos puertos. Los servidores como Apache y Nginx resuelven esto haciendo que el servidor se ejecute con el usuario root y después, cada petición se maneja con otro proceso creado con el usuario www-data (u otro según el sistema Linux o BSD que empleemos).

Otras configuraciones

El proxy inverso no solo nos permite resolver casos como el mostrado aquí sino también cuando queremos tener bajo diferentes URIs diferentes servicios. Por ejemplo en el caso anterior imagina que hubiésemos querido tener esta configuración:

  • juego.com/cuatro
  • juego.com/nibbles
  • juego.com/bingo

De este modo la URL base de cada juego cambia y debemos agregar según el caso la reescritura de las URLs antes de pasar la información al servicio subyacente. En Apache se resuelve con el uso de mod_rewrite y las directivas de este módulo y en Nginx de una forma mucho más simple a través de rewrite. En Yaws esta tarea es mucho más complicada y requiere de la escritura de un código de reescritura de URLs.

Conclusiones

Como puedes ver esta forma no solo nos permite centralizar el frontal web y la configuración pública de qué verán nuestros usuarios sino que además nos da más flexibilidad para agregar otros elementos como certificados SSL o sistemas de autenticación basados en HTTP de forma unificada en nuestro servidor de elección sin necesidad de modificar nada ni en Node.JS, ni en Elixir, ni en PHP-FPM, ni ningún otro sistema subyacente.

¿Y tú?, ¿has necesitado instalar un proxy inverso en algún momento?, ¿tuviste problemas y decidiste emplear otra solución diferente?, ¿empleas esta misma solución pero te gustaría matizar algún elemento concreto?, ¿necesitas ayuda configurando un proxy inverso? ¡Déjanos tu comentario!