Cómo configurar Nginx con Node.js en Producción

Share Button

nodenginx¿Qué pasa si tienes un VPS o una instancia EC2 en Amazon Web Services y quieres tener varios procesos de Node ejecutándose?

¿Y si quieres servir la parte pública de una aplicación web por un lado y la parte Backend por otro?

Si corremos nuestra app Node en el puerto 80 para que sea accesible desde una IP o dominio, no podemos usar el mismo puerto con otro proceso. ¿Cómo podemos solucionar esto? con Nginx.

Nginx es un servidor web, al estilo de Apache pero orientado a eventos (como Node) y actúa como un proxy lo que nos permite redireccionar el tráfico entrante en función del dominio de donde vienen, hacia el proceso  y puerto que nos interese.

nginx como reverse proxy

En este tutorial veremos como configurarlo en una instancia Amazon de una manera muy sencilla. Vamos a ello!

Gracias a Node, he pasado del mundo Frontend al Backend intentado ser un Full-Stack Developer. Esto ha hecho que pase de tocar el backend a acercarme peligrosamente al mundo DevOp, lo cuál es divertido porque aprendes algo nuevo y tienes una visión más amplia de la arquitectura web.

No he probado esto en local, porque me daba muchos fallos, si alguien tiene más experiencia con Nginx puede dejar en los comentarios sus aportes y entre todos aprenderemos más :)

Esto está probado en una instancia Ec2 de Amazon Web Services. Si no sabes como arrancar y configurar una, en ésta antigua entrada del blog cuento como hacerlo.

Una vez tenemos nuestro servidor con Linux (Ubuntu preferiblemente), instalamos Nginx

$ sudo apt-get install nginx

Ahora creamos una carpeta donde se alojará el contenido público y HTML estático que serviremos por el puerto 80.

$ sudo mkdir -p /var/www
$ sudo chown -R ubuntu:ubuntu /var/www #Donde ubuntu es el nombre de usuario del sistema

Configuramos Nginx en su fichero “default” de configuración

$ sudo nano /etc/nginx/sites-available/default

Y sustituimos el contenido del fichero por este otro, sustituyendo “tu_dominio” por el nombre de tu dominio en cuestión.

server {
  listen 0.0.0.0:80;

  root /var/www/tu_dominio;
  index index.html index.htm;
  server_name tu_dominio.com;
  access_log /var/log/nginx/tu_dominio.access.log;
  error_log /var/log/nginx/tu_dominio.error.log debug;

  location / {
    try_files $uri $uri/ /index.html;
  }
}

Antes de reiniciar Nginx, acuérdate de configurar en el Panel de configuración de DNS de tu dominio, de tal manera que “tu_dominio.com” apunte con un registro tipo A a la IP de tu servidor. De paso configura un subdominio tipo “sudominio.tu_dominio.com” que apunte también a la misma IP de tu máquina, también como registro tipo A.

Este archivo indica que escuche en el puerto 80 y muestre el contenido de la carpeta /var/www/tu_dominio cuando se acceda por el navegador con el nombre “tu_dominio.com”.

Para que se vea algo cuando accedamos, creamos un archivo index.html en esa carpeta con el contenido que queramos

$ sudo mkdir -p /var/www/tu_dominio
$ sudo nano /var/www/tu_dominio/index.html

Y el contenido puede ser un simple “Hola Mundo”;

Para finalizar esta parte, editamos el archivo /etc/hosts y añadimos los dominios de tal manera que quede así

127.0.0.1         localhost
127.0.0.1         ubuntu
<IP_del_servidor> tu_dominio.com
<IP_del_servidor> subdominio.tu_dominio.com

Ya podemos reiniciar Nginx

$ sudo service nginx restart

Creamos un symlink del archivo default en la carpeta “sites-enabled

$ sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default

De esta manera, si todo va bien, al acceder a “tu_dominio.com” veremos un “Hola Mundo” en el navegador.

Configuración de Node.js con Nginx.

