Saltar a contenido

⚠ Vigencia por confirmar. Documento previo al estándar de documentación, migrado sin reescribir su contenido técnico. Revisar contra el código antes de usarlo como fuente de verdad.

Guía de Configuración CI/CD con Runners Self-Hosted

Esta es la guía genérica y reutilizable para configurar automatización CI/CD en cualquier proyecto usando GitHub Actions y runners self-hosted en servidores privados.

Arquitectura General

graph TB
    subgraph GitHub["GitHub"]
        Repo["Repository"]
        Actions["GitHub Actions"]
        GHCR["GHCR Registry"]
        Secrets["Secrets"]
    end

    subgraph Staging["Servidor Staging"]
        RunnerS["Runner Self-Hosted<br/>(staging label)"]
        DockerS["Docker Engine"]
        AppS["Aplicación"]
    end

    subgraph Production["Servidor Producción"]
        RunnerP["Runner Self-Hosted<br/>(production label)"]
        DockerP["Docker Engine"]
        AppP["Aplicación"]
    end

    Repo -->|Push| Actions
    Actions -->|Build Image| GHCR
    Actions -->|Dispatch staging| RunnerS
    RunnerS -->|Pull Image| GHCR
    RunnerS -->|Deploy| DockerS
    DockerS -->|Runs| AppS

    Actions -->|Dispatch production| RunnerP
    RunnerP -->|Pull Image| GHCR
    RunnerP -->|Deploy| DockerP
    DockerP -->|Runs| AppP

    Secrets -.->|Auth| GHCR
    Secrets -.->|SSH Keys| RunnerS
    Secrets -.->|SSH Keys| RunnerP

Flujo de Despliegue

graph LR
    A["Push a rama"] -->|build| B["Build imagen<br/>en GitHub"]
    B -->|push| C["Publica a GHCR"]

    subgraph "Staging (Automático)"
        D["Runner staging<br/>ejecuta deploy.sh"]
    end

    subgraph "Production (Manual)"
        E["Run workflow<br/>en GitHub"]
        F["Runner production<br/>ejecuta deploy.sh"]
    end

    C -->|push a staging| D
    C -->|workflow_dispatch<br/>en main| E
    E -->|click Approve| F

    style D fill:#90EE90
    style F fill:#FFB6C6

Paso 1: Configurar Runner Self-Hosted

1.1 En el Servidor Staging

# 1. Crear directorio de trabajo
mkdir -p /opt/github-runner-staging
cd /opt/github-runner-staging

# 2. Descargar runner (verificar versión más reciente)
curl -o actions-runner-linux-x64.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz
tar xzf ./actions-runner-linux-x64.tar.gz
rm actions-runner-linux-x64.tar.gz

# 3. En GitHub: Settings → Actions → Runners → New self-hosted runner
#    Copia el token (válido 1 hora)

# 4. Ejecutar configuración
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token <TOKEN>

# Respuestas:
# - name: staging
# - work folder: _work (default OK)
# - labels: staging ⚠️ IMPORTANTE: debe coincidir con runs-on
# - default (default OK)

1.2 En el Servidor Producción

Repite los pasos anteriores con: - Directorio: /opt/github-runner-production - Name: production - Labels: production (o el label que uses en tu workflow)

1.3 Instalar como Servicio Systemd

cd /opt/github-runner-staging (o production)

# Instalar
sudo ./svc.sh install

# Iniciar
sudo systemctl start actions-runner
sudo systemctl enable actions-runner

# Verificar
sudo systemctl status actions-runner
sudo journalctl -u actions-runner -f  # Ver logs en tiempo real

Verificación en GitHub: Ve a: Settings → Actions → Runners Debes ver ambos runners en verde ("Idle")


Paso 2: Configurar SSH para Git

El runner necesita poder hacer git fetch del repositorio sin contraseña.

2.1 Generar SSH Keys

⚠️ IMPORTANTE: Las SSH keys deben estar en el HOME del user del runner, NO en /opt/github-runner/.ssh/

Cuando configuras el runner con ./config.sh, systemd lo ejecuta como un user específico (ej: _svc_actions_staging). SSH busca las keys en ~/.ssh/ de ese user.

# Verificar home del user
sudo -u _svc_actions_staging echo ~
# Output: /home/_svc_actions_staging (o similar)

# Generar SSH key en el home correcto
sudo mkdir -p /home/_svc_actions_staging/.ssh
sudo ssh-keygen -t rsa -f /home/_svc_actions_staging/.ssh/id_rsa -N "" -C "staging-runner"

# Permisos correctos (CRÍTICO)
sudo chown -R _svc_actions_staging:_svc_actions_staging /home/_svc_actions_staging/.ssh
sudo chmod 700 /home/_svc_actions_staging/.ssh
sudo chmod 600 /home/_svc_actions_staging/.ssh/id_rsa

2.2 Agregar Public Key en GitHub

# Copiar la public key
sudo cat /home/_svc_actions_staging/.ssh/id_rsa.pub

