Proyecto de infraestructura: montar un pipeline CI/CD completo y autoalojado, desde cero, sin depender de servicios externos como GitHub Actions o GitLab CI.
1. Introducción
¿Qué es CI/CD? Metodología que automatiza la integración y el despliegue de código.
- CI (Integración Continua): ejecuta tests automáticamente en cada cambio de código.
- CD (Despliegue Continuo): despliega a producción automáticamente si los tests pasan.
Objetivo del proyecto:
- Almacenar el código en un servidor Git privado (Gitea)
- Detectar automáticamente cambios en el repositorio
- Ejecutar tareas de despliegue automáticamente
- Actualizar el servidor web sin intervención manual
2. Instalación de Gitea
Crear el contenedor LXC (Osiris)
1sudo lxc-create -n osiris -t download -- -d debian -r bookworm -a amd64
2lxc-start -n osiris
3lxc-attach -n osiris
Configurar red del contenedor
IP estática en la DMZ (192.168.0.5):
1nano /etc/systemd/network/10-eth0.network
1[Match]
2Name=eth0
3
4[Network]
5Address=192.168.0.5/24
6Gateway=192.168.0.1
7DNS=192.168.0.2
1systemctl enable systemd-networkd
2systemctl start systemd-networkd
Instalar dependencias
1apt update && apt install -y git wget sqlite3
Crear usuario para Gitea
Por seguridad, Gitea no se ejecuta como root:
1useradd -r -d /var/lib/gitea -s /bin/bash -m gitea
Descargar e instalar Gitea
1cd /tmp
2wget https://dl.gitea.com/gitea/1.21.7/gitea-1.21.7-linux-amd64 -O gitea
3chmod +x gitea
4mv gitea /usr/local/bin/
Crear los directorios necesarios
1mkdir -p /var/lib/gitea/{custom,data,log}
2mkdir -p /etc/gitea
3chown -R gitea:gitea /var/lib/gitea
4chown -R gitea:gitea /etc/gitea
Crear servicio systemd
1nano /etc/systemd/system/gitea.service
1[Unit]
2Description=Gitea (Git with a cup of tea)
3After=network.target
4
5[Service]
6Type=simple
7User=gitea
8Group=gitea
9WorkingDirectory=/var/lib/gitea
10ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
11Restart=always
12Environment=USER=gitea HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea
13
14[Install]
15WantedBy=multi-user.target
Explicación de cada sección:
[Unit]→ metadatos: descripción y que arranque después de que la red esté lista (After=network.target)[Service]→Type=simple(proceso en primer plano),User=gitea(no root),Restart=always(se reinicia si se cae),Environmentcon las variables necesarias[Install]→WantedBy=multi-user.target, se inicia en modo multiusuario
1systemctl daemon-reload
2systemctl enable gitea
3systemctl start gitea
4systemctl status gitea

Configurar DNAT en RA
Para acceder a Gitea desde internet, redirigir el puerto 3000:
1sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 3000 -j DNAT --to-destination 192.168.0.5:3000
2sudo iptables-save > /etc/iptables/rules.v4
Configurar DNS
Para acceder con nombre de dominio en vez de IP. Conectar a ISIS:
1lxc-attach -n isis
Zona DNS externa:
1nano /etc/bind/zones/db.externa.josemanuel.gonzalonazareno.org
1git IN A 172.22.201.179
Zona DNS interna:
1nano /etc/bind/zones/db.josemanuel.gonzalonazareno.org
1git IN A 192.168.0.5
1systemctl restart bind9
Configuración inicial de Gitea
Acceder vía navegador a http://172.22.201.179:3000 y completar el instalador:

Cuenta de administrador:
- Usuario:
josemanuel - Email:
***@gmail.com - Contraseña:
********

Crear un repositorio de prueba
En la interfaz de Gitea: botón + → Nuevo repositorio → Nombre: Web-Demo → Descripción:
“Sitio web de prueba para CI/CD” → marcar “Inicializar repositorio” → Licencia: MIT License →
.gitignore vacío → Crear repositorio.
3. Instalación de Drone CI
Crear el contenedor LXC (Thoth)
1lxc-create -n thoth -t download -- -d debian -r bookworm -a amd64
2lxc-start -n thoth
3lxc-attach -n thoth
Configurar la red
IP estática 192.168.0.6:
1nano /etc/systemd/network/10-eth0.network
1[Match]
2Name=eth0
3
4[Network]
5Address=192.168.0.6/24
6Gateway=192.168.0.1
7DNS=192.168.0.2
1systemctl enable systemd-networkd
2systemctl start systemd-networkd
Instalar Docker
Drone se ejecuta dentro de contenedores Docker:
1apt update && apt install -y ca-certificates curl gnupg
2install -m 0755 -d /etc/apt/keyrings
3curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
4chmod a+r /etc/apt/keyrings/docker.asc
5echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
6apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Configurar LXC para Docker anidado
Docker dentro de LXC requiere configuración especial; si no, Thoth no deja crear contenedores Docker. En RA, editar la configuración del contenedor:
1nano /var/lib/lxc/thoth/config
1lxc.apparmor.profile = unconfined
2lxc.cgroup2.devices.allow = a
3lxc.cap.drop =
4lxc.mount.auto = proc:rw sys:rw cgroup:rw
1lxc-stop -n thoth
2lxc-start -n thoth
3lxc-attach -n thoth
Generar una contraseña compartida
Drone Server y Runner se comunican mediante un secreto compartido:
1openssl rand -hex 16

