Actividad 2. API Spring Boot con GitHub Actions y AWS Elastic Beanstalk

Sitio: Campus virtual DAW - damiansu
Curso: Despliegue de Aplicaciones Web
Libro: Actividad 2. API Spring Boot con GitHub Actions y AWS Elastic Beanstalk
Imprimido por: Invitado
Día: jueves, 22 de enero de 2026, 04:40

Descripción

Práctica: API REST en Spring Boot con Integración Continua y Despliegue en la nube (GitHub Actions + AWS Elastic Beanstalk)

1. Explicación

Contexto de la práctica

En esta práctica se trabaja con un flujo real de desarrollo profesional, similar al que se utiliza en entornos empresariales.

El objetivo no es solo crear una aplicación, sino aprender cómo se integra, prueba y despliega automáticamente un proyecto Java en la nube a partir de cambios en el repositorio.

La práctica combina tres elementos clave:

 
Idea fundamental

El principio base de esta práctica es el siguiente:

El código se escribe y se prueba en local.
El pipeline valida y despliega automáticamente.

Nunca se programa dentro del pipeline.

El pipeline no arregla errores, solo los detecta.

Si el proyecto no funciona en local, no funcionará en la nube.

 
Flujo real de trabajo (visión general)

El flujo completo que se sigue es el siguiente:

  1. El desarrollador trabaja en su ordenador

  2. Los cambios se suben al repositorio

  3. GitHub ejecuta el pipeline automáticamente

  4. Si todo es correcto, la aplicación se despliega en AWS

Este flujo se repite en cada cambio del proyecto.

 
Fase 1: Desarrollo en local

Todo comienza en el entorno local del alumno.

En esta fase se realizan las siguientes tareas:

  • Creación de una API REST mínima con Spring Boot

  • Configuración del proyecto con Maven

  • Ejecución de pruebas locales con:

mvn test

Debemos de comprobar que:

  • El proyecto compila correctamente

  • La aplicación arranca sin errores

  • Los endpoints funcionan en local

Esta fase es obligatoria antes de continuar.

 
Fase 2: Control de versiones (Git)

Una vez validado el proyecto en local:

  • Se registran los cambios con git commit

  • Se suben al repositorio remoto con git push

Cada push al repositorio principal es el disparador del pipeline.

Aquí no se ejecuta nada manualmente: todo el proceso posterior es automático.

Fase 3: Integración Continua con GitHub Actions

Cuando se realiza un push, GitHub ejecuta el workflow definido en el repositorio.

Este workflow realiza las siguientes acciones:

  1. Descarga el código del repositorio

  2. Configura Java 17

  3. Compila el proyecto con Maven

  4. Ejecuta los tests

  5. Genera el artefacto (JAR)

Si alguna de estas fases falla, el pipeline se detiene y no se despliega nada.

Este proceso garantiza que solo código válido llegue a producción.

Fase 4: Despliegue Continuo en AWS Elastic Beanstalk

Cuando el pipeline finaliza correctamente:

  • El JAR generado se empaqueta

  • Se despliega automáticamente en un entorno de Elastic Beanstalk

  • AWS se encarga de:

    • La máquina virtual

    • El servidor web

    • El arranque de la aplicación

    • La exposición del servicio por HTTP

El alumno no gestiona servidores, solo su aplicación.

El resultado final es una API accesible públicamente desde Internet.

El objetivo principal es...

Con esta práctica el alumno demuestra que:

  • Sabe desarrollar una API REST real

  • Entiende el concepto de Integración Continua (CI)

  • Comprende el Despliegue Continuo (CD)

  • Es capaz de trabajar con pipelines automáticos

  • Puede desplegar aplicaciones en la nube de forma profesional

No es una simulación: es un flujo real de CI/CD.

 Idea clave final
El desarrollador controla el código.
El pipeline controla la calidad.
La nube ejecuta la aplicación.

Este es el mismo modelo que se utiliza en proyectos profesionales.

2. Fase 1: Desarrollo en local

Antes de automatizar cualquier proceso de integración o despliegue, es imprescindible preparar correctamente el entorno de desarrollo.

El objetivo de esta fase es garantizar que el proyecto se comporta de la misma forma en local, en el pipeline de Integración Continua y en la nube.

En esta fase no se busca desarrollar una aplicación funcional, sino validar que:

  • el proyecto se construye correctamente,

  • el entorno es reproducible,

  • y el repositorio está listo para ser automatizado.

