Despliegue de contenedores NestJS
Simplifica el despliegue de Node.js/Nest.js: guía paso a paso con Docker y Google Cloud
Una guía corregida sobre contenedores multietapa de Node.js y NestJS, artefactos inmutables y la elección entre Docker Hub y Cloud Build para entregar servicios en Cloud Run.
8 min de lecturaPor Abdullah Raheel
Desplegar un servicio Node.js o NestJS
Esta es la ruta de despliegue que usé en 2023 para un microservicio NestJS: crear una imagen Docker, publicarla desde GitHub Actions y desplegarla en Cloud Run. He corregido las partes de la guía original que hoy no llevaría a producción.
Docker empaqueta el entorno de ejecución, las dependencias y la aplicación en una sola imagen. Reduce las diferencias entre los entornos local, de CI y de Cloud Run, aunque la configuración y la infraestructura todavía pueden variar.
Este recorrido cubre tres etapas: crear y probar la imagen, publicarla mediante GitHub Actions y Docker Hub, y desplegarla en Google Cloud con revisiones manuales o despliegue continuo.
Requisitos previos
- Instala Docker antes de iniciar la compilación.
- En Windows, instala WSL (Subsistema de Windows para Linux) para que Docker pueda usar el entorno Linux necesario.
- Si la instalación de WSL falla con el error 0x80370114, habilita las funciones de virtualización necesarias antes de continuar.
Etapa 1: crear la imagen Docker
La guía original de 2023 usaba una sola etapa de Node 18 Alpine, instalaba las dependencias de producción y ejecutaba la compilación de Nest en esa misma imagen. Ese orden no ofrece una compilación fiable de NestJS: TypeScript y otras herramientas de compilación suelen estar en devDependencies. La imagen corregida separa la compilación del entorno de ejecución, para que la compilación disponga de todas sus herramientas sin enviarlas a producción.
FROM node:18-alpine AS build
WORKDIR /usr/src/app
# Copy manifests first so dependency layers can be reused.
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /usr/src/app/dist ./dist
EXPOSE 8080
USER node
CMD ["node", "dist/main.js"]La etapa de compilación usa npm ci, que requiere un archivo de bloqueo guardado en el repositorio, y compila con las versiones de Nest y TypeScript fijadas por el proyecto. La etapa de ejecución recibe las dependencias de producción y la salida compilada, y después abandona los privilegios de root mediante el usuario node de la imagen.
Dockerfile
.dockerignore
node_modules
npm-debug.log
distLos archivos ignorados reducen el contexto de compilación de Docker y evitan copiar en la imagen las dependencias del host o un directorio dist ya existente.
Ejecutar e inspeccionar el contenedor localmente
Crea la imagen y confirma que Docker la generó.
docker build --tag nest-microservice .
docker imagesInicia un contenedor en segundo plano y asigna su puerto interno 8080 al puerto local 5000. Revisa docker ps, llama al punto de comprobación de estado HTTP e inspecciona docker logs antes de publicar la imagen.
docker run -d --publish 5000:8080 --name nest-container nest-microservice
docker ps
docker ps --all
docker stop nest-containerCuando la imagen y el contenedor funcionen localmente, envía Dockerfile y .dockerignore al repositorio de GitHub.
Etapa 2: publicar la imagen con GitHub Actions
La integración continua requiere un repositorio de GitHub con el código de Node.js o NestJS y una cuenta de Docker Hub. Crea un token de acceso personal de Docker Hub y guarda el nombre de la cuenta y el token como secretos del repositorio llamados DOCKERHUB_USERNAME y DOCKERHUB_TOKEN.
El flujo de trabajo de 2023 que aparece abajo se ejecutaba con cada envío a main, extraía el código, iniciaba sesión en Docker Hub, habilitaba Buildx, compilaba el Dockerfile raíz y publicaba abdullahraheel/nest-microservice:latest. Se conserva como implementación original, incluidas las versiones históricas de sus acciones.
name: ci
on:
push:
branches:
- "main"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/nest-microservice:latest- Guarda y registra el flujo de trabajo en el repositorio.
- Envía el cambio a main para iniciar el flujo de trabajo.
- Inspecciona la ejecución de Actions y diagnostica el paso exacto si falla una tarea.
- Comprueba que el repositorio y la etiqueta publicados aparecen en Docker Hub.
Etapa 3: desplegar la imagen en Google Cloud Run
Crea un proyecto de Google Cloud, abre Cloud Run y crea un servicio. Para un despliegue manual, indica la imagen de Docker Hub como username/repository:tag, elige la región y los ajustes del servicio y crea la revisión. La imagen de ejemplo es abdullahraheel/nest-microservice:latest; usa el repositorio y la etiqueta exactos que produzca tu propio flujo de trabajo.
Conectar Cloud Build para el despliegue continuo
- Elige Create Service y selecciona la opción para configurar el despliegue continuo con Cloud Build.
- Selecciona el proveedor del repositorio y el repositorio.
- Elige la rama de despliegue; en este ejemplo se usa main.
- Selecciona Dockerfile como tipo de compilación e indica su ruta.
- Guarda la configuración y crea el servicio.
Una vez activa la conexión, los cambios fusionados en main hacen que Cloud Build compile el repositorio y vuelva a desplegar el servicio automáticamente. Es una alternativa a desplegar la imagen de Docker Hub producida en la etapa 2, no otro consumidor del mismo artefacto.
Resumen y referencias
- Compila el proyecto NestJS o Node.js como imagen Docker y prueba el contenedor localmente.
- Elige una ruta de entrega: publicar una imagen versionada mediante GitHub Actions o permitir que Cloud Build compile el repositorio conectado.
- Despliega manualmente una revisión exacta de la imagen o usa la ruta conectada de Cloud Build para crear revisiones automáticas del servicio.
- Usa un programador externo o un control de instancia única para el trabajo programado; una instancia web de Cloud Run no es por naturaleza un único trabajador cron duradero.