Crear aplicación OAuth en Gitea
Drone necesita autenticarse con Gitea vía OAuth:
- Abrir Gitea → login como
josemanuel - Avatar → Configuración → Aplicaciones
- Sección “Administrar aplicaciones OAuth2” → Crear una nueva aplicación OAuth2
- Nombre:
Drone CI; URL de redirección:http://drone.josemanuel.gonzalonazareno.org:8080/login - Crear aplicación
OAuth es un protocolo de autorización que permite a Drone acceder a Gitea sin necesitar la contraseña del usuario.
Crear configuración de Drone Server
1mkdir -p /var/lib/drone
2nano /root/drone.env
1DRONE_GITEA_SERVER=http://git.josemanuel.gonzalonazareno.org:3000
2DRONE_GITEA_CLIENT_ID=********
3DRONE_GITEA_CLIENT_SECRET=********
4DRONE_RPC_SECRET=********
5DRONE_SERVER_HOST=drone.josemanuel.gonzalonazareno.org:8080
6DRONE_SERVER_PROTO=http
7DRONE_DATABASE_DRIVER=sqlite3
8DRONE_DATABASE_DATASOURCE=/data/database.sqlite
9DRONE_USER_CREATE=username:josemanuel,admin:true
10DRONE_SERVER_PORT=:8080
Explicación de cada variable:
DRONE_GITEA_SERVER→ URL de GiteaDRONE_GITEA_CLIENT_ID/DRONE_GITEA_CLIENT_SECRET→ credenciales OAuth de GiteaDRONE_RPC_SECRET→ secreto para la comunicación Server↔RunnerDRONE_SERVER_HOST/DRONE_SERVER_PROTO→ dominio público y protocolo de DroneDRONE_DATABASE_DRIVER/DRONE_DATABASE_DATASOURCE→ base de datos (SQLite)DRONE_USER_CREATE→ crea automáticamente un usuario adminDRONE_SERVER_PORT→ puerto interno
Ejecutar Drone Server con Docker
1docker network create drone-network
2
3docker run \
4 --privileged \
5 --network=drone-network \
6 --volume=/var/lib/drone:/data \
7 --env-file=/root/drone.env \
8 --publish=8080:8080 \
9 --restart=always \
10 --detach=true \
11 --name=drone \
12 drone/drone:2
Configurar DNAT y DNS para Drone
1sudo iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 8080 -j DNAT --to-destination 192.168.0.6:8080
2sudo iptables-save > /etc/iptables/rules.v4
1lxc-attach -n isis
2nano /etc/bind/zones/db.externa.josemanuel.gonzalonazareno.org
1drone IN A 172.22.201.179
1nano /etc/bind/zones/db.josemanuel.gonzalonazareno.org
1drone IN A 192.168.0.6
1systemctl restart bind9
Acceder a Drone
1http://drone.josemanuel.gonzalonazareno.org:8080
Instalar Drone Runner
El Runner es el que ejecuta las tareas:
1lxc-attach -n thoth
2nano /root/drone-runner.env
1DRONE_RPC_PROTO=http
2DRONE_RPC_HOST=drone:8080
3DRONE_RPC_SECRET=********
4DRONE_RUNNER_CAPACITY=2
5DRONE_RUNNER_NAME=docker-runner
Explicación:
DRONE_RPC_PROTO/DRONE_RPC_HOST→ protocolo y nombre del contenedor Drone Server (resuelto por la red Dockerdrone-network)DRONE_RPC_SECRET→ mismo secreto que el ServerDRONE_RUNNER_CAPACITY→ máximo de builds simultáneosDRONE_RUNNER_NAME→ nombre identificativo del runner
1docker run \
2 --privileged \
3 --network=drone-network \
4 --volume=/var/run/docker.sock:/var/run/docker.sock \
5 --env-file=/root/drone-runner.env \
6 --restart=always \
7 --detach=true \
8 --name=runner \
9 drone/drone-runner-docker:1
4. Configuración del Pipeline
Configurar repositorio en Anubis
Clonar el repositorio desde Gitea:
1ssh anubis
2git config --global user.name "Jose Manuel"
3git config --global user.email "***@gmail.com"
4cd /var/www/html
5sudo git clone http://git.josemanuel.gonzalonazareno.org:3000/josemanuel/Web-Demo.git web-demo