Se trabajará con el código de ejemplo inicial, que servirá como base para las siguientes fases de la práctica.

1 Creación del proyecto base

Se crea un proyecto Java con Maven, que será la base del proceso de automatización posterior.

Este proyecto contendrá únicamente la estructura mínima necesaria para:

  • compilar código Java,

  • ejecutar la fase de tests,

  • y generar un artefacto reproducible.

2. Configuración de un entorno de construcción reproducible

El objetivo de este apartado es asegurar que el proyecto se construye siempre con la misma versión de Java, independientemente del entorno donde se ejecute.

Configuración de Java en el proyecto

Desde la configuración del proyecto se establece:

  • SDK: Amazon Corretto 17

  • Language level: 17

De este modo:

  • el entorno local,

  • el pipeline de Integración Continua,

  • y el entorno en la nube

utilizarán exactamente la misma versión de Java.

Project structure (Local, pipeline y nube usan el mismo Java)

Comprobamos el Maven:

Incorporación del Maven Wrapper

Para evitar depender de una instalación global de Maven en cada máquina, se añade el Maven Wrapper, que será el utilizado posteriormente por el pipeline.

Desde IntelliJ IDEA, utilizando la opción Run Anything (pulsando dos veces la tecla Ctrl), se ejecuta:

Pulsamos dos veces seguidas la tecla Ctrl:

mvn -N io.takari:maven:wrapper

Tras ejecutar el comando, se comprueba que se ha generado la siguiente estructura:

mvnw
mvnw.cmd
pom.xml
src
target

El uso del Maven Wrapper garantiza que todos los entornos utilizan la misma versión de Maven, independientemente de la configuración local del sistema.

Verificación de versiones

Se comprueba la versión de Java activa en el sistema:

java -version

  • SDK: Amazon Corretto 17 

  • Language level: 17

Y se verifica la versión de Java utilizada por el Maven Wrapper:

./mvnw -v
  •  Maven Wrapper (mvnw): 25... hay que cambiar

En este punto puede ocurrir que las versiones no coincidan. Por ejemplo, el proyecto puede estar configurado con Java 17 en IntelliJ, mientras que el sistema utiliza otra versión diferente.

Ajuste temporal del Java del sistema

En caso de discrepancia, se ajusta temporalmente la variable JAVA_HOME solo para el proyecto, sin afectar al entorno global:

/usr/libexec/java_home -V

export JAVA_HOME=$(/usr/libexec/java_home -v 17)

Tras el ajuste, se vuelve a comprobar:

java -version
./mvnw -v

Resultado final

Una vez realizadas las comprobaciones, el entorno queda alineado:

  • IntelliJ → Java 17

  • Maven Wrapper → Java 17

  • Sistema (temporal) → Java 17

  • Pipeline futuro → Java 17

El proyecto queda preparado para ejecutarse de forma consistente en cualquier entorno (lo que afecta a todo el pipeline)

3. Fase 2: Control de versiones (Git)

Una vez preparado el entorno de construcción reproducible, es necesario integrar un sistema de control de versiones.

La Integración Continua depende directamente de Git, ya que los pipelines se ejecutan a partir de eventos como commits o push al repositorio remoto.

Por tanto, esta fase es imprescindible antes de automatizar cualquier proceso.

Inicialización del repositorio Git

Durante la creación del proyecto se ha inicializado un repositorio Git local, lo que permite registrar la evolución del código desde el primer momento.

En este punto:

  • el repositorio no contiene aún ningún commit,

  • pero Git ya detecta los archivos del proyecto.

Se comprueba el estado inicial del repositorio con:

git status

En este punto, el repositorio no contiene aún ningún commit, pero ya se encuentran detectados los archivos del proyecto.

Configuración del archivo .gitignore

Antes de realizar el primer commit, se configura el archivo .gitignore para evitar subir al repositorio:

  • archivos generados por el proceso de compilación,

  • configuraciones específicas del IDE,

  • ficheros del sistema operativo.

Un .gitignore que tenemos es algo parecido a esto:

target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.kotlin

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store

Se configura .gitignore para excluir configuraciones del IDE (carpeta .idea/) y artefactos generados (target/).

Un .gitignore mínimo y adecuado para este proyecto es:

# Maven
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

# IntelliJ IDEA
.idea/
*.iml
*.iws
*.ipr

# macOS
.DS_Store

