Implementando CI/CD moderno con GitHub Actions y Docker

Aprende a configurar un pipeline de integración y despliegue continuo usando GitHub Actions y Docker, con ejemplos prácticos y buenas prácticas.

AI & ML
LLMRAGAgents

Implementando CI/CD moderno con GitHub Actions y Docker

¿Tu empresa está lista para IA? Descargá nuestro checklist gratuito →

Descargar checklist

Introducción

En el desarrollo de software moderno, la integración continua (CI) y el despliegue continuo (CD) son prácticas esenciales para entregar código de calidad de forma rápida y confiable. En este artículo, exploraremos cómo combinar GitHub Actions y Docker para construir un pipeline CI/CD robusto, escalable y fácil de mantener. Veremos desde la configuración básica hasta ejemplos prácticos con aplicaciones reales.

¿Por qué GitHub Actions y Docker?

GitHub Actions es la plataforma de CI/CD integrada en GitHub, que permite automatizar flujos de trabajo directamente desde tu repositorio. Es flexible, se integra con el ecosistema de GitHub y ofrece una amplia gama de acciones predefinidas. Docker, por su parte, proporciona contenedores ligeros que garantizan entornos consistentes desde desarrollo hasta producción. Juntos, permiten crear pipelines que compilan, prueban y despliegan aplicaciones de manera eficiente.

Ventajas clave

  • Entornos reproducibles: Docker asegura que el código se ejecute igual en local, CI y producción.
  • Escalabilidad: GitHub Actions se ejecuta en la nube, sin necesidad de gestionar servidores propios.
  • Comunidad y marketplace: Miles de acciones listas para usar, como actions/checkout o docker/build-push-action.

Configuración inicial del pipeline

Para comenzar, necesitas un repositorio con una aplicación (por ejemplo, Node.js) y un Dockerfile. Crearemos un archivo de flujo de trabajo en .github/workflows/ci-cd.yml.

Estructura básica del workflow

name: CI/CD Pipeline

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

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Run tests
        run: docker run myapp:${{ github.sha }} npm test

Este workflow se ejecuta en cada push o pull request a la rama main. Primero, clona el código, construye la imagen Docker y ejecuta las pruebas dentro del contenedor.

Construcción y publicación de imágenes Docker

Una vez que las pruebas pasan, el siguiente paso es publicar la imagen en un registro, como Docker Hub o GitHub Container Registry. Para ello, usamos la acción oficial docker/build-push-action.

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    needs: build-and-test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4
      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:latest, ghcr.io/${{ github.repository }}:${{ github.sha }}

Aquí, solo publicamos en la rama main después de que las pruebas hayan pasado. Utilizamos el token de GitHub autenticado para iniciar sesión en ghcr.io (GitHub Container Registry).

Despliegue continuo (CD)

El despliegue puede hacerse a un servidor, Kubernetes o un servicio PaaS como Heroku. Para este ejemplo, usaremos un despliegue simple a un servidor VPS mediante SSH.

¿Querés un diagnóstico personalizado? Completá el checklist gratuito →

Descargar checklist

Configurar secretos

Agrega los siguientes secretos en tu repositorio (Settings > Secrets and variables > Actions):

  • SSH_HOST: IP del servidor
  • SSH_USER: usuario SSH
  • SSH_KEY: clave privada SSH
  • DEPLOY_PATH: ruta donde se desplegará la aplicación

Workflow de deploy

deploy:
  runs-on: ubuntu-latest
  needs: build-and-push
  steps:
    - name: Deploy via SSH
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USER }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          cd ${{ secrets.DEPLOY_PATH }}
          docker pull ghcr.io/${{ github.repository }}:latest
          docker stop myapp || true
          docker rm myapp || true
          docker run -d --name myapp -p 80:3000 ghcr.io/${{ github.repository }}:latest

Este job se conecta por SSH al servidor, baja la última imagen, detiene y elimina el contenedor antiguo, y ejecuta el nuevo.

Buenas prácticas

1. Uso de caché para acelerar builds

Docker cachea capas, pero en CI es recomendable usar caché explícita para capas como npm install.

- name: Build with cache
  uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

2. Ejecutar pruebas en paralelo

Divide las pruebas en varios jobs para reducir el tiempo total.

3. Seguridad: escaneo de vulnerabilidades

Integra herramientas como trivy para escanear imágenes Docker en busca de vulnerabilidades.

- name: Scan image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}

Ejemplo completo

A continuación, un flujo de trabajo completo que integra todo lo visto:

name: CI/CD
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: docker build -t myapp:test .
      - name: Run tests
        run: docker run myapp:test npm test
  push:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Login
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:latest, ghcr.io/${{ github.repository }}:${{ github.sha }}
  deploy:
    needs: push
    runs-on: ubuntu-latest
    steps:
      - uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd ${{ secrets.DEPLOY_PATH }}
            docker pull ghcr.io/${{ github.repository }}:latest
            docker stop app || true
            docker rm app || true
            docker run -d --name app -p 80:3000 ghcr.io/${{ github.repository }}:latest

Conclusión

Hemos visto cómo implementar un pipeline CI/CD moderno con GitHub Actions y Docker. Esta combinación no solo automatiza las tareas repetitivas, sino que también garantiza entornos consistentes y despliegues confiables. Con un poco de configuración inicial, puedes acelerar tu ciclo de desarrollo y minimizar errores manuales.

Para profundizar, visita la documentación oficial de GitHub Actions y la guía de Docker.

¿Listo para dar el próximo paso? Evaluá tu empresa con nuestro checklist gratuito →

Descargar checklist

Publicaciones relacionadas