Configurar Apache para servir este directorio:
1sudo nano /etc/httpd/conf.d/web-demo.conf
1<VirtualHost *:80>
2 ServerName www.josemanuel.gonzalonazareno.org
3 DocumentRoot /var/www/html/web-demo
4
5 <Directory /var/www/html/web-demo>
6 AllowOverride All
7 Require all granted
8 </Directory>
9
10 ErrorLog /var/log/httpd/web-demo-error.log
11 CustomLog /var/log/httpd/web-demo-access.log combined
12</VirtualHost>
1sudo chown -R apache:apache /var/www/html/web-demo
2sudo chmod -R 755 /var/www/html/web-demo
3sudo systemctl restart httpd
Crear un index con una plantilla
1cd web-demo
2sudo nano index.html
1<!DOCTYPE html>
2<html lang="es">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>Web Demo - CI/CD Pipeline</title>
7<style>
8body {
9 font-family: Arial, sans-serif;
10 max-width: 800px;
11 margin: 50px auto;
12 padding: 20px;
13 background: #f0f0f0;
14}
15.container {
16 background: white;
17 padding: 30px;
18 border-radius: 10px;
19 box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20}
21h1 { color: #2c3e50; }
22.version { color: #7f8c8d; font-size: 14px; }
23</style>
24</head>
25<body>
26<div class="container">
27 <h1>Pipeline CI/CD Funcionando</h1>
28 <p>Esta página se despliega automáticamente desde Gitea.</p>
29</div>
30</body>
31</html>
1sudo git add index.html
2sudo git commit -m "Añadido index.html - versión 1.0"
3sudo git push origin main
Al hacer push, pide las credenciales de Gitea.

Configurar clave SSH para despliegue
Para que Drone pueda desplegar automáticamente en Anubis, necesita autenticarse por SSH:
1ssh-keygen -t ed25519 -f ~/.ssh/drone_deploy -N ""
Configurar secreto en Drone
- En Drone → repositorio
josemanuel/Web-Demo→ Settings → Secrets → New Secret - Rellenar: Name:
ssh_key; Value: la clave privada completa; Pull Request Access: Disabled - Create
Los secretos en Drone se almacenan encriptados y solo están disponibles durante la ejecución del pipeline — es una forma segura de manejar credenciales.

Activar repositorio en Drone
En Drone → repositorio → Settings → General: Project Visibility: Public; Configuration:
.drone.yml; Timeout: 60 minutes → Guardar cambios → Activate Repository.

Crear archivo .drone.yml
Define qué hacer cuando hay un push. En Anubis, dentro de web-demo:
1cd /var/www/html/web-demo
2sudo nano .drone.yml
1kind: pipeline
2type: docker
3name: deploy-web
4
5steps:
6- name: deploy-to-anubis
7 image: appleboy/drone-ssh
8 settings:
9 host: 172.16.0.200
10 username: josemanuel
11 key:
12 from_secret: ssh_key
13 port: 22
14 script:
15 - cd /var/www/html/web-demo
16 - git pull origin main
17 - echo "Desplegado exitosamente en $(date)"
Explicación del fichero:
kind: pipeline/type: docker→ pipeline que se ejecuta en contenedores Dockername: deploy-web→ nombre descriptivosteps→ lista de pasos; aquí un único pasodeploy-to-anubisimage: appleboy/drone-ssh→ imagen que ejecuta comandos por SSHhost/username/port→ datos de conexión al servidor de desplieguekey: from_secret: ssh_key→ usa la clave SSH guardada como secretoscript→ comandos remotos: entra al directorio, hacegit pully confirma con un mensaje
Subir .drone.yml a Gitea
1sudo git add .drone.yml
2sudo git commit -m "Añadido pipeline CI/CD"
3sudo git push origin main
Este push dispara automáticamente el pipeline en Drone.
5. Pruebas y Verificación
Verificar build en Drone
Drone → josemanuel/Web-Demo → Builds. Debería verse un build ejecutándose o completado.

Ver logs del build
Al hacer clic en el build se ven los logs detallados de cada paso (clone, deploy-to-anubis).

Despliegue automático completo
Prueba de extremo a extremo: modificar index.html en Anubis y comprobar que se despliega
solo.
1cd /var/www/html/web-demo
2sudo nano index.html
3sudo git add index.html
4sudo git commit -m "Actualizado a versión xxx"
5sudo git push origin main
Verificación: en Drone aparece un build nuevo automáticamente, y
http://www.josemanuel.gonzalonazareno.org muestra la versión nueva sin haber copiado
archivos a mano.

Conclusión
Con esto queda montado un pipeline CI/CD autoalojado y funcional: Gitea como servidor Git
privado, Drone como motor de CI/CD (Server + Runner sobre Docker), y un despliegue
automático por SSH a un servidor Apache cada vez que se hace push a main.