Revisión del estado del repositorio

Hago el git status y necesito cambiar cosillas:

damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 % git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
        new file:   .idea/.gitignore
        new file:   .idea/misc.xml
        new file:   .idea/vcs.xml
        new file:   pom.xml
        new file:   src/main/java/com/dam/cicd/Main.java

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   pom.xml

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .mvn/
        mvnw
        mvnw.cmd

Viendo esto necesito quitar .idea del índice

git rm -r --cached .idea

Unificar pom.xml, Para que no esté “new” y “modified” a la vez:

git add pom.xml

Añadir Maven Wrapper (imprescindible para CI)

git add mvnw mvnw.cmd .mvn

Asegurar lo básico

git add .gitignore src

Revisión final 

git status
damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 % git status        
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
        new file:   .mvn/wrapper/MavenWrapperDownloader.java
        new file:   .mvn/wrapper/maven-wrapper.jar
        new file:   .mvn/wrapper/maven-wrapper.properties
        new file:   mvnw
        new file:   mvnw.cmd
        new file:   pom.xml
        new file:   src/main/java/com/dam/cicd/Main.java

Este git status es exactamente el estado ideal para el primer commit de un proyecto preparado para CI/CD.

Damos nuestro datos:

git config --global user.name "Damián" 
git config --global user.email "info@damiansu.com"
git config --global --list

Primer commit del proyecto

Se registra el primer commit del proyecto:

git commit -m "Configuración inicial del proyecto para CI/CD"

Se verifica el historial:

git log --oneline

 

Validación final del entorno local

Como última comprobación antes de automatizar, se valida que el proyecto puede construirse y ejecutar su fase de tests usando el Maven Wrapper:

./mvnw test

La ejecución finaliza correctamente con:

  • BUILD SUCCESS

Ejecución de la fase test mediante maven-surefire-plugin

damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 % ./mvnw test
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.dam.cicd:dam-ci-cd-api-001 >-------------------
[INFO] Building dam-ci-cd-api-001 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ dam-ci-cd-api-001 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ dam-ci-cd-api-001 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ dam-ci-cd-api-001 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/damiansualdea/IdeaProjects/dam-ci-cd-api-001/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ dam-ci-cd-api-001 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ dam-ci-cd-api-001 ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Aunque todavía no existan tests implementados, esta validación garantiza que:

  • el proyecto compila correctamente,

  • Maven funciona mediante wrapper,

  • y el entorno local queda preparado para integrarlo en un pipeline de Integración Continua.

En la siguiente fase se configurará GitHub Actions, donde cada push al repositorio activará automáticamente el proceso de Integración Continua.

4. FASE 3A: Creación del repositorio en GitHub

Pipeline real en un proyecto Spring Boot

Objetivo de la fase:

Subir el proyecto a GitHub y configurar un pipeline de Integración Continua que ejecute automáticamente:

./mvnw test

cada vez que se produce un cambio en el repositorio.

A partir de esta fase:

  • no se ejecutan validaciones manuales,

  • el proyecto se valida automáticamente,

  • los errores se detectan antes del despliegue.

1. Creación del repositorio en GitHub

Desde GitHub se crea un nuevo repositorio:

  • Repository name: dam-ci-cd-api-001

  • Visibility: Public o Private (indiferente)

No marcar ninguna de estas opciones:

  • Add a README

  • Add .gitignore

  • Add a license

El repositorio debe crearse vacío, ya que el código ya existe en local.

2 Conectar el repositorio local con GitHub

Desde el terminal de IntelliJ, en la raíz del proyecto:

Comprobación de remotos
git remote -v

Si el proyecto no está conectado a ningún repositorio remoto, no debe aparecer nada.

Añadir remoto

Utilizando el Quick setup, ya nos deja todo preparado:

Utilizando la URL del repositorio recién creado:

git remote add origin https://github.com/damiansu/dam-ci-cd-api-001.git

Se verifica la conexión:

git remote -v

 
Nota sobre la rama main

En la documentación de GitHub puede aparecer el comando: 

git branch -M main

Este comando sirve para renombrar la rama actual a main.

En este proyecto no es necesario, ya que la rama principal ya se llama main.

Subir el proyecto a GitHub

Para realizar el primer push es necesario:

  • el nombre de usuario de GitHub (no el correo),

  • un Personal Access Token.

