Volver al blog

Cómo desplegar una aplicación Node.js (Express.js) con Docker en Ubuntu 20.04

Cómo desplegar una aplicación Node.js (Express.js) con Docker en Ubuntu 20.04

Introducción

Docker es una plataforma de contenedores que constituye un entorno estandarizado, ligero, virtualizado, portátil y definido por software. Permite que el software se ejecute de forma aislada de otros programas que se ejecutan en la máquina host física. Docker es un componente definitorio del aspecto de Desarrollo e Integración Continuos del Desarrollo de Software. Ofrece una alternativa ligera a las máquinas virtuales y permite a los desarrolladores disfrutar de arquitecturas de aplicaciones distribuidas. Para obtener una perspectiva completa del ecosistema de Docker, consulte este artículo.

El proceso de creación de una aplicación con Docker comienza cuando un desarrollador crea una imagen para su aplicación. Luego, la imagen se desplegará dentro de un contenedor. La imagen contiene los componentes definitorios de una aplicación, como el código de la aplicación, las bibliotecas, los archivos de configuración, las variables de entorno y el entorno de ejecución. La imagen estandariza el entorno dentro de un contenedor, lo que otorga a la contenedorización sus características de portabilidad. Node.js es un entorno de ejecución de JavaScript en el backend, de código abierto y multiplataforma, que puede ejecutar código JavaScript fuera de un navegador web. Está construido sobre el motor motor de JavaScript V8. Express.js es un framework de JavaScript minimalista para el backend que se ejecuta sobre Node.js.

En este tutorial, crearemos una imagen para un sitio web que se ejecuta en el framework Express. Utilizaremos Bootstrap, que es una biblioteca de frontend, para mejorar el aspecto de la interfaz. Una vez que hayamos creado la imagen, crearemos un contenedor y lo subiremos a Docker Hub. Docker Hub permite a los desarrolladores alojar aplicaciones contenedorizadas para un despliegue sencillo en cualquier entorno Docker. Una vez que su contenedor esté alojado en Docker Hub, lo descargaremos y crearemos otra imagen que realmente servirá nuestro sitio web.

Requisitos previos

Este será un tutorial práctico. Debe crear un entorno que le permita seguir los pasos.

Paso 1: Configurar las dependencias de la aplicación

Debe crear el código fuente de su aplicación antes de poder crear la imagen. El código fuente de la aplicación incluye el código, el contenido estático y las dependencias que se copiarán al contenedor. Comience creando un directorio para su proyecto en el directorio personal del usuario no root. Lo llamaremos node_express, pero es libre de usar el nombre de directorio que prefiera:

A continuación, acceda a este directorio:

Este será el directorio raíz de su aplicación. Una aplicación de node.js espera un archivo package.json en la carpeta raíz. Npm utiliza este archivo para determinar qué dependencias necesita su aplicación. Introduzca el siguiente comando para crear este archivo:

Después de eso, añada el siguiente fragmento de código al archivo. Puede actualizar el nombre, el autor, la descripción y el archivo del punto de entrada como desee:

Como puede ver, este archivo especifica el nombre del proyecto, la versión, el autor y la licencia bajo la cual se compartirá el código de la aplicación. Se recomienda utilizar un nombre corto y descriptivo para su proyecto para evitar duplicados en el registro de npm. Hemos especificado la licencia ISC para el proyecto, la cual permite copiar, modificar o distribuir libremente el código de la aplicación.

Lo más importante es que debe tener en cuenta las siguientes directivas en el archivo:

  • main”: esta directiva especifica el punto de entrada de la aplicación, que establecemos como index.js. Crearemos este archivo en breve.
  • dependencies”: esta directiva especifica las dependencias de la aplicación que se obtendrán del registro de npm cuando ejecutemos el comando npm, en nuestro caso, queremos Express versión 4.17.1 y superior.

Ahora puede guardar el archivo presionando Ctrl + O. Luego, cierre el archivo presionando Ctrl + X. A continuación, instalaremos las dependencias ejecutando el siguiente comando:

El comando instala las dependencias de la aplicación especificadas en el archivo package.json dentro de los directorios node_modules. Se crearon automáticamente la primera vez que ejecutó el comando. Con las dependencias de nuestra aplicación instaladas, ahora puede comenzar a agregar el código de la aplicación.

Step 2: Adding Your Application Code Files

Crearemos un sitio web de recetas básico, cortesía de allrecipes. El punto de entrada principal para la aplicación es el archivo index.js . Agregaremos un directorio views que contendrá las distintas páginas y recursos estáticos del proyecto. El sitio web tendrá una página de inicio que contendrá información introductoria y enlaces a algunas recetas.

El código de nuestra página de inicio se colocará en el archivo home.html. Primero, cree el archivo index.js ingresando el siguiente comando:

Agregue el siguiente código, que importa y crea una aplicación Express. También especifica el objeto Router, el directorio base y el puerto en el que se servirá esta aplicación:

require es una función de JavaScript que carga un módulo. En este caso, estamos cargando el módulo express. Luego, utilizaremos el módulo importado para crear los objetos express y router. El objeto router realiza las funciones de enrutamiento de la aplicación al responder a las llamadas de métodos HTTP que agregaremos a este objeto a medida que avancemos en el tutorial.

También hemos establecido path y port. La constante path define el directorio base para el código. En nuestro caso es el subdirectorio views dentro del directorio raíz del proyecto. El port especifica el puerto en el que la aplicación express debe escuchar, en nuestro ejemplo, lo hemos establecido en 8090.

Una vez que tenemos las constantes, podemos especificar algunas rutas para la aplicación utilizando el objeto router . Agregue el siguiente código al archivo index.js para especificar las rutas:

Puede agregar middleware a las rutas utilizando la función router.use. En este caso, agregamos una función que registra las solicitudes del enrutador antes de pasarlas a las rutas de la aplicación. Una solicitud GET a la base de la aplicación devolverá un home.html página. Luego, hemos agregado páginas para tres recetas que también se recuperarán mediante la solicitud GET a la página de la receta específica.

Finalmente, agregue el siguiente código para montar el router middleware y los activos estáticos de la aplicación. Además, indique a la aplicación express que escuche en el puerto 8090:

Su archivo index.js completo debería verse así:

Ya puede guardar y cerrar el archivo. El siguiente paso es agregar las páginas web estáticas al directorio views. Comience ingresando el siguiente comando para crear el directorio:

Ingrese el siguiente comando para abrir el archivo home.html de la página de destino:

Agregue el siguiente código al archivo. El código importa Bootstrap y ofrece a los visitantes del sitio web información sobre de qué se trata el sitio web:

Además de importar Bootstrap, la página también añade un menú de navegación básico para ayudarnos a movernos por las páginas y volver a la página de inicio. También añadimos una línea para importar nuestro archivo CSS personalizado:

Utilizaremos este archivo para añadir estilos personalizados a la aplicación más adelante. Ahora, vamos a crear las tres páginas para las recetas. Primero comenzaremos creando la página de lasaña. Abre el archivo con el editor nano usando el siguiente comando:

En el archivo abierto, añade el siguiente código. Este archivo importará Bootstrap, el archivo custom.css, especificará un menú de navegación y ofrecerá información sobre la receta de lasaña:

Sigamos el mismo proceso para crear un archivo para la página de la receta de guacamole. Abre el archivo con nano ejecutando el siguiente comando:

Luego agrega este código al archivo:

Finalmente, creemos el archivo banana_bread.html ingresando el comando:

Luego, agrega el siguiente código HTML al archivo:

Ahora, hemos creado todas las páginas. Si recuerdas, debemos agregar el archivo css/custom.css. Introduce el siguiente comando para crear el directorio:

Luego crea y abre el archivo en el editor nano con el comando:

Puedes agregar más códigos CSS para dar estilo a tu sitio web como desees. Por brevedad, agreguemos el siguiente fragmento de código al archivo:

Guarda y cierra el archivo cuando hayas terminado.

Puedes iniciar la aplicación ya que ahora tenemos el código fuente de la aplicación y las dependencias del proyecto instaladas.