Ahora queremos que una aplicación Node, que esté corriendo en nuestro servidor en el puerto 3000 sea accesible a través de “subdominio.tu_dominio.com”. Esto lo conseguiremos haciendo lo siguiente:
Creamos un archivo de configuración para nuestro subdominio:

$ sudo nano /etc/nginx/sites-available/subdominio.tu_dominio.com

Y añadimos la siguiente configuración, de modo que Nginx actúe como un Proxy que redirija al puerto 3000 del servidor cuando el tráfico provenga de “subdominio.tu_dominio.com”

upstream subdominio.tu_dominio.com {  
  server 127.0.0.1:3000;
}

server {  
  listen 0.0.0.0:80;
  server_name subdominio.tu_dominio.com;
  access_log /var/log/nginx/subdominio.tu_dominio.access.log;
  error_log /var/log/nginx/subdominio.tu_dominio.error.log debug;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;

    proxy_pass http://subdominio.tu_dominio.com;
    proxy_redirect off;
  }
}

Creamos un symlink para este archivo en sites-enabled como hicimos con el fichero default.

$ sudo ln -s /etc/nginx/sites-available/subdominio.tu_dominio.com /etc/nginx/sites-enabled/subdominio.tu_dominio.com

Si accedemos a “subdominio.tu_dominio.com” nos aparecerá el mensaje Error 502 – Bad Gateway, esto es porque aún no tenemos nada corriendo en el puerto 3000 y no tiene nade que mostrar.

Por tanto es hora de poner en marcha nuestra app node, desde cualquier directorio del servidor que queramos y ejecutarla preferiblemente con “forever”

$ sudo npm install -g forever
$ forever start nuestra_app.js

Si el servidor se reiniciara por alguna razón, tendríamos que ejecutar estos comandos de node de nuevo, para evitar y esto y conseguir que las aplicaciones se nos ejecuten automáticamente con el inicio de la máquina, creamos un “job”

$ sudo nano /etc/init/subdominio.tu_dominio.com.conf

con el siguiente contenido:

description "subdominio.tu_dominio.com"
env APP_PATH="ruta_donde_esta_la_app_node"

start on startup
stop on shutdown

script
  cd $APP_PATH
  exec forever start nuestra_app.js
end script

Y listo!, si entramos en “subdominio.tu_dominio.com” veremos nuestra aplicación node ejecutándose y si entramos por “dominio.com” veremos un Hola Mundo, estando los dos proyectos en la misma máquina.

Share Button
The following two tabs change content below.

cazaustre

