Saltar al contenido principal

TP12.2 - Pipeline CI/CD y Despliegue

Objetivos del TP​

En este TP, usted va a:

  1. ✅ Crear un workflow completo de GitHub Actions
  2. ✅ Automatizar pruebas, build y publicación Docker
  3. ✅ Desplegar en Railway (o alternativa)
  4. ✅ Configurar el despliegue automático

Duración estimada: 1h30

Prerrequisitos: TP12.1 terminado, cuenta de GitHub, cuenta de Railway


Contexto​

Usted automatiza el despliegue de la aplicación Todo del TP12.1. Cada push en main dispara:

  1. Pruebas automáticas
  2. Build de la imagen Docker
  3. Publicación en Docker Hub
  4. Despliegue en Railway

Parte 1: Preparación​

Estructura de Archivos​

.github/
└── workflows/
├── ci.yml # Pruebas en cada PR
├── cd.yml # Build + Deploy en main
└── docker-publish.yml # Publicación Docker

Crear los Secrets de GitHub​

Ir a: Repository → Settings → Secrets and variables → Actions

# Docker Hub
DOCKERHUB_USERNAME=su-usuario
DOCKERHUB_TOKEN=su-token

# Railway
RAILWAY_TOKEN=su-token

# (Opcional) Heroku
HEROKU_API_KEY=su-api-key
HEROKU_APP_NAME=su-app
HEROKU_EMAIL=su-email

Parte 2: Workflow CI (Pruebas)​

.github/workflows/ci.yml​

name: CI - Tests

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

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest

services:
postgres:
image: postgres:15-alpine
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven'

- name: Run Unit Tests
run: mvn test -Dspring.profiles.active=test

- name: Run Integration Tests
run: mvn verify -Dspring.profiles.active=test
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
SPRING_DATASOURCE_USERNAME: test
SPRING_DATASOURCE_PASSWORD: test

- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: target/surefire-reports/

- name: Generate Coverage Report
run: mvn jacoco:report

- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
files: target/site/jacoco/jacoco.xml
fail_ci_if_error: false

Parte 3: Workflow CD (Build & Deploy)​

.github/workflows/cd.yml​

name: CD - Build and Deploy

on:
push:
branches: [main]
release:
types: [published]

env:
REGISTRY: docker.io
IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/todo-app

jobs:
# ===== JOB 1: Test =====
test:
name: Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven'

- name: Run Tests
run: mvn test

# ===== JOB 2: Build Docker =====
build:
name: Build and Push Docker
runs-on: ubuntu-latest
needs: test

outputs:
image-tag: ${{ steps.meta.outputs.tags }}
version: ${{ steps.meta.outputs.version }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix=
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Print image info
run: |
echo "Image pushed: ${{ env.IMAGE_NAME }}"
echo "Tags: ${{ steps.meta.outputs.tags }}"

# ===== JOB 3: Deploy to Railway =====
deploy:
name: Deploy to Railway
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment: production

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Railway CLI
run: npm i -g @railway/cli

- name: Deploy to Railway
run: railway up --detach
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

- name: Print deployment info
run: |
echo "✅ Deployed to Railway!"
echo "Image: ${{ needs.build.outputs.image-tag }}"

Parte 4: Configuración Railway​

Crear un Proyecto Railway​

  1. Ir a railway.app
  2. "New Project" → "Empty Project"
  3. "Add Service" → "GitHub Repo" → Seleccione su repositorio
  4. "Add Database" → "PostgreSQL"

Obtener el Token Railway​

  1. Account Settings → Tokens
  2. "Create Token"
  3. Copie el token en GitHub Secrets

Variables de Entorno Railway​

En el dashboard de Railway, configure:

SPRING_PROFILES_ACTIVE=prod
DATABASE_URL=${{Postgres.DATABASE_URL}}
# Railway inyecta automáticamente DATABASE_URL desde PostgreSQL

railway.json (opcional)​

{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile"
},
"deploy": {
"numReplicas": 1,
"healthcheckPath": "/actuator/health",
"healthcheckTimeout": 30,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 3
}
}

Parte 5: Alternativa Heroku​

.github/workflows/deploy-heroku.yml​

name: Deploy to Heroku

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.14
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
heroku_email: ${{ secrets.HEROKU_EMAIL }}
usedocker: true

Archivos Heroku Necesarios​

heroku.yml:

build:
docker:
web: Dockerfile
run:
web: java -Dserver.port=$PORT -jar app.jar

Parte 6: Probar el Pipeline​

1. Push Inicial​

git add .
git commit -m "Add CI/CD pipeline"
git push origin main

2. Verificar GitHub Actions​

  1. Ir a la pestaña Actions
  2. Verificar que el workflow se ejecute
  3. Cada job debe pasar a verde ✅

3. Verificar Docker Hub​

  1. Ir a hub.docker.com
  2. Verificar que la imagen esté publicada
  3. Verificar los tags

4. Verificar el Despliegue​

# Railway
curl https://your-app.railway.app/actuator/health

# O Heroku
curl https://your-app.herokuapp.com/actuator/health

Parte 7: Workflow con Notificaciones​

Notificaciones Slack/Discord​

  notify:
name: Notify
runs-on: ubuntu-latest
needs: [deploy]
if: always()

steps:
- name: Send Slack notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,message,commit,author,action,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
if: always()

Parte 8: Badge de Estado​

Agregar al README.md​

# Todo App

![CI Status](https://github.com/YOUR_USERNAME/todo-app/workflows/CI%20-%20Tests/badge.svg)
![CD Status](https://github.com/YOUR_USERNAME/todo-app/workflows/CD%20-%20Build%20and%20Deploy/badge.svg)

## Descripción
Aplicación de gestión de tareas con Spring Boot.

## Enlaces
- Producción: https://your-app.railway.app
- API Docs: https://your-app.railway.app/swagger-ui.html

Verificación Final​

Checklist​

  • Push dispara el workflow CI
  • Pruebas pasan en GitHub Actions
  • Imagen Docker publicada en Docker Hub
  • Aplicación desplegada en Railway
  • /actuator/health retorna UP
  • La API funciona en producción

URLs a Verificar​

# Health check
curl https://your-app.railway.app/actuator/health

# API
curl https://your-app.railway.app/api/todos

# Crear un todo en prod
curl -X POST https://your-app.railway.app/api/todos \
-H "Content-Type: application/json" \
-d '{"title": "Test en producción!"}'

🎯 ¡Módulo Terminado!​

¡Felicidades! Ha completado el Módulo 12 - Despliegue y Producción.

Resumen:

  • Capítulo 1: Preparación para producción
  • Capítulo 2: Docker y contenerización
  • Capítulo 3: CI/CD con GitHub Actions
  • Capítulo 4: Despliegue en la nube
  • TP12.1: Dockerización completa
  • TP12.2: Pipeline CI/CD

Competencias adquiridas:

  • ✅ Perfiles Spring Boot (dev, prod, docker)
  • ✅ Dockerfile multi-stage optimizado
  • ✅ Docker Compose con PostgreSQL
  • ✅ Workflows GitHub Actions
  • ✅ Despliegue automático en Railway
  • ✅ Monitoreo y health checks

➡️ Volver al Módulo 12


⬅️ Volver al Plan de Curso