Técnico••15 min
CI/CD Completo con GitHub Actions y AWS
Guía paso a paso para implementar un pipeline CI/CD profesional usando GitHub Actions, incluyendo tests, builds, y despliegues automatizados a AWS.
MG
María González
VP of Engineering
Pipeline CI/CD Moderno con GitHub Actions
En InfraUX, automatizamos todo. Este es nuestro enfoque para construir pipelines CI/CD robustos que despliegan a AWS con confianza.
Arquitectura del Pipeline
1graph LR
2 A[Push to GitHub] --> B[GitHub Actions]
3 B --> C{Branch?}
4 C -->|main| D[Build & Test]
5 C -->|develop| E[Build & Test]
6 C -->|feature/*| F[Build & Test]
7 D --> G[Deploy to Prod]
8 E --> H[Deploy to Staging]
9 F --> I[Preview Deploy]
10 G --> J[CloudFront Invalidation]
11 H --> J
12 I --> J
Configuración Base del Workflow
1# .github/workflows/deploy.yml
2name: Deploy to AWS
3
4on:
5 push:
6 branches: [main, develop]
7 pull_request:
8 branches: [main]
9
10env:
11 AWS_REGION: us-east-1
12 ECR_REPOSITORY: infraux-app
13 ECS_SERVICE: infraux-service
14 ECS_CLUSTER: infraux-cluster
15 CONTAINER_NAME: infraux-container
16
17jobs:
18 test:
19 runs-on: ubuntu-latest
20 steps:
21 - uses: actions/checkout@v4
22
23 - name: Setup Node.js
24 uses: actions/setup-node@v4
25 with:
26 node-version: '20'
27 cache: 'npm'
28
29 - name: Install dependencies
30 run: npm ci
31
32 - name: Run linter
33 run: npm run lint
34
35 - name: Run tests
36 run: npm run test:ci
37
38 - name: Upload coverage
39 uses: codecov/codecov-action@v3
40 with:
41 token: ${{ secrets.CODECOV_TOKEN }}
Build y Push a ECR
1 build:
2 needs: test
3 runs-on: ubuntu-latest
4 outputs:
5 image: ${{ steps.image.outputs.image }}
6
7 steps:
8 - uses: actions/checkout@v4
9
10 - name: Configure AWS credentials
11 uses: aws-actions/configure-aws-credentials@v4
12 with:
13 aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
14 aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
15 aws-region: ${{ env.AWS_REGION }}
16
17 - name: Login to Amazon ECR
18 id: login-ecr
19 uses: aws-actions/amazon-ecr-login@v2
20
21 - name: Build, tag, and push image
22 id: image
23 env:
24 ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
25 IMAGE_TAG: ${{ github.sha }}
26 run: |
27 docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
28 docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
29 echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
Deploy a ECS con Blue/Green
1 deploy:
2 needs: build
3 runs-on: ubuntu-latest
4 environment:
5 name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
6 url: ${{ steps.deploy.outputs.url }}
7
8 steps:
9 - uses: actions/checkout@v4
10
11 - name: Configure AWS credentials
12 uses: aws-actions/configure-aws-credentials@v4
13 with:
14 aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
15 aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
16 aws-region: ${{ env.AWS_REGION }}
17
18 - name: Download task definition
19 run: |
20 aws ecs describe-task-definition \
21 --task-definition ${{ env.ECS_SERVICE }} \
22 --query taskDefinition > task-definition.json
23
24 - name: Update task definition
25 id: task-def
26 uses: aws-actions/amazon-ecs-render-task-definition@v1
27 with:
28 task-definition: task-definition.json
29 container-name: ${{ env.CONTAINER_NAME }}
30 image: ${{ needs.build.outputs.image }}
31
32 - name: Deploy to ECS
33 uses: aws-actions/amazon-ecs-deploy-task-definition@v1
34 with:
35 task-definition: ${{ steps.task-def.outputs.task-definition }}
36 service: ${{ env.ECS_SERVICE }}
37 cluster: ${{ env.ECS_CLUSTER }}
38 wait-for-service-stability: true
Estrategias Avanzadas
1. Cache de Dependencias
1- name: Cache node modules
2 uses: actions/cache@v3
3 with:
4 path: ~/.npm
5 key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
6 restore-keys: |
7 ${{ runner.os }}-node-
2. Matrix Strategy para Tests
1test:
2 strategy:
3 matrix:
4 node-version: [18, 20]
5 os: [ubuntu-latest, windows-latest]
6 runs-on: ${{ matrix.os }}
7 steps:
8 - uses: actions/setup-node@v4
9 with:
10 node-version: ${{ matrix.node-version }}
3. Despliegue Canary
1- name: Deploy Canary
2 run: |
3 # Actualizar solo el 10% del tráfico
4 aws ecs update-service \
5 --cluster ${{ env.ECS_CLUSTER }} \
6 --service ${{ env.ECS_SERVICE }} \
7 --deployment-configuration \
8 "maximumPercent=110,minimumHealthyPercent=90,deploymentCircuitBreaker={enable=true,rollback=true}"
9
10 # Esperar 5 minutos
11 sleep 300
12
13 # Verificar métricas
14 ERRORS=$(aws cloudwatch get-metric-statistics \
15 --namespace AWS/ECS \
16 --metric-name HTTPCode_Target_5XX_Count \
17 --dimensions Name=ServiceName,Value=${{ env.ECS_SERVICE }} \
18 --start-time $(date -u -d '5 minutes ago' +%Y-%m-%dT%H:%M:%S) \
19 --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
20 --period 300 \
21 --statistics Sum \
22 --query 'Datapoints[0].Sum' \
23 --output text)
24
25 if [ "$ERRORS" -gt "10" ]; then
26 echo "Too many errors, rolling back"
27 exit 1
28 fi
Seguridad en el Pipeline
1. Escaneo de Vulnerabilidades
1- name: Run Trivy vulnerability scanner
2 uses: aquasecurity/trivy-action@master
3 with:
4 image-ref: ${{ needs.build.outputs.image }}
5 format: 'sarif'
6 output: 'trivy-results.sarif'
7
8- name: Upload Trivy scan results
9 uses: github/codeql-action/upload-sarif@v2
10 with:
11 sarif_file: 'trivy-results.sarif'
2. Secrets Scanning
1- name: Scan for secrets
2 uses: trufflesecurity/trufflehog@main
3 with:
4 path: ./
5 base: ${{ github.event.repository.default_branch }}
6 head: HEAD
Notificaciones y Monitoreo
1- name: Notify Slack
2 if: always()
3 uses: 8398a7/action-slack@v3
4 with:
5 status: ${{ job.status }}
6 text: |
7 Deployment ${{ job.status }}
8 Branch: ${{ github.ref }}
9 Commit: ${{ github.sha }}
10 Author: ${{ github.actor }}
11 webhook_url: ${{ secrets.SLACK_WEBHOOK }}
12
13- name: Create deployment record
14 uses: actions/github-script@v7
15 with:
16 script: |
17 await github.rest.repos.createDeployment({
18 owner: context.repo.owner,
19 repo: context.repo.repo,
20 ref: context.sha,
21 environment: '${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}',
22 description: 'Deployed via GitHub Actions',
23 auto_merge: false,
24 required_contexts: []
25 });
Optimizaciones de Performance
Técnica | Mejora | Implementación |
---|---|---|
Docker Layer Caching | -70% build time | BuildKit + cache mounts |
Parallel Jobs | -50% total time | Matrix strategy |
Artifact Caching | -30% install time | actions/cache |
Self-hosted Runners | -80% queue time | EC2 spot instances |
Ejemplo Completo: Next.js a S3/CloudFront
1name: Deploy Next.js to AWS
2
3on:
4 push:
5 branches: [main]
6
7jobs:
8 deploy:
9 runs-on: ubuntu-latest
10
11 steps:
12 - uses: actions/checkout@v4
13
14 - name: Setup Node.js
15 uses: actions/setup-node@v4
16 with:
17 node-version: '20'
18 cache: 'npm'
19
20 - name: Install and Build
21 run: |
22 npm ci
23 npm run build
24 npm run export
25 env:
26 NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
27
28 - name: Configure AWS
29 uses: aws-actions/configure-aws-credentials@v4
30 with:
31 aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
32 aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
33 aws-region: us-east-1
34
35 - name: Deploy to S3
36 run: |
37 aws s3 sync ./out s3://${{ secrets.S3_BUCKET }} \
38 --delete \
39 --cache-control "public, max-age=31536000, immutable" \
40 --exclude "*.html" \
41 --exclude "*.json"
42
43 aws s3 sync ./out s3://${{ secrets.S3_BUCKET }} \
44 --delete \
45 --cache-control "public, max-age=0, must-revalidate" \
46 --exclude "*" \
47 --include "*.html" \
48 --include "*.json"
49
50 - name: Invalidate CloudFront
51 run: |
52 aws cloudfront create-invalidation \
53 --distribution-id ${{ secrets.CLOUDFRONT_ID }} \
54 --paths "/*"
Métricas de Nuestro Pipeline
<div class="info-box"> 📊 **Estadísticas de InfraUX:** - Deploys diarios: 45+ - Tiempo promedio: 3.2 minutos - Tasa de éxito: 99.7% - Rollbacks automáticos: 0.3% </div>Best Practices
-
Versionado Semántico Automático
1- name: Bump version 2 uses: mathieudutour/github-tag-action@v6.1 3 with: 4 github_token: ${{ secrets.GITHUB_TOKEN }} 5 default_bump: patch
-
Environments y Aprobaciones
1environment: 2 name: production 3 url: https://infraux.com
-
Rollback Automático
1- name: Rollback on failure 2 if: failure() 3 run: | 4 aws deploy stop-deployment \ 5 --deployment-id ${{ steps.deploy.outputs.deployment-id }} \ 6 --auto-rollback-enabled
Conclusión
Un pipeline CI/CD bien diseñado es la columna vertebral de cualquier equipo de desarrollo moderno. Con GitHub Actions y AWS, puedes construir flujos de trabajo que no solo automatizan, sino que también protegen y optimizan tus despliegues.
<div class="success-box"> 🚀 **Pro tip:** Comienza simple y evoluciona. No necesitas todas estas características desde el día 1. Construye incrementalmente basándote en las necesidades de tu equipo. </div>#cicd#github-actions#aws#devops#automation