Habíamos configurado la aplicación para escuchar en un puerto 8090, ejecuta el siguiente comando para indicar al firewall que permita el tráfico a través de este puerto. Si habías especificado un puerto diferente, reemplaza el número de puerto en el comando:

Ahora, puedes iniciar la aplicación. Pero primero, asegúrate de estar en el directorio raíz del proyecto ejecutando el siguiente comando:

Inicia la aplicación con node index.js. Si especificaste un punto de entrada diferente, reemplázalo con tu punto de entrada:

Si navegas con tu navegador a http://your_public_server_ip:8090, verás la página de inicio de Recetas tal como se definió:

Recipes

 

Puedes ver los enlaces a las distintas recetas en la navegación. Hagamos clic en algunos. A continuación tenemos la página de la receta de Lasaña:

Node.js app install on Ubuntu 1

Y aquí tenemos la página de la receta de Guacamole:

Guacamole

Hasta este punto, has creado tu aplicación y has comprobado que funciona como se esperaba. Puedes salir del servidor presionando Ctrl + C y continuar con la creación del Dockerfile. Los Dockerfiles ayudan en la escalabilidad al hacer posible recrear una instancia de la aplicación cuando sea necesario.

Paso 3: Creación del Dockerfile

Docker lee las instrucciones especificadas en un Dockerfile al compilar imágenes. Especifica el entorno de ejecución de una aplicación. Por lo tanto, ayuda a los desarrolladores a evitar discrepancias con las dependencias o cambios en las versiones del entorno de ejecución. Introduzca el siguiente comando para crear el Dockerfile:

Una imagen de Docker se crea utilizando varias capas de imágenes que se construyen unas sobre otras. Se empieza añadiendo una imagen base para formar el punto de partida de la aplicación.

Dado que la aplicación espera ejecutarse en un entorno node.js, empezaremos añadiendo la imagen node:10-alpine para node.js. Actualmente, mientras escribimos este tutorial, esta es la versión LTS recomendada de Node.js. Elegimos esta imagen específica porque se deriva del proyecto Alpine Linux. Por lo tanto, ayudará a mantener el tamaño de nuestra imagen al mínimo. Hay varias variantes de imágenes en la página de imágenes de Node en Docker Hub entre las que puede elegir según sus necesidades.

Añada el siguiente código para establecer la imagen base de la aplicación utilizando la FROM directiva:

Esta imagen incluye Node.js y npm. Cada Dockerfile debe comenzar con una directiva FROM. La imagen node de Docker viene por defecto con un usuario node que no es root y que puede utilizar para ejecutar el contenedor de su aplicación como root. La seguridad de Docker recomienda no ejecutar los contenedores como root y restringir los privilegios únicamente a los necesarios para ejecutar sus recursos.

En ese caso, utilizaremos el directorio de inicio del usuario node como directorio de trabajo para la aplicación, así como el usuario dentro del contenedor. Puede consultar esta guía de mejores prácticas para la imagen de Node en Docker para obtener más información.

Crearemos el subdirectorio node_modules dentro de /home/node junto con el directorio de la aplicación para ayudar a simplificar los permisos del código de la aplicación. La creación de estos directorios garantiza que tengan los permisos correctos cuando ejecutemos el comando npm install localmente dentro de los contenedores. Una vez creados los directorios, debe asignar la propiedad de los mismos al usuario node. Haremos esto dentro del Dockerfile añadiendo la siguiente línea:

A continuación, establecerá el directorio de trabajo añadiendo la siguiente línea:

Es una buena idea establecer siempre el WORKDIR para que Docker no tenga que crear uno por defecto.

Añada la siguiente línea para copiar los archivos package.json y package-lock.json :

Se recomienda añadir la instrucción COPY antes de ejecutar npm install o de copiar el código fuente de la aplicación. Esto le permite aprovechar el mecanismo de almacenamiento en caché de Docker. Durante el proceso de compilación, Docker comprueba si tiene una capa almacenada en caché para cada instrucción. Esto significa que si no ha cambiado el archivo package.json, Docker utilizará la capa de imagen existente y evitará reinstalar los node modules, lo que agiliza los procesos de compilación.

Antes de ejecutar npm install, añada la siguiente línea para cambiar el usuario a node para garantizar que todos los archivos de la aplicación y el directorio node_modules sean propiedad del usuario node que no es root:

Nuestro contenedor ya está listo para ejecutar el comando npm install. Añada la siguiente línea al Dockerfile:

Una vez que se hayan instalado los node_modules, añada la siguiente línea que le indicará a Docker que copie el código de la aplicación en el directorio de la aplicación en el contenedor con los permisos y la propiedad correctos, es decir, el usuario node que no es root:

El último paso es exponer el puerto 8090 en el contenedor, tal como habíamos definido en nuestro archivo de entrada index.js :

EXPOSE establece qué puertos del contenedor estarán abiertos en tiempo de ejecución. CMD ejecuta el comando para iniciar la aplicación, en este caso, node index.js.

Solo debe tener un comando CMD en el Dockerfile, ya que solo el último surte efecto. Consulte la documentación de referencia de Dockerfile para obtener una lista de cosas que puede hacer con Dockerfile.

Su Dockerfile completo debería verse así:

Ahora puede guardar y cerrar el archivo.

Lo siguiente que debe hacer es agregar el archivo .dockerignore. Al igual que el archivo .gitignore, el archivo .dockerignore especifica qué archivos y directorios dentro del directorio del proyecto no deben copiarse al contenedor.

Abra el archivo con el editor nano:

Agregue las siguientes líneas dentro del archivo:

Si está trabajando con un repositorio git, también debería agregar el directorio .git y el archivo .gitignore. Guarde y cierre el archivo.

Si todo ha ido bien, es hora de compilar la imagen de la aplicación utilizando el comando docker build. Puede agregar la bandera –t al comando docker build para etiquetar la imagen con un nombre fácil de recordar, en lugar de la cadena aleatoria que Docker establece de forma predeterminada. También subiremos la imagen a Docker Hub, por lo que es mejor incluir su nombre de usuario de Docker Hub en la etiqueta.

Usaremos nodejs-express-image como nombre de etiqueta. Es libre de elegir el nombre de etiqueta que desee. Aquí está el comando para compilar la imagen:

Recuerde reemplazar your_dockerhub_username con su nombre de usuario real de Docker Hub. El . (punto) al final especifica que el contexto de compilación es el directorio actual.

El proceso de compilación tarda un minuto o dos. Una vez que haya terminado, ingrese el comando para verificar sus imágenes:

Debería ver algo como esto:

sudo docker images

Recuerde que reemplazamos your_dockerhub_username con un nombre de usuario real.

Después de confirmar que su imagen se ha compilado, ahora puede crear un contenedor con la imagen usando docker run. Se incluirán las siguientes banderas:

  • -p: publica el puerto en el contenedor y lo asigna a un puerto en el sistema host. Usaremos el puerto 80 en el sistema host para fines de demostración. Sin embargo, si tiene otro proceso ejecutándose en ese puerto, no dude en modificarlo según sea necesario. Obtenga más información sobre la vinculación de puertos en la documentación de Docker.
  • -d: para el modo desatendido (detached). Permite que el contenedor continúe ejecutándose en segundo plano.
  • --name: puede usar esto para establecer un nombre fácil de recordar en lugar de dejar que Docker asigne una cadena aleatoria.

El comando para compilar el contenedor es el siguiente. Reemplace su nombre de usuario de Docker Hub de manera adecuada:

Espere a que el contenedor se compile y comience a ejecutarse. Puede usar este comando para inspeccionar todos los contenedores en ejecución:

Debería ver una salida similar a la siguiente:

Node.js app install on Ubuntu 3

Como se ve en la salida, el contenedor ahora está en ejecución. Puede verlo en el navegador si visita la dirección IP pública de su servidor sin el puerto en el navegador. Se cargará su página de inicio:

awesome recipe

 

Ha implementado con éxito un sitio web estático de Node Express con Docker. Veamos cómo podemos subir esta imagen a Docker Hub para su uso futuro y con fines de escalado.