En GitHub: 1. Repo → Settings → Deploy Keys 2. Click "Add deploy key" 3. Title: staging-runner 4. Key: (pega la output anterior) 5. ✅ Allow write access 6. Click "Add key"

2.3 Verificar SSH Access

# Test 1: Autenticación SSH
sudo -u _svc_actions_staging ssh -T git@github.com
# Output: "Hi YOUR_ORG/YOUR_REPO! You've successfully authenticated..."

# Test 2: Git clone
sudo -u _svc_actions_staging git clone git@github.com:YOUR_ORG/YOUR_REPO.git /tmp/test
rm -rf /tmp/test

Paso 3: Autenticar Acceso a GHCR (Registry Privado)

El runner necesita poder hacer docker login y docker pull de imágenes privadas.

3.1 Crear Personal Access Token (PAT)

En GitHub: 1. Settings → Developer settings → Personal access tokens (classic) 2. Click "Generate new token" 3. Nombre: ghcr-runner-pat 4. Permisos mínimos: - ✅ read:packages (para pull) - ✅ repo (para acceder al repo) 5. Copy el token (no lo pierdan, no se puede ver después)

3.2 Login a GHCR en el Runner

# Opción 1: Login interactivo (NO recomendado para runners)
docker login ghcr.io

# Opción 2: Via stdin (más seguro)
echo "YOUR_PAT_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin

# Verificar
docker pull ghcr.io/your-org/your-image:latest

# Logout cuando termines pruebas
docker logout ghcr.io

Para runners: El login se guarda en ~/.docker/config.json del user del runner. Systemd lo ejecuta como ese user, así que el login persiste entre execuciones.


Paso 4: Configurar GitHub Environments (Opcional)

Para requerir aprobación manual antes de deployar a producción (solo en GitHub Enterprise).

En GitHub: 1. Repo → Settings → Environments 2. Click "New environment" 3. Nombre: production 4. (Si es Enterprise) Click "Add protection rules" - ✅ Require reviewers - ✅ Restrict to selected branches: main

Nota: Plan Team/Pro no tiene "Require reviewers". Usa workflow_dispatch manual como alternativa.


Paso 5: Workflow CI/CD Estándar

name: Build & Deploy

on:
  push:
    branches: [staging, main]
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/checkout@v4
      - name: Build and push image
        run: |
          docker build -t ghcr.io/your-org/your-image:tag .
          docker push ghcr.io/your-org/your-image:tag

  deploy-staging:
    needs: build
    runs-on: [self-hosted, staging]  # Label debe coincidir
    if: github.ref == 'refs/heads/staging'
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        run: ./deploy.sh

  deploy-prod:
    needs: build
    runs-on: [self-hosted, production]  # Label debe coincidir
    if: github.event_name == 'workflow_dispatch'  # SOLO manual
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        run: ./deploy.sh

Checklist de Configuración

  • [ ] Runner staging instalado y registrado (Settings → Actions → Runners)
  • [ ] Runner production instalado y registrado
  • [ ] SSH keys generadas en el home correcto del user del runner
  • [ ] Deploy Keys agregadas en GitHub para ambos runners
  • [ ] ssh -T git@github.com funciona como el user del runner
  • [ ] PAT creado con permisos read:packages y repo
  • [ ] docker login ghcr.io funciona en ambos servidores
  • [ ] Workflow .github/workflows/build-deploy.yml existe
  • [ ] Labels en runs-on coinciden con los labels del runner
  • [ ] Primer push a staging y observar que deploy-staging corre

Troubleshooting

Runner no aparece en GitHub

# En el servidor
sudo systemctl restart actions-runner
sudo journalctl -u actions-runner -f

# En GitHub: Settings → Actions → Runners
# Si sigue sin aparecer, ejecutar config.sh de nuevo con un token nuevo

SSH no funciona

# Verificar home del user
sudo -u _svc_actions_staging echo ~

# Verificar que las keys están en el lugar correcto
sudo ls -la /home/_svc_actions_staging/.ssh/

# Verificar permisos
sudo stat /home/_svc_actions_staging/.ssh/id_rsa
# Debe ser 0600 (-rw-------)

Docker login falla

# Verificar que el PAT tiene permisos correctos
# PAT debe tener: read:packages + repo

# Logout e intentar de nuevo
docker logout ghcr.io
echo "YOUR_PAT" | docker login ghcr.io -u USERNAME --password-stdin

# Verificar login funcionó
docker pull ghcr.io/your-org/your-image:test

Notas Importantes

  1. SSH Keys deben estar en el home correcto — No en /opt/github-runner/.ssh/, sino en ~/.ssh/ del user que corre el runner
  2. Labels deben coincidir — runs-on: [self-hosted, staging] necesita un runner CON el label staging
  3. PAT tiene expiration — GitHub PATs expiran por defecto. Renovar antes de que expire
  4. Systemd ejecuta como user específico — Las credenciales de Docker se guardan para ese user, no para root

¿Preguntas o aclaraciones necesarias?