Herramientas de usuario

Herramientas del sitio


clase:daw:daw-proyecto:tema07

7. Despliegue en el proyecto

Objetivo del tema: /git-flow publish = producción actualizada, sin entrar nunca al servidor. Y en cada push, GitHub compila y pasa todos los tests. develop no se despliega en ningún sitio: es local.

Lo que ya sabéis: qué es GitHub Actions y un primer workflow con VPN y SSH, en Despliegue 5; cómo funciona nginx-proxy con VIRTUAL_HOST, en Despliegue 4.c. Lo nuevo aquí es sustituir la VPN y el SSH por un runner dentro del servidor.

El servidor

Un VPS compartido por toda la clase, con tres cosas montadas por el profesor:

  • Docker.
  • nginx-proxy en la red nginx-proxy-network, escuchando en el 80. Cada aplicación se publica sola por su VIRTUAL_HOST: <alumno>.proyectosdaw.cip.fpmislata.com, que ya está en compose.yaml desde el tema 4.
  • Un runner autoalojado de GitHub por alumno, registrado contra su repositorio. Un runner es un proceso que espera trabajos de GitHub Actions y los ejecuta en la máquina donde está instalado. Como está en el servidor, un workflow que se ejecute en él puede hacer docker compose directamente: ni SSH, ni VPN, ni registro de imágenes. El profesor lo registra con el token que da GitHub en Settings → Actions → Runners → New self-hosted runner; es la única vez que alguien entra al servidor.
Todos los runners comparten el Docker del servidor, así que con docker ps cada uno ve los contenedores de los demás. Es un servidor de clase y se asume. Lo que separa a cada alumno es el nombre de proyecto de Compose (el del repositorio) y su subdominio.

Los mismos scripts

En el servidor no hay nada nuevo que aprender: la aplicación se opera con el mismo script del tema 4, app.sh, con sus cuatro subcomandos. Lo único que cambia es una variable de entorno que fija el workflow:

COMPOSE_FILE=compose.yaml

  • Con COMPOSE_FILE=compose.yaml Compose carga solo producción: el compose.override.yaml de desarrollo no se aplica, así que ni ng serve, ni puertos publicados, ni datos de prueba.
  • app.sh start hace up -d –build –wait: construye la imagen con el código del commit, sustituye los contenedores que han cambiado y espera a los healthcheck. Nunca hace down -v: el volumen db-data se queda y un despliegue nunca borra la base de datos; Flyway aplica las migraciones nuevas al arrancar.
  • Los workflows solo llaman a app.sh, así que lo mismo que hace GitHub se puede lanzar a mano desde el servidor si un día hace falta.

Secretos

La contraseña de la base de datos de producción no está en ningún fichero: está en Settings → Secrets and variables → Actions del repositorio como DB_PASSWORD. El workflow la pasa como variable de entorno a app.sh y Compose la lee en ${DB_PASSWORD}. En desarrollo esa misma variable viene del .env del worktree.

Los workflows

Un solo fichero para CI y CD:

name: CI/CD

on:
  push:
    branches: [develop, main]
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: 25 }
      - uses: actions/setup-node@v4
        with: { node-version: 22 }

      - name: Levantar la aplicación
        env: { DB_PASSWORD: ci }
        run: |
          docker network create nginx-proxy-network
          docker compose up -d --build --wait

      - name: Tests del backend (unitarios y ArchUnit)
        working-directory: backend
        run: mvn -B test

      - name: Tests de API y E2E
        working-directory: frontend
        run: |
          npm ci
          npx playwright install --with-deps chromium
          npm run test:api
          npm run test:e2e

  deploy:
    needs: ci
    if: github.ref == 'refs/heads/main'
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Desplegar
        env:
          COMPOSE_FILE: compose.yaml
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
        run: ./app.sh start
      - name: Smoke test
        run: curl -fsS http://<alumno>.proyectosdaw.cip.fpmislata.com/api/clientes > /dev/null