Un token de acceso personal:

  • solo se muestra una vez al crearse,

  • si se pierde, debe generarse uno nuevo.

Configuración recomendada del token:

  • Expiration: 30 o 90 días

  • Scope: repo (solo este)

git push -u origin main

Verificación visual

En GitHub:

  1. Abre el repositorio dam-ci-cd-api-001

  2. Refresca la página

    • pom.xml

    • mvnw

    • .mvn/

    • src/

      Debes ver:

Si esto aparece, el repositorio está correctamente sincronizado.

5. FASE 3B: Creación del pipeline de Integración Continua con GitHub Actions

Configurar un pipeline de Integración Continua (CI) que se ejecute automáticamente en GitHub cada vez que se suben cambios al repositorio.

El pipeline debe:

  • descargar el código,

  • configurar el entorno,

  • compilar el proyecto,

  • ejecutar los tests.

A partir de este momento, cada push validará automáticamente el proyecto

1 Creación del archivo de workflow

GitHub Actions detecta los pipelines definidos en la ruta:

.github/workflows/

Desde el panel Project de IntelliJ (lado izquierdo):

  1. Clic derecho sobre el proyecto

  2. New → Directory

    • Nombre: .github

Dentro de .github:

  1. New → Directory

    • Nombre: workflows

Dentro de workflows:

  1. New → File

    • Nombre exacto: ci.yml

La ruta debe quedar exactamente así:

.github/workflows/ci.yml

2 Definición del pipeline de Integración Continua

En el archivo ci.yml se define el comportamiento del pipeline.

Contenido completo del archivo (copiar tal cual):

name: CI - Construcción con Maven

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build-test:
    runs-on: ubuntu-latest

    steps:
      - name: Clonar repositorio
        uses: actions/checkout@v4

      - name: Configurar JDK 17
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "17"
          cache: maven

      - name: Compilar y ejecutar tests con Maven Wrapper
        run: ./mvnw -B clean test

Este archivo no ejecuta nada en local.

Define qué debe hacer GitHub cuando detecta cambios en el repositorio.

4 Subir el workflow y lanzar el pipeline

Una vez creado el archivo ci.yml, se incorpora al repositorio remoto para que GitHub pueda ejecutarlo.

Revisión del estado del repositorio

Antes de realizar el commit del workflow, se revisa el estado del repositorio para comprobar qué archivos han cambiado:

git status

Debe aparecer el archivo del workflow como pendiente de añadir:

damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 % git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .github/workflows/ci.yml

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   .github/workflows/ci.yml

Añadir el workflow al control de versiones

Una vez verificado el estado, se añade el archivo del pipeline al control de versiones:

git add .github/workflows/ci.yml

Se vuelve a comprobar el estado:

git status

Y se realiza el commit correspondiente:

git commit -m "Añadir pipeline de integración continua con GitHub Actions"

Finalmente, se suben los cambios al repositorio remoto:

git push

Tras el push, GitHub Actions detecta automáticamente el pipeline y lo ejecuta.

5 Ver el pipeline en acción

Desde GitHub:

  1. Acceder al repositorio

  2. Abrir la pestaña Actions

  3. Seleccionar el workflow CI - Construcción con Maven

Se comprueba:

  • Evento: push sobre la rama main

  • Estado: Success

  • Ejecución correcta de:

    • checkout del repositorio

    • configuración de Java

    • compilación y tests

7 Forzar un fallo del pipeline (CI en rojo)

Objetivo: Comprobar que el pipeline detecta errores y marca la ejecución como Failure.

Procedimiento

  1. Introducir un error de compilación en Main.java (por ejemplo, eliminar un ;).

  2. Verificar en local:

./mvnw test
  1. Subir el cambio:

git add src/main/java/com/dam/cicd/Main.java
git commit -m "Romper compilación para demostrar CI en rojo"
git push

El pipeline fallará automáticamente.

Volver a verde
  1. Corregir el error

  2. Commit y push:

git commit -am "Arreglar error de compilación y restaurar CI"
git push

El pipeline volverá a ejecutarse correctamente.

Conclusión

Con esta fase queda implementada una Integración Continua real, donde:

  • cada push valida el proyecto,

  • los errores se detectan automáticamente,

  • el entorno es reproducible y externo.

“A partir de ahora, cada push valida automáticamente el proyecto.”

6. FASE 4: Despliegue Continuo (CD) con AWS Elastic Beanstalk

Objetivo de la fase

