From a80a6f964ff4c26057acf82d0316124cd2e90bd2 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Fri, 20 Jun 2025 11:38:26 -0300 Subject: [PATCH] =?UTF-8?q?Test=20de=20optimizaci=C3=B3n=20de=20Pipeline?= =?UTF-8?q?=20con=20Cache.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/deploy.yml | 112 +++++++++++++------------ Backend/GestionIntegral.Api/Dockerfile | 23 ++--- Frontend/Dockerfile | 18 +--- 3 files changed, 71 insertions(+), 82 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 1fc7c77..06bca2b 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Build and Deploy +name: Optimized Build and Deploy on: push: @@ -10,72 +10,80 @@ jobs: runs-on: ubuntu-latest steps: - - name: Run Entire CI/CD Process on Host via SSH + - name: Run Optimized CI/CD Process on Host via SSH run: | set -e - - # 1. Preparar el cliente SSH (sin cambios) - apt-get update > /dev/null && apt-get install -y openssh-client git > /dev/null + + # Configura SSH + sudo apt-get update -qq && sudo apt-get install -y openssh-client git mkdir -p ~/.ssh echo "${{ secrets.PROD_SERVER_SSH_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.PROD_SERVER_HOST }} >> ~/.ssh/known_hosts - # 2. Conectarse al HOST y ejecutar el script. - ssh ${{ secrets.PROD_SERVER_USER }}@${{ secrets.PROD_SERVER_HOST }} << EOF + # Ejecuta en el host remoto + ssh ${{ secrets.PROD_SERVER_USER }}@${{ secrets.PROD_SERVER_HOST }} << 'EOSSH' set -e - - # --- PARTE 1: PREPARACIÓN (EN EL HOST) --- - echo "--- (HOST) Preparing temporary workspace ---" + echo "--- INICIO DEL DESPLIEGUE OPTIMIZADO ---" - # Gitea reemplazará estas variables ANTES de enviar el script. - TEMP_DIR="/tmp/gitea-build/${{ gitea.run_id }}" - REPO_NAME_RAW="${{ gitea.repository }}" - # El shell remoto ejecutará estos comandos. Escapamos los '$'. - REPO_NAME=\$(echo "\$REPO_NAME_RAW" | tr '[:upper:]' '[:lower:]') - GITEA_REPO_PATH="/var/lib/docker/volumes/gitea-stack_gitea-data/_data/git/repositories/\${REPO_NAME}.git" + # 1. Preparar entorno + TEMP_DIR=$(mktemp -d) + REPO_NAME="gestionintegralweb" + GITEA_REPO_PATH="/var/lib/docker/volumes/gitea-stack_gitea-data/_data/git/repositories/$REPO_NAME.git" - git config --global --add safe.directory "\$GITEA_REPO_PATH" - - echo "Cloning repository from local path: \$GITEA_REPO_PATH" - rm -rf \$TEMP_DIR - git clone \$GITEA_REPO_PATH \$TEMP_DIR - cd \$TEMP_DIR + echo "Clonando repositorio..." + git clone "$GITEA_REPO_PATH" "$TEMP_DIR" + cd "$TEMP_DIR" git checkout "${{ gitea.sha }}" - # --- PARTE 2: CONSTRUIR IMÁGENES CON KANIKO (EN EL HOST) --- - echo "--- (HOST) Building images... ---" + # 2. Directorios de caché + KANIKO_CACHE_BACKEND="/kaniko_cache/backend" + KANIKO_CACHE_FRONTEND="/kaniko_cache/frontend" + sudo mkdir -p "$KANIKO_CACHE_BACKEND" "$KANIKO_CACHE_FRONTEND" + sudo chmod 777 "$KANIKO_CACHE_BACKEND" "$KANIKO_CACHE_FRONTEND" - docker run --rm -v "\$(pwd)":/workspace gcr.io/kaniko-project/executor:v1.9.0 \ - --context=/workspace --dockerfile=/workspace/Backend/GestionIntegral.Api/Dockerfile --no-push \ - --destination=\${REPO_NAME}-backend:latest --tarPath=/workspace/backend.tar - - docker run --rm -v "\$(pwd)":/workspace gcr.io/kaniko-project/executor:v1.9.0 \ - --context=/workspace --dockerfile=/workspace/Frontend/Dockerfile --no-push \ - --destination=\${REPO_NAME}-frontend:latest --tarPath=/workspace/frontend.tar - - # --- PARTE 3: DESPLEGAR (EN EL HOST) --- - echo "--- (HOST) Loading images and deploying... ---" + # 3. Construcción paralela con Kaniko (caché activada) + build_image() { + local context=$1 + local dockerfile=$2 + local cache_dir=$3 + local image_name=$4 + local tar_path=$5 + + docker run --rm \ + -v "$TEMP_DIR":/workspace \ + -v "$cache_dir":/cache \ + gcr.io/kaniko-project/executor:v1.9.0 \ + --context="/workspace/$context" \ + --dockerfile="/workspace/$dockerfile" \ + --cache=true \ + --cache-dir=/cache \ + --destination="$image_name" \ + --tarPath="/workspace/$tar_path" + } - docker load < backend.tar - docker load < frontend.tar + echo "Construyendo imágenes en paralelo..." + (build_image "." "Backend/GestionIntegral.Api/Dockerfile" "$KANIKO_CACHE_BACKEND" "$REPO_NAME-backend:latest" "backend.tar") & + (build_image "." "Frontend/Dockerfile" "$KANIKO_CACHE_FRONTEND" "$REPO_NAME-frontend:latest" "frontend.tar") & + wait + # 4. Cargar imágenes + echo "Cargando imágenes en Docker..." + docker load -i "$TEMP_DIR/backend.tar" + docker load -i "$TEMP_DIR/frontend.tar" + + # 5. Despliegue con Docker Compose cd /opt/gestion-integral - # Gitea reemplaza los secretos aquí. Es seguro. export DB_SA_PASSWORD='${{ secrets.DB_SA_PASSWORD_SECRET }}' - # Detener explícitamente los servicios para forzar a que usen la nueva imagen al levantar. - echo "--- (HOST) Stopping old services... ---" - docker compose down - - # Levantar los servicios. Docker Compose usará las imágenes recién cargadas. - echo "--- (HOST) Starting new services... ---" - docker compose up -d - - # --- PARTE 4: LIMPIEZA (EN EL HOST) --- - echo "--- (HOST) Cleaning up... ---" - # Escapamos la variable para que la evalúe el host remoto - rm -rf \$TEMP_DIR - docker image prune -af - echo "--- ¡¡DESPLIEGUE COMPLETADO Y VERIFICADO!! ---" - EOF \ No newline at end of file + echo "Recreando servicios..." + docker compose up -d --force-recreate + + # 6. Limpieza + echo "Realizando limpieza..." + rm -rf "$TEMP_DIR" + docker image prune -af --filter "until=24h" + + echo "--- DESPLIEGUE COMPLETADO CON ÉXITO ---" + echo "Tiempo total: $(($SECONDS / 60)) minutos y $(($SECONDS % 60)) segundos" + EOSSH \ No newline at end of file diff --git a/Backend/GestionIntegral.Api/Dockerfile b/Backend/GestionIntegral.Api/Dockerfile index c543e9d..e8ede6c 100644 --- a/Backend/GestionIntegral.Api/Dockerfile +++ b/Backend/GestionIntegral.Api/Dockerfile @@ -1,46 +1,37 @@ # --- Etapa 1: Build --- -# Usamos el SDK de .NET 9 (o el que corresponda) para compilar. FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /src -# Copiamos los archivos de proyecto (.sln y .csproj) y restauramos las dependencias. -# Esto es una optimización de caché de Docker. +# Copia solo los archivos de proyecto primero para cachear dependencias COPY GestionIntegralWeb.sln . COPY Backend/GestionIntegral.Api/GestionIntegral.Api.csproj Backend/GestionIntegral.Api/ -# Restauramos los paquetes NuGet. +# Restaura dependencias RUN dotnet restore "GestionIntegralWeb.sln" -# Copiamos todo el resto del código fuente. +# Copia el resto del código COPY . . -# Nos movemos al directorio del proyecto y lo construimos en modo Release. +# Construye el proyecto WORKDIR "/src/Backend/GestionIntegral.Api" RUN dotnet build "GestionIntegral.Api.csproj" -c Release -o /app/build # --- Etapa 2: Publish --- -# Publicamos la aplicación, lo que genera los artefactos listos para producción. FROM build AS publish RUN dotnet publish "GestionIntegral.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false # --- Etapa 3: Final --- -# Usamos la imagen de runtime de ASP.NET, que es mucho más ligera que el SDK. FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app -# Instalar dependencias de Chromium en la imagen de ASP.NET (basada en Debian) +# Instala dependencias optimizadas RUN apt-get update && apt-get install -y --no-install-recommends \ - libgbm1 libgbm-dev \ - libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 \ + libgbm1 libgconf-2-4 libgdk-pixbuf2.0-0 libgtk-3-0 \ libnss3 libxss1 libasound2 libxtst6 \ ca-certificates fonts-liberation lsb-release xdg-utils wget \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* COPY --from=publish /app/publish . -# El puerto en el que la API escuchará DENTRO del contenedor. -# Usaremos 8080 para evitar conflictos si en el futuro corres algo en el puerto 80. EXPOSE 8080 - -# El comando para iniciar la API cuando el contenedor arranque. ENTRYPOINT ["dotnet", "GestionIntegral.Api.dll"] \ No newline at end of file diff --git a/Frontend/Dockerfile b/Frontend/Dockerfile index 7c68d90..c9a3eca 100644 --- a/Frontend/Dockerfile +++ b/Frontend/Dockerfile @@ -1,34 +1,24 @@ # --- Etapa 1: Build --- -# Usamos una imagen de Node.js para construir los archivos estáticos de React. FROM node:20-alpine AS build WORKDIR /app -# Copiamos los archivos de dependencias desde la ruta correcta y las instalamos. +# Copia solo dependencias primero para cachear COPY Frontend/package.json Frontend/package-lock.json ./ -RUN npm install +RUN npm install --frozen-lockfile -# Copiamos el resto del código del frontend. +# Copia el resto del código COPY Frontend/. . -# Ejecutamos el script de build de Vite, que genera la carpeta 'dist'. +# Construye la aplicación RUN npm run build # --- Etapa 2: Serve --- -# Usamos una imagen de Nginx súper ligera para servir los archivos estáticos. FROM nginx:stable-alpine -# Copiamos nuestro archivo de configuración personalizado a la carpeta de configuración de Nginx. -# Esto sobreescribirá la configuración por defecto. COPY Frontend/nginx.conf /etc/nginx/conf.d/default.conf WORKDIR /usr/share/nginx/html -# Eliminamos el index.html por defecto de Nginx. RUN rm -f index.html - -# Copiamos los archivos construidos desde la etapa anterior a la carpeta que Nginx sirve. COPY --from=build /app/dist . -# Nginx por defecto escucha en el puerto 80, así que lo exponemos. EXPOSE 80 - -# Comando para iniciar Nginx. Esto asegura que se mantenga corriendo. CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file