Lo que hay que entender:

  • ci corre en un runner de GitHub (ubuntu-latest), en cada push a develop y a main. Levanta la aplicación con el mismo compose.yaml + compose.override.yaml del portátil (sin .env: valen los puertos por defecto y el perfil datos-prueba), y pasa los tests con los mismos comandos que en el worktree: mvn test, npm run test:api y npm run test:e2e. –wait espera a los healthcheck.
  • deploy corre en el runner del servidor (self-hosted), solo en main y solo si ci ha pasado (needs). Hace checkout del commit y ./app.sh start con COMPOSE_FILE=compose.yaml: la imagen se construye en el servidor con ese código.
  • El smoke test es el último paso: una petición a la URL de producción que tiene que responder 200. Si el despliegue ha dejado la aplicación caída, el workflow sale en rojo y se ve en el acto.
  • workflow_dispatch permite relanzarlo a mano desde la pestaña Actions (siempre sobre main).

Y dos workflows pequeños de operación, status.yml y logs.yml, solo con workflow_dispatch y un paso ./app.sh status o ./app.sh logs (con el mismo COMPOSE_FILE) en self-hosted. Son la forma de ver qué pasa en producción sin acceso al servidor: se lanzan desde Actions y la salida queda en el log del workflow.

La IA y los workflows

El YAML lo escribe el agente a partir de lo de arriba. Y no hay skill para cuando CI sale en rojo, porque no debería salir en rojo: pasa los mismos comandos que /git-flow finish acaba de pasar en el worktree. Si sale, es que algo distingue el runner del portátil (un fichero que no está en git, una variable, un puerto) o que ha fallado el despliegue o el smoke test. Un workflow rojo tiene un log largo y la causa está en tres líneas: se abre el paso fallido en Actions, se copia su log y se le pregunta al agente en una conversación nueva. Desde la terminal, gh run view –log-failed da solo los pasos que han fallado, que es lo que hay que pegarle (tema 5: no pegarle logs enteros).

Ejercicios

Ejercicio 1

  • Crea un issue «CI» e inícialo.
  • Comprueba que app.sh funciona como lo va a ejecutar el servidor, sin .env y solo con producción: COMPOSE_FILE=compose.yaml ./app.sh start desde el repositorio principal, ./app.sh status y luego ./app.sh stop.
  • Añade el secreto DB_PASSWORD en Settings → Secrets and variables → Actions.
  • Escribe .github/workflows/ci-cd.yml solo con el job ci. Finaliza el issue y mira en Actions cómo se ejecuta en el push a develop. Si sale en rojo, arréglalo hasta que esté en verde.

Ejercicio 2

  • Pide al profesor que registre tu runner en el servidor. Comprueba en Settings → Actions → Runners que aparece como Idle.
  • Crea un issue «Despliegue» e inícialo. Añade el job deploy al workflow con el needs: ci, el if sobre main y el smoke test contra tu URL.
  • Finaliza el issue y /git-flow publish. Sigue la ejecución en Actions y comprueba que la aplicación responde en http://<alumno>.proyectosdaw.cip.fpmislata.com.

Ejercicio 3

  • Añade status.yml y logs.yml con workflow_dispatch sobre self-hosted.
  • Lánzalos desde Actions y lee la salida de app.sh status en el log.

Ejercicio 4

  • Cambia a propósito la URL del smoke test por una que no existe, finaliza el issue y publica. CI pasa y deploy sale en rojo. Copia el log del paso fallido, pregúntaselo al agente y comprueba que señala la URL. Arréglala y vuelve a publicar.
  • Haz un cambio visible en la aplicación, finaliza y publica. Comprueba en producción que el cambio está y que los datos que había siguen estando.

Fuera de este tema, por ser de un sistema real y no de un proyecto de clase: entornos de dev y pre, copias de seguridad, copiar datos de producción, tareas programadas y HTTPS (el proxy sirve HTTP).

clase/daw/daw-proyecto/tema07.txt · Última modificación: por claude