El objetivo de esta fase es automatizar el despliegue de una API Spring Boot en la nube, de forma que cada cambio subido a GitHub provoque automáticamente la actualización de la aplicación en producción.

En esta fase se pasa de Integración Continua (CI) a Despliegue Continuo (CD), utilizando:

  • GitHub Actions

  • AWS Elastic Beanstalk (plataforma Java SE)

Flujo completo de CD

A partir de esta fase, el flujo real del proyecto será:

git commit
   ↓
git push
   ↓
GitHub Actions
   ↓
Compilación y empaquetado
   ↓
Creación del artefacto de despliegue
   ↓
AWS Elastic Beanstalk
   ↓
Aplicación actualizada automáticamente

No hay pasos manuales intermedios.

BLOQUE A — Laboratorio (AWS Academy / Learner Lab)

En el laboratorio, el objetivo es desplegar manualmente una versión en Elastic Beanstalk y comprobar que la API funciona.
La automatización completa con IAM + Secrets se verá después (Bloque B).
1 Creación de una API mínima Spring Boot

Se parte de una API REST sencilla, cuyo objetivo no es la funcionalidad, sino validar el proceso de despliegue automático.

Endpoint disponible
  • /api/estado -> Devuelve información básica del estado de la aplicación en formato JSON.

Código principal de la aplicación

src/main/java/com/dam/cicd/Man.java

package com.dam.cicd;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

src/main/java/com/dam/cicd/EstadoControlador.java

