Ir al contenido

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

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
dist

Los 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 images

Inicia 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-container

Cuando 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
  1. Guarda y registra el flujo de trabajo en el repositorio.
  2. Envía el cambio a main para iniciar el flujo de trabajo.
  3. Inspecciona la ejecución de Actions y diagnostica el paso exacto si falla una tarea.
  4. 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

  1. Elige Create Service y selecciona la opción para configurar el despliegue continuo con Cloud Build.
  2. Selecciona el proveedor del repositorio y el repositorio.
  3. Elige la rama de despliegue; en este ejemplo se usa main.
  4. Selecciona Dockerfile como tipo de compilación e indica su ruta.
  5. 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

  1. Compila el proyecto NestJS o Node.js como imagen Docker y prueba el contenedor localmente.
  2. Elige una ruta de entrega: publicar una imagen versionada mediante GitHub Actions o permitir que Cloud Build compile el repositorio conectado.
  3. Despliega manualmente una revisión exacta de la imagen o usa la ruta conectada de Cloud Build para crear revisiones automáticas del servicio.
  4. 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.