Front End Developer at Chefly
Desarrollador Front-End y Diseñador Gráfico Freelance. Apasionado de la tecnología HTML5 y el mundo JavaScript. Geek, adicto a las series y a las camisetas.
  • http://www.magmax.org/ Miguel Ángel García

    Lo primero, decirte que es un buen artículo :D

    Sin embargo, me gustaría criticar un par de cosas. La primera está relacionada con el antipatrón “Golden Hammer” (http://sourcemaking.com/antipatterns/golden-hammer ). Quizá JavaScript no sea la mejor solución para todo. Y para mentiras está la estadística. En este caso, no sabes cuántos de esos proyectos tienen continuidad, ni cuántos se usan de verdad. Además, hoy día, para el backend puedes usar lo que te dé la gana (python, ruby, java, php o incluso C, C++, Go-lang o JS), pero para el frontend en web, estás vendido. Así que es normal que JS sea el más usado.

    Además, cuando he leído “funciona orientado a eventos (como Node)”, no he podido evitar acordarme de la frase de Jesulín “esto es como un toro”. Todo funciona orientado a eventos: Si Apache no recibe una petición, no hace nada. Si cualquier interfaz en GTK, QT, … no recibe un evento, no hace nada. Si tu servidor CUPS no recibe un evento, no hace nada. No descuidemos esto.

    Ya metiéndome en harina, comentarte que si editas el archivo /etc/hosts dejas de utilizar el DNS. Esto puede estar bien, ya que ganarás algo de velocidad, pero puede complicarte las cosas a la larga, si necesitas mover los servicios de un subdominio a otra máquina. Depurar un error tan tonto como no haber borrado una entrada del /etc/hosts puede llevar un buen rato. Es mejor dejar que el DNS haga su trabajo. Salvo en local/desarrollo/testing, donde es posible que no queramos/podamos modificar el DNS.

    Además de todo esto, comentar una alternativa a “forever”, como es “supervisord”.

    Y no entiendo qué problema puedes haber tenido para ejecutarlo en local, ya que es prácticamente igual :D

    En fin… soy muy quisquilloso XD Pero como dije antes, buen artículo.

    • http://carlosazaustre.es/ Carlos Azaustre

      Hola @magmax9:disqus! Muchísimas gracias por comentar y aportar tu visión. Yo apenas tengo experiencia en el Backend y más allá, lo que más suelo tocar es el Front-End, así que una visión de alguien con más experiencia en ese campo me ayuda mucho y tu comentario es muy útil para mi.

      Nunca había configurado un servidor Apache ni en mis épocas de juventud cuando le daba al PHP+MySQL, por tanto Nginx es un mundo totalmente para mi, además, experiencia en Linux tengo muy poca…

      Comentaba que no me funcionaba en local, porque al configurar el /etc/hosts o mismamente los ficheros de configuración para los dominios y subdominios, si ponia localhost no me funcionaba, si me inventaba un nombre tampoco… andaba un poco perdido. Y después de ver varios tutoriales, para comprobar si de verdad podia hacerlo, lo probé en un entorno de producción.

      Si sabes como configurarlo en local, te estaría muy agradecido que lo compartieses :)

      Un saludo y muchas gracias de nuevo por pasarte por aquí y meterme caña :)

      • http://www.magmax.org/ Miguel Ángel García

        Si metes los cambios del /etc/hosts, debería funcionarte tal cual.

        • Wilberth Loría

          es cierto.

    • Brian Serrano Satizabal

      Hola @magmax9:disqus tienes puntos importantes para resaltar, yo al igual que el autor, poseo poco conocimiento en backend, aunque programo e php y nodejs, puedo decir que tengo ideas vagas de como configurar un servidor apache y no tengo ninguna sobre nginx. Lo otro es que creo que cuando el autor dice “funciona orientado a eventos (como Node)” y vos decís: “Todo funciona orientado a eventos”, estas errado. Cuando se habla de orientado a eventos quiere decir que algo esta en constante escucha esperando a que ocurra alguna cosa para reaccionar, y esto es lo que hace node y sobre ese paradigma fue creado nginx. Mientras que Apache server es transaccional y bloqueante, es decir, espera alguna peticion para atentederla y pone en cola otras peticiones que han llegado en cierto momento, Nginx es un servidor donde espera un evento para ejecutar algo y a su vez no es bloqueante, lo que indica que atiende muchas peticiones al tiempo sin armar pilas.

      Lo otro también es que ningún programador puede definir si “x” o “y” lenguaje es la mejor solución para todo. En el proceso de desarrollo de software es importante elegir las tecnologías adecuadas que se ajusten a las características de la aplicación. Tal vez no exista hoy en día un ejemplo de aplicación robusta basada en Javascript usando el stack MEAN, pero de eso se trata que estos stack de programación evolucionen con el tiempo y el día de mañana podamos tener ejemplos admirables de aplicaciones MEAN.

      Cada quien defiende los lenguajes que usa, encontrándole las ventajas y desventajas de unos con otros.

      • http://www.magmax.org/ Miguel Ángel García

        Show me the code.

        Diseñemos un servidor web. Debe atender un socket en un puerto determinado, que es un recurso único y de uso exclusivo. Por lo tanto, sólo un único proceso puede tenerlo ocupado cada vez. Este proceso tendrá un hilo atendiéndolo y, cuando llegue un mensaje, tendrá distintas opciones:

        1. Atención directa.

        Consiste en que el propio proceso e hilo atienda la llamada. Esto sería un problema, ya que el socket queda desatendido y podrían perderse mensajes.

        2. Lanzar un hilo que lo atienda.

        Se crea un nuevo hilo y se vuelve a proceder a escuchar en el puerto, a la espera de nuevos mensajes.

        3. Lanzar un proceso que lo atienda.

        Se crea un nuevo proceso que atienda el mensaje. Requiere más recursos que lanzar un hilo.

        4. Pool de hilos.

        Se mantiene un pool de hilos y se utiliza una cola para comunicar los hilos con el proceso principal. La llegada de un mensaje se comunica a los hilos y sólo uno de ellos atiende cada mensaje. Si hay más mensajes que hilos, quedarán en la cola.

        5. Pool de procesos.

        Igual que el 4, pero con procesos. Habitualmente se utiliza una cola de mensajes (persistente o no) para manejarlo.

        Si a alguien se le ocurre otra manera, que lo diga.

        Como ves, no hay magia, sólo código. Y todos estos mecanismos funcionan orientados a eventos (ver wikipedia: http://es.wikipedia.org/wiki/Programaci%C3%B3n_dirigida_por_eventos), ya que todos tienen un “bucle de eventos”.

        Las soluciones 4 y 5 tienen un problema: Si los mensajes llegan más rápido de lo que se pueden atender, quedarán encolados y se apreciará la demora. Puede parecer que en las opciones 2 y 3 no es así, pero ocurre lo mismo; la diferencia es que se confía en el sistema operativo para realizar las labores de coordinación entre procesos/hilos. Si llegan más mensajes de los que deben, el sistema operativo podría llegar a utilizar demasiados recursos generando procesos, de manera que también se aprecie la demora.

        No estoy diciendo que nginx sea mejor que apache ni lo contrario, pero puedo demostrar que Apache también está orientado a eventos; aquí tienes su implementación: http://svn.apache.org/repos/asf/httpd/httpd/trunk/server/mpm/event/event.c

        Dices que apache es transacional y bloqueante. Te agradecería que lo demostraras. Si fuera así, no entendería que lo usaran el 60% de los servidores web actuales, ni que Internet tuviera la velocidad actual: http://w3techs.com/technologies/overview/web_server/all

        Yo siempre digo que la mejor tecnología siempre es aquélla que no estás utilizando, ya que siempre encontrarás defectos a la que usas. Yo no defiendo nginx o apache. No defiendo javascript ni java ni python. Defiendo usar las herramientas con cabeza, y ser críticos con lo que se lee y con lo que se opina.

        Disculpad si me he extendido y si he tardado en responder, pero he estado ocupado últimamente y quería comentar todos los puntos.

  • http://www.jpamies.com Jordi Pàmies

    Carlos, Más que muy buen artículo, muy buen blog ;)

    Sólo quería decir que yo uso ngnix para redirigir a node y apache en la empresa, node nos ha dado un rendimiento brutal…

    Lo hacemos un poco distinto, en lugar de usar subdominios utilizamos el location para enviar a un puerto o otro, ya que tenemos varios node.js, una regla para los estáticos y sino lo manda al apache para que sirva la petición de un MVC PHP.

    ej:

    location ~* ^/[1-2].[0-1]/player/alive {
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:3101;
    proxy_read_timeout 90;
    allow all;
    }

  • Wilberth Loría

    Pero quien le enseño hacer eso?? Esta muy bueno lo felicito creo que me servira para utilizar drupal y express en un mismo servidor

  • Julián D. Vásquez G.

    Excelente Articulo. Muchas Gracias por compartir tus conocimientos. Aporto que para utilizarlo con socket.io le cambié un detalle. Salía el error WebSocket connection… gracias a la ayuda de stackoverflow. http://goo.gl/PZ7VPr