package com.dam.cicd;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class EstadoControlador {

    @GetMapping("/api/estado")
    public Map<String, Object> estado() {
        return Map.of(
                "estado", "OK",
                "servicio", "dam-ci-cd-api-001",
                "mensaje", "API operativa"
        );
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>

    <groupId>com.dam.cicd</groupId>
    <artifactId>dam-ci-cd-api-001</artifactId>
    <version>1.0.0</version>
    <name>dam-ci-cd-api-001</name>
    <description>API Spring Boot para CI/CD</description>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- API REST -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Tests -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Genera JAR ejecutable (bootJar) -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Comprobamos que:

java -jar target/*.jar

Paramos el Spring Boot y vamos con el código del main

src/main/resources/application.properties

server.port=${PORT:8080}

Esta configuración permite que la aplicación:

  • use el puerto 8080 en local,

  • respete el puerto dinámico asignado por Elastic Beanstalk en la nube.

2 Empaquetado como JAR ejecutable

Elastic Beanstalk (Java SE) despliega JARs ejecutables, no proyectos completos.

Compilación del proyecto

Desde la raíz del proyecto:

./mvnw clean test
./mvnw clean package

Se genera un JAR ejecutable en:

target/dam-ci-cd-api-001-1.0.0.jar
Prueba en local
java -jar target/*.jar

Acceso desde navegador:

  • http://localhost:8080/api/estado

Deberías ver el JSON con estado, servicio, mensaje.

Con esto se valida que:

  • la aplicación arranca correctamente,

  • el JAR es ejecutable,

  • el endpoint responde como se espera.

3 Preparación del paquete de despliegue para Elastic Beanstalk

Elastic Beanstalk espera un ZIP limpio, con el JAR en la raíz y un comando de arranque claro.

Creación del Procfile

En la raíz del proyecto se crea un archivo llamado Procfile (sin extensión):

web: java -jar app.jar

Este archivo indica a Elastic Beanstalk cómo arrancar la aplicación.

Creación del ZIP de despliegue
mkdir -p deploy
cp target/*.jar deploy/app.jar
cp Procfile deploy/Procfile
(cd deploy && zip -r ../deploy.zip .)
 

Resultado final:

deploy.zip
 ├── app.jar
 └── Procfile

Este ZIP es el único artefacto que se despliega en AWS.

 
4 Creación del entorno en Elastic Beanstalk

Desde la consola de AWS:

  1. Abrir Elastic Beanstalk

  2. Pulsar Create application

  3. Configurar:

  • Application name: dam-ci-cd-api-001

  • Platform: Java

  • Platform branch: Java SE

  • Environment tier: Entorno de servidor web

  • Application code: Cargar el código

  • Archivo: deploy.zip

Comprobación final en AWS:

Comprobación final en AWS:

  • Abrir la URL del entorno y verificar:

    • /api/estado responde

    • (opcional) / si lo has añadido como texto “Fin de práctica”.

Hemos creado dos roles

El Ngnix de EB intenta conectar por defecto en el 5000, hay que abrirlo

/api/estado

Fin de la práctica para alumnos de laboratorio

A partir de aquí se continúa con cuenta Free Tier, porque el siguiente bloque requiere gestionar IAM + Secrets, algo que en AWS Academy/Learner Lab puede estar limitado.

BLOQUE B — Continuación (Cuenta Free Tier)
Automatizar el CD con GitHub Actions → Elastic Beanstalk

Objetivo: que cada git push despliegue automáticamente en EB.

5 Bucket S3 para versiones (Elastic Beanstalk)

Elastic Beanstalk usa un bucket automático del tipo:

  • elasticbeanstalk-us-east-1-XXXXXXXXXXXX

No es necesario crear un bucket nuevo si vas a desplegar con EB directamente.

6 Credenciales AWS para GitHub Actions (IAM)
  • IAM → Users → Create user github-actions-eb

  • Policies:

    • AWSElasticBeanstalkFullAccess

    • AmazonS3FullAccess

  • Create access key → te da:

    • AWS_ACCESS_KEY_ID

    • AWS_SECRET_ACCESS_KEY

7 Configurar Secrets en GitHub
  • App Spring Boot empaquetada en JAR

  • Elastic Beanstalk Java SE

  • Secrets en GitHub:

    • AWS_ACCESS_KEY_ID

    • AWS_SECRET_ACCESS_KEY

  • Región: us-east-1

  • App/Entorno: los vas a hardcodear (ok)

Secrets necesarios en GitHub

En tu repo → Settings → Secrets and variables → Actions → New repository secret

Crea estos 4:

  • AWS_ACCESS_KEY_ID

  • AWS_SECRET_ACCESS_KEY

  • AWS_SESSION_TOKEN  

  • AWS_REGION → pon us-east-1

8 Workflow de CD: .github/workflows/deploy-eb.yml

En tu repo crea este archivo: .github/workflows/deploy-eb.yml

Este workflow hace:

  • build del JAR,

  • crea deploy.zip,

  • despliega en Elastic Beanstalk.

Nota importante: en tu YAML final usa tu app real:

  • application_name: dam-ci-cd-api-001

  • environment_name: dam-ci-cd-api-001-env

name: Despliegue a Elastic Beanstalk

on:
  push:
    branches:
      - main   # Se ejecuta cuando haces push a main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      # Descargar el código del repositorio
      - name: Checkout del repositorio
        uses: actions/checkout@v4

      # Configurar Java 17 (Corretto)
      - name: Configurar Java 17
        uses: actions/setup-java@v4
        with:
          distribution: 'corretto'
          java-version: '17'

      # Compilar el proyecto (ajusta si usas Gradle)
      - name: Compilar proyecto con Maven
        run: mvn clean package -DskipTests

      # Configurar credenciales de AWS
      - name: Configurar credenciales de AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      # Crear el paquete para Elastic Beanstalk
      - name: Preparar artefacto para Elastic Beanstalk
        run: |
          mkdir deploy
          cp target/*.jar deploy/app.jar
          cp Procfile deploy/Procfile
          cd deploy
          zip -r deploy.zip .

      # Subir nueva versión y desplegar en Elastic Beanstalk
      - name: Desplegar en Elastic Beanstalk
        uses: einaregilsson/beanstalk-deploy@v22
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          application_name: dam-ci-cd-api-001
          environment_name: Dam-ci-cd-api-001-env
          region: us-east-1
          version_label: v-${{ github.run_id }}
          deployment_package: deploy/deploy.zip
          wait_for_environment_recovery: true

9 Reglas de repositorio (evitar subir artefactos)

Comprobamos el estado del git

git status

NO subas deploy.zip  ni deploy/

.gitignore

# Maven
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

# IntelliJ IDEA
.idea/
*.iml
*.iws
*.ipr

# macOS
.DS_Store


# Artefactos locales de despliegue
/deploy/
/deploy.zip

git status

damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 % git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .github/workflows/deploy-eb.yml
        new file:   Procfile
        new file:   src/main/java/com/dam/cicd/EstadoControlador.java
        new file:   src/main/resources/application.properties

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   .github/workflows/deploy-eb.yml
        modified:   .gitignore
        modified:   Procfile
        modified:   pom.xml
        modified:   src/main/java/com/dam/cicd/EstadoControlador.java
        modified:   src/main/java/com/dam/cicd/Main.java
        modified:   src/main/resources/application.properties

git status

damiansualdea@Mac-Studio-de-Damian-2 dam-ci-cd-api-001 %  git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .github/workflows/deploy-eb.yml
        modified:   .gitignore
        new file:   Procfile
        modified:   pom.xml
        new file:   src/main/java/com/dam/cicd/EstadoControlador.java
        modified:   src/main/java/com/dam/cicd/Main.java
        new file:   src/main/resources/application.properties

10 Resultado esperado (CD real)

Ejecutamos

git commit -m "Configurar API y despliegue automático en Elastic Beanstalk"

Push a GitHub (esto dispara TODO)
 
git push origin main

Ver el pipeline en acción

En cuanto hagas el push, ve a GitHub:

Y en AWS:

7. Todo en producción

Código final del controlador

Se actualiza el controlador para incluir un endpoint raíz (/) accesible públicamente, que servirá como verificación visual del despliegue en producción, además del endpoint de estado de la API.

src/main/java/com/dam/cicd/EstadoControlador.java

package com.dam.cicd;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class EstadoControlador {

    @GetMapping("/")
    public String inicio() {
        return "Fin de la práctica: CI/CD con GitHub Actions y AWS Elastic Beanstalk";
    }

    @GetMapping("/api/estado")
    public Map<String, Object> estado() {
        return Map.of(
                "estado", "OK",
                "servicio", "dam-ci-cd-api-001",
                "mensaje", "API operativa"
        );
    }
}

Publicación del cambio en producción

Una vez modificado el código, se registra el cambio y se envía al repositorio remoto:

git add src/main/java/com/dam/cicd/EstadoControlador.java
git commit -m "Añadir endpoint raíz y estado de la API"
git push origin main

 

Verificación final en producción

Una vez finalizado el pipeline, se comprueba el correcto funcionamiento de la aplicación accediendo a:

  • Endpoint raíz / → Muestra el mensaje de fin de práctica.

  • Endpoint de estado /api/estado → Devuelve un JSON con el estado del servicio.

Con esto se confirma que:

  • El despliegue automático funciona correctamente.

  • La aplicación está accesible públicamente.

  • El entorno de producción se ha actualizado sin intervención manual.

Conclusión de la práctica

Se ha implementado un sistema completo de Integración y Despliegue Continuo (CI/CD) utilizando:

  • GitHub Actions para la automatización del pipeline.

  • Maven para la construcción reproducible del proyecto.

  • AWS Elastic Beanstalk como plataforma de despliegue en producción.

Cada cambio enviado al repositorio activa automáticamente un pipeline que:

  • compila la aplicación,

  • genera el artefacto,

  • y lo despliega en AWS.

El correcto funcionamiento del sistema se valida mediante un endpoint raíz accesible públicamente y un endpoint adicional de estado del servicio.

Regla de oro (la más importante)

Si no estás usando la aplicación → el entorno debe estar apagado o terminado.

AWS cobra por recursos activos, no por código.

Terminar el entorno cuando no se use

No basta con detener la instancia: hay que terminar el entorno.

Pasos:
  1. AWS Console

  2. Elastic Beanstalk

  3. Aplicación → Entorno

  4. Actions → Terminate environment

  5. Confirmar

Coste tras terminar el entorno
  • 0 €

  • No queda EC2 activa.

  • No hay balanceador.

  • No hay servicios ejecutándose.

Si no estás usando la aplicación → el entorno debe estar apagado o terminado
¿Es complicado volver a lanzarlo? No.
  • Crear el entorno de nuevo: 5–10 minutos

  • El código ya está en GitHub.

  • GitHub Actions vuelve a desplegar automáticamente.

Coste real aproximado (orientativo)

Si se deja un entorno activo por error:

  • Elastic Beanstalk + EC2 24/7 5–10 € al mes

  • Con varios entornos o errores → puede subir más

En cloud:

Los entornos se crean para usarse y se destruyen cuando se termina.
 
Ahorro de costes en AWS

Una vez comprobado que la aplicación funciona correctamente en Elastic Beanstalk, el entorno debe ser terminado para evitar costes innecesarios.

Elastic Beanstalk permite crear el entorno de nuevo en cualquier momento, por lo que no es necesario mantenerlo activo permanentemente.

Mantener entornos cloud encendidos sin uso genera cargos económicos evitables