Paso 4: Trabajar con repositorios de imágenes de Docker

Puede subir sus imágenes a registros de imágenes como Docker Hub y guardarlas para su uso futuro, compartirlas con otros desarrolladores o permitir el escalado de sus contenedores. Podemos subir la imagen que creamos a Docker Hub y usarla para recrear un contenedor.

Use el siguiente comando para iniciar sesión en su cuenta de Docker Hub. Reemplácelo con su nombre de usuario real de Docker Hub:

Introduzca su contraseña cuando se le solicite. Una vez que haya iniciado sesión, se creará un ~/.docker/config.json archivo en el directorio de inicio de su usuario que contiene sus credenciales de Docker Hub.

Con eso configurado, introduzca el siguiente comando para subir la imagen a Docker Hub, especificando la etiqueta que estableció al compilar la imagen anteriormente:

Este comando sube la imagen de Docker a su cuenta de Docker Hub. Si visita su cuenta, podrá ver su imagen recientemente subida:

Docker Hub

Podemos probar la utilidad del repositorio de imágenes destruyendo el contenedor de la aplicación actual y volviéndolo a compilar usando la imagen del repositorio.

Enumere sus contenedores actuales introduciendo el comando:

Debería ver una salida similar a esta:

Docker Hub

Tome nota del CONTAINER ID que aparece en su salida, cópielo y úselo para detener su contenedor con el comando, reemplazando el ID con el suyo:

Introduzca el siguiente comando para enumerar todas las imágenes de Docker disponibles en su sistema:

La salida mostrará el nombre de su imagen, la imagen de node.js y otras imágenes del proceso de compilación.

Introduzca el siguiente comando para eliminar las imágenes, incluidas las imágenes no utilizadas o huérfanas:

Escriba y para confirmar. Esto elimina los contenedores y las imágenes detenidos. Si los enumera, verá una lista vacía en la salida:

output

Ahora, ha eliminado tanto el contenedor que ejecuta la aplicación como la propia imagen. Obtenga más información sobre cómo eliminar contenedores, imágenes y volúmenes de Docker siguiendo nuestro tutorial.

Ahora podemos recrear todo el proceso descargando primero la imagen de Docker Hub con el siguiente comando. Reemplace su nombre de usuario de Docker Hub de manera adecuada:

Enumere sus imágenes de Docker nuevamente con el comando:

Debería ver la imagen en la salida:

sudo docker

Ahora puede volver a compilar su contenedor usando el comando del Step 3. Por supuesto, reemplace su nombre de usuario de Docker Hub donde corresponda:

Enumere sus contenedores para confirmar que se ha vuelto a compilar:

Debería ver una salida similar:

En su navegador, navegue a la dirección IP pública de su servidor y debería ver su aplicación ejecutándose.

Conclusión

Si ha seguido el tutorial hasta este punto, ahora tiene un sitio web estático creado con Express y Bootstrap, y desplegado con Docker. Utilizó los archivos del sitio web estático para compilar una imagen de Docker y usó la imagen para crear un contenedor. Luego subió la imagen a un registro de imágenes de Docker, Docker Hub, haciéndola disponible para su uso futuro o escalado. Para probar el uso del registro de imágenes, destruyó las imágenes y los contenedores, descargó las imágenes del registro y volvió a compilar los contenedores.

Este tutorial explicó cómo desplegar una aplicación de Node.js. Si desea aprender a usar un entorno de desarrollo web diferente, tenemos un tutorial sobre Cómo desplegar una aplicación de Laravel con Docker Compose en Nginx.

Para obtener más recursos sobre cómo utilizar Docker, consulte los siguientes tutoriales:

¡Feliz informática!

author

Hark Labs

Autor · CloudSigma

Preslav Dobrev es diseñador creativo en CloudSigma, centrado en una identidad empresarial coherente mediante el uso de canales de marketing tradicionales e innovadores. Es experto en fusionar la visión artística con el marketing estratégico para crear narrativas de marca impactantes.

Comentarios

Aún no hay comentarios. Sea el primero.