Introducción
Node.js es un entorno de ejecución de JavaScript que se ha vuelto popular en los últimos años para crear aplicaciones del lado del servidor.
Este tutorial muestra cómo implementar una aplicación Node.js en un servidor en la nube a través de Docker, Docker Hub y Docker Compose.
Requisitos previos
- Este tutorial asume que tienes Docker instalado en tu sistema local. Si no lo tienes, puedes encontrar instrucciones sobre cómo instalarlo en la documentación oficial.
- También necesitará un servidor en la nube con una distribución de Linux, preferiblemente Ubuntu 24.04. Si usa otra distribución, es posible que deba seguir instrucciones específicas para instalar Docker en el servidor.
- Algunos pasos también requieren una cuenta de Docker Hub (gratuita) para cargar la imagen de Docker para la aplicación.
- Si no tienes experiencia previa con Docker, no te preocupes, este tutorial es muy básico y explica los conceptos centrales detrás de lo que estamos haciendo.
- Necesita una aplicación Node.js que pueda implementar.
Acerca de Docker
Si recién estás comenzando a usar Docker, aquí hay algunos términos que vale la pena revisar para asegurarnos de que estamos en la misma página.
- Imágenes: en Docker, las imágenes son «instantáneas» o plantillas de un sistema de archivos y contienen todo lo necesario para ejecutar una aplicación.
- Contenedores: Son instancias reales de la aplicación en ejecución. Se crean tomando una plantilla (una imagen) y convirtiéndola en algo que se puede iniciar y tiene estado.
- Las capas son los elementos que componen una imagen de Docker. Cada capa se construye sobre otra, lo que permite ofrecer una función llamada almacenamiento en caché de capas. Esto significa que, cuando solo cambia una capa de una imagen, no es necesario reconstruir ni recargar todas las capas.
- Los registros son el lugar donde se suben (push) imágenes para que estén disponibles para todo el mundo o para quienes tengan credenciales de acceso. En este tutorial, usaremos Docker Hub, pero también existen alternativas de GCP, AWS, Azure, GitHub y otras plataformas.
Paso 1: Crear un Dockerfile
Cree un archivo llamado Dockerfile con el siguiente contenido en el directorio raíz de su proyecto Node.js:
FROM node:20.17
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
EXPOSE 8080
CMD [ "node", "src/index.js" ]Un Dockerfile es donde se guardan las instrucciones que permiten a Docker crear una imagen. Cada instrucción representa la creación de una capa que modifica el sistema de archivos de la imagen.
En este caso, creamos nuestra imagen a partir de una plantilla, a veces llamada imagen base, que en este caso es node:20.17. Esta es una imagen oficial proporcionada por Docker y puedes encontrar más información aquí.
El siguiente paso establece la variable de entorno NODE_ENV en producción. El principal efecto es evitar la instalación de paquetes de desarrollo al ejecutar la siguiente instalación de npm, pero a menudo puede generar mejores optimizaciones en los módulos que utiliza.
Con el comando WORKDIR movemos el directorio actual a /app, que a su vez se convierte en el directorio en el que se ejecutan las siguientes instrucciones.
La línea de paquete COPY*.json copia los archivos package.json y package-lock.json a la carpeta /app del sistema de archivos de imagen de Docker. Tenga en cuenta que el punto final es necesario para indicar el directorio actual.
Ahora usamos la directiva RUN para instalar dependencias de producción usando el comando npm ci (ci significa instalación limpia y está diseñado para usarse en entornos automatizados).
Cabe destacar que, hasta ahora, solo copiábamos los archivos package*.json en la compilación, en lugar de copiar todo el directorio del proyecto. Esto nos permite aprovechar el almacenamiento en caché de las capas de Docker, de modo que, si los paquetes dependientes no cambian, las capas se pueden usar sin tener que recompilar.
La siguiente línea (COPY . .) copia los archivos restantes en la imagen.
Opcionalmente, podemos especificar que queremos exponer un puerto de red específico del contenedor para que una aplicación web pueda acceder a él. Tenga en cuenta que la directiva EXPOSE no expone realmente el puerto: como indica la documentación, actúa como un documento entre quien crea la imagen y quien ejecuta el contenedor sobre los puertos que se publicarán. .
Finalmente, la última directiva especifica el comando que Docker debe usar para ejecutar la aplicación al iniciar el contenedor. En este caso, asumimos que el punto de entrada de la aplicación es el archivo index.js.
Suele ser recomendable crear también un archivo llamado .dockerignore junto con tu Dockerfile. Esto garantiza que, al ejecutar COPY..., no se copien en la imagen archivos innecesarios de tu ordenador.
.git
Dockerfile
node_modulesEn este caso, no queremos que las versiones de desarrollo de directorios como git o node_modules estén disponibles en la plantilla que construimos.
Paso 2 – Crea la imagen
Ahora que tenemos un Dockerfile, podemos decirle a Docker que lo use para construir una imagen.
El comando básico para hacer esto se parece al siguiente comando y debe ejecutarse en la carpeta principal del proyecto:
docker build -t myproject .
Si no se completa exitosamente con el error "/bin/sh -c npm ci", reemplace npm ci en el Dockerfile con npm install e intente nuevamente.
La opción -t especifica el nombre de la imagen, en este caso "myproject". Al final de la línea, debe indicarle a Docker que busque el Dockerfile en el directorio actual.
Nota: La primera vez que ejecute la compilación, tardará un tiempo porque Docker necesita descargar todas las capas de la imagen base (en este caso, Node.js 20.17).
Dado que planeamos cargar esta imagen en el registro en línea de Docker Hub (para acceder a ella desde nuestro servidor), necesitamos nombrar la imagen usando una convención de nomenclatura específica.
Entonces el comando anterior se vería así:
docker build -t username/myproject:latest .
Donde "nombre de usuario" es tu nombre de usuario de Docker Hub y "último" es la etiqueta de la imagen. Una imagen puede tener varias etiquetas, por lo que a veces verás un flujo de trabajo similar a este:
docker build -t myproject .
docker tag myproject username/myproject:latest
docker tag myproject username/myproject:20240905Estos comandos crean una imagen y luego la etiquetan con las etiquetas latest y 20240904 (la fecha en que se actualizó este tutorial por última vez).
Docker Hub no elimina las imágenes antiguas por defecto, lo que permite mantener un historial de todas las imágenes que se han subido al registro. La imagen con la etiqueta "más reciente" siempre es la que se creó más recientemente, mientras que las imágenes más antiguas se etiquetan con una fecha.
Paso 3 – Presione la imagen
Ahora que tenemos la imagen, necesitamos subirla al registro. Primero, ejecute el siguiente comando para asegurarse de que su instancia de Docker esté autenticada con Docker Hub:
docker login
Luego ejecute docker push para cargar la imagen con todas las etiquetas.
docker push username/myproject
Si su aplicación es pequeña, este comando debería completarse rápidamente, ya que solo necesita cargar las capas relacionadas con la aplicación Node.js y sus dependencias de JavaScript.
Una vez que tenga una nueva versión de la imagen, deberá ejecutar el comando push nuevamente para asegurarse de que esté cargada en Docker Hub.
Paso 4: Instalar Docker en Ubuntu 24.04
Ahora podemos proceder a instalar Docker y Docker Compose en el servidor. Como se mencionó en los prerrequisitos, se asume que ya tiene configurado un servidor Ubuntu 24.04.
En primer lugar, la instalación de Docker requiere algunas dependencias del sistema que se pueden instalar con los siguientes comandos:
sudo apt-get update
sudo apt-get install ca-certificates curlAhora agregue la clave GPG oficial de Docker y configure un repositorio apt personalizado:
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullPor último, actualice nuevamente el directorio apt e instale Docker Community Edition:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginEl comando anterior también instala Docker Compose, una herramienta que simplifica enormemente la gestión de contenedores y su ciclo de vida.
El último paso útil implica agregar el usuario actual de Ubuntu al grupo Docker para que podamos ejecutar comandos Docker directamente desde él.
Esto se puede hacer fácilmente con el siguiente comando:
sudo gpasswd -a myuser docker
Asegúrese de que todo salió bien ejecutando los siguientes comandos:
docker --version
docker ps
docker compose versionSi no ves ningún error ni advertencia, estás listo.
Paso 5: Ejecutar el contenedor con Docker Compose
Cree un archivo llamado docker-compose.yml con el siguiente contenido en su servidor:
services:
myproject:
container_name: 'myproject'
image: 'username/myproject'
restart: unless-stoppedEste es un archivo Docker Compose muy básico que configura un contenedor llamado myproject según el nombre de usuario/imagen myproject de Docker Hub. Si no especifica una etiqueta, se usará la última por defecto, pero también puede establecer una etiqueta específica si lo desea:
services:
myproject:
container_name: 'myproject'
image: 'username/myproject:20240904'
restart: unless-stoppedFinalmente, el atributo de reinicio indica que el contenedor debe reiniciarse automáticamente en caso de falla, a menos que se detenga manualmente.
Si ejecuta este comando Compose ahora, la imagen de Docker se anulará y, con suerte, su aplicación se ejecutará:
docker compose -f docker-compose.yml up
Este comando crea un contenedor y lo ejecuta. Docker captura la salida del contenedor y la muestra en la consola. Presione Ctrl + C (o CMD + C) y espere unos segundos a que el contenedor se detenga.
Si todo salió bien, ya está listo para ejecutar el contenedor como fantasma, de modo que siga ejecutándose en segundo plano hasta que se detenga. Esto se puede lograr añadiendo la opción -d al comando:
docker compose -f docker-compose.yml up -d¡Boom, nudo! (Oh, lo digo en serio)
Asegúrese de consultar la documentación de referencia del archivo Compose, donde encontrará funciones útiles como la asignación de puertos de red entre el servidor y el contenedor. Aquí tiene un ejemplo rápido que asigna el puerto externo 80 al puerto interno 8080:
services:
myproject:
container_name: 'myproject'
image: 'username/myproject'
restart: unless-stopped
ports:
- '80:8080'Paso 6 – Instalar una nueva versión
Supongamos que necesita publicar un cambio en su aplicación. A menos que tenga habilitadas las compilaciones automatizadas, deberá repetir los pasos 2 y 3 hasta que aparezca una nueva imagen en Docker Hub.
Luego, en tu servidor, debes extraer manualmente la nueva imagen, de la siguiente manera:
docker compose -f docker-compose.yml pull
Y reinicia el contenedor con la nueva imagen:
docker compose -f docker-compose.yml up -d --force-recreate
Resultado
¡Genial, lo lograste! Esa fue la introducción básica a la implementación de una aplicación Node.js en Ubuntu 24.04 con Docker, Docker Hub y Docker Compose.
Vimos cómo escribir un Dockerfile simple, cómo construir la imagen, enviarla e implementarla en el servidor.
Docker ofrece mucho más de lo que se explica en este tutorial, así que asegúrese de consultar la documentación de Docker y Docker Compose para obtener más información sobre los conceptos y las características.









