8e30e8cf29
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
277 lines
11 KiB
Markdown
277 lines
11 KiB
Markdown
---
|
|
name: deploy_server
|
|
lang: go
|
|
domain: infra
|
|
description: "Servidor de deploy continuo para apps del registry. Recibe webhooks de Gitea, gestiona targets de deploy en operations.db y orquesta deploys a VPS remotos via SSH. Soporta tres estrategias: systemd, systemd-remote y docker-compose."
|
|
tags: [service, deploy, ci, cd, webhook, gitea, ssh, vps, docker-compose, systemd]
|
|
uses_functions: []
|
|
uses_types: []
|
|
framework: "net/http"
|
|
entry_point: "main.go"
|
|
dir_path: "apps/deploy_server"
|
|
---
|
|
|
|
## Estrategias de deploy
|
|
|
|
Tres estrategias disponibles segun el tipo de app:
|
|
|
|
| Estrategia | Flujo | Para que |
|
|
|---|---|---|
|
|
| `systemd` (default) | Build local + rsync + systemctl restart | Apps Go compiladas localmente y subidas al VPS |
|
|
| `systemd-remote` | SSH: git pull + build remoto + systemctl restart | Apps Go cuyo source vive en el VPS (build in-situ) |
|
|
| `docker-compose` | SSH: git pull + docker compose pull + up -d | Stacks Docker Compose en el VPS |
|
|
|
|
### systemd (build local + rsync)
|
|
|
|
```
|
|
SSH check → build local (build_cmd en source_dir) → rsync al VPS
|
|
→ chmod +x binario → systemctl restart → health check
|
|
```
|
|
|
|
### systemd-remote (build remoto)
|
|
|
|
```
|
|
SSH check → git pull origin <branch> en remote_dir
|
|
→ build remoto (build_cmd via SSH) → systemctl restart → health check
|
|
```
|
|
|
|
### docker-compose
|
|
|
|
```
|
|
SSH check → git pull origin <branch> en remote_dir
|
|
→ docker compose [-f extra...] pull → docker compose up -d → health check
|
|
```
|
|
|
|
## Uso
|
|
|
|
```bash
|
|
# Servidor (escucha webhooks y expone API)
|
|
./deploy_server serve --port 9090
|
|
|
|
# CLI: gestionar targets de deploy
|
|
./deploy_server target add --app my_app --host produccion --port 8080 --health /api/health \
|
|
--build "CGO_ENABLED=0 GOOS=linux go build -o my_app ." --strategy systemd
|
|
./deploy_server target list
|
|
./deploy_server target remove my_app
|
|
|
|
# Target con strategy systemd-remote (build en el VPS)
|
|
./deploy_server target add --app agents_and_robots --host localhost \
|
|
--remote-dir /home/ubuntu/CodeProyects/agents_and_robots \
|
|
--binary launcher --build "bash build.sh" \
|
|
--strategy systemd-remote --branch master
|
|
|
|
# Target con strategy docker-compose
|
|
./deploy_server target add --app element_matrix_chat --host localhost \
|
|
--remote-dir /home/ubuntu/CodeProyects/element_matrix_chat \
|
|
--strategy docker-compose --branch master \
|
|
--compose-files "docker-compose.livekit.yml"
|
|
|
|
# CLI: deploy manual
|
|
./deploy_server deploy my_app # deploy a todos los hosts del target
|
|
./deploy_server deploy my_app --host produccion # deploy a un host específico
|
|
|
|
# CLI: setup inicial de una app en un VPS
|
|
./deploy_server setup my_app --host produccion
|
|
|
|
# CLI: estado de servicios remotos
|
|
./deploy_server status my_app # systemd: systemctl status / docker-compose: docker compose ps
|
|
./deploy_server status --all
|
|
```
|
|
|
|
### Flags de target add
|
|
|
|
| Flag | Default | Descripcion |
|
|
|---|---|---|
|
|
| `--app` | (requerido) | Nombre de la app (debe coincidir con el nombre del repo en Gitea para webhooks) |
|
|
| `--host` | (requerido) | Alias SSH de ~/.ssh/config (o `localhost` si deploy_server corre en el mismo host) |
|
|
| `--remote-dir` | /opt/apps/<app> | Directorio en el host remoto donde vive la app |
|
|
| `--binary` | <app> | Nombre del binario (solo para strategies systemd/systemd-remote) |
|
|
| `--build` | "" | Comando de build (local para systemd, remoto para systemd-remote) |
|
|
| `--user` | "" | Usuario systemd del servicio |
|
|
| `--port` | 0 | Puerto del servicio (para health checks) |
|
|
| `--health` | "" | Path del health check (ej: /api/health) |
|
|
| `--env` | {} | Variables de entorno como JSON |
|
|
| `--strategy` | systemd | Estrategia: `systemd`, `systemd-remote`, `docker-compose` |
|
|
| `--source-dir` | "" | Directorio local del source relativo al registry root (override de apps/<app>) |
|
|
| `--branch` | main | Branch de git para strategies remotas (git pull origin <branch>) |
|
|
| `--compose-files` | "" | Archivos compose adicionales separados por coma (ej: "docker-compose.livekit.yml") |
|
|
|
|
## API
|
|
|
|
```
|
|
POST /webhook/push — recibe push de Gitea, detecta app afectada, despliega
|
|
GET /api/targets — lista todos los targets de deploy
|
|
GET /api/targets/:app — detalle de un target
|
|
POST /api/deploy/:app — trigger manual de deploy
|
|
GET /api/status/:app — estado del servicio remoto (systemctl status o docker compose ps)
|
|
GET /api/health — health check del propio deploy_server
|
|
GET /api/logs — ultimos 20 deploy logs (todas las apps)
|
|
GET /api/logs/:app — ultimos 20 deploy logs de una app
|
|
```
|
|
|
|
## Webhook de Gitea
|
|
|
|
El endpoint `POST /webhook/push` recibe payloads de Gitea en formato JSON. El matching funciona en dos pasos:
|
|
|
|
1. **Por paths**: analiza los archivos modificados en los commits. Si algun archivo empieza con `apps/<nombre>/`, extrae `<nombre>` como app afectada.
|
|
2. **Fallback por nombre de repo**: si no hay match por paths, compara `repository.name` del payload contra los targets registrados.
|
|
|
|
El fallback es el mecanismo principal para repos externos (como agents_and_robots o element_matrix_chat) que no viven dentro de un monorepo con estructura `apps/`.
|
|
|
|
### Seguridad del webhook
|
|
|
|
- Si `DEPLOY_WEBHOOK_SECRET` esta seteada, se valida el header `X-Gitea-Signature` (HMAC-SHA256).
|
|
- Si no esta seteada, se acepta cualquier request (solo para desarrollo).
|
|
- El secret debe coincidir exactamente entre la env var y la configuracion del webhook en Gitea.
|
|
|
|
### Configuracion en Gitea
|
|
|
|
Para crear un webhook en Gitea que apunte al deploy_server:
|
|
|
|
```bash
|
|
source bash/functions/infra/gitea_create_webhook.sh
|
|
export GITEA_URL="https://tu-gitea.ejemplo.com"
|
|
export GITEA_TOKEN="<token-api>"
|
|
gitea_create_webhook "<owner>" "<repo>" "http://<ip>:9090/webhook/push" "<webhook_secret>"
|
|
```
|
|
|
|
**Gitea en Docker**: si Gitea corre en un container Docker, `127.0.0.1` apunta al container, no al host. Usar la gateway de la red Docker del container de Gitea:
|
|
|
|
```bash
|
|
# Encontrar la gateway
|
|
docker inspect <gitea_container> --format '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}'
|
|
# Resultado: 10.0.17.1 (ejemplo)
|
|
# URL del webhook: http://10.0.17.1:9090/webhook/push
|
|
```
|
|
|
|
**Gitea ALLOWED_HOST_LIST**: Gitea restringe por defecto las URLs de webhooks. Configurar en `app.ini`:
|
|
|
|
```ini
|
|
[webhook]
|
|
ALLOWED_HOST_LIST = *
|
|
```
|
|
|
|
O listar las IPs/subredes permitidas. Reiniciar Gitea tras el cambio.
|
|
|
|
**Firewall (UFW)**: si el host tiene UFW activo, permitir tráfico desde redes Docker al puerto del deploy_server:
|
|
|
|
```bash
|
|
sudo ufw allow from 10.0.0.0/8 to any port 9090 comment 'deploy_server from Docker'
|
|
```
|
|
|
|
## Despliegue del propio deploy_server
|
|
|
|
deploy_server usa `github.com/mattn/go-sqlite3` (CGO). Para compilar:
|
|
|
|
```bash
|
|
# Local (Linux nativo o WSL)
|
|
CGO_ENABLED=1 go build -o deploy_server .
|
|
|
|
# Cross-compile para Linux desde otra plataforma
|
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o deploy_server_linux .
|
|
```
|
|
|
|
### Instalacion en un VPS
|
|
|
|
```bash
|
|
# 1. Subir binario
|
|
scp deploy_server_linux <host>:/home/ubuntu/CodeProyects/deploy_server/deploy_server
|
|
|
|
# 2. Crear systemd unit
|
|
sudo tee /etc/systemd/system/deploy_server.service << 'EOF'
|
|
[Unit]
|
|
Description=deploy_server CI/CD
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/home/ubuntu/CodeProyects/deploy_server/deploy_server serve --port 9090
|
|
WorkingDirectory=/home/ubuntu/CodeProyects/deploy_server
|
|
User=ubuntu
|
|
Group=ubuntu
|
|
Environment=DEPLOY_WEBHOOK_SECRET=<secret>
|
|
Environment=PATH=/usr/local/go/bin:/usr/bin:/bin
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# 3. Activar
|
|
sudo systemctl daemon-reload && sudo systemctl enable --now deploy_server
|
|
|
|
# 4. Verificar
|
|
curl -s http://127.0.0.1:9090/api/health
|
|
```
|
|
|
|
### SSH a localhost
|
|
|
|
Cuando deploy_server y las apps estan en el mismo VPS, el deploy usa SSH a `localhost`. Requisitos:
|
|
|
|
```bash
|
|
# Generar key si no existe
|
|
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
|
|
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
|
|
# Aceptar host key
|
|
ssh-keyscan localhost >> ~/.ssh/known_hosts
|
|
# Verificar
|
|
ssh -o BatchMode=yes localhost true
|
|
```
|
|
|
|
## Despliegue actual: organic-machine.com
|
|
|
|
deploy_server corre en el VPS organic-machine.com como servicio systemd (`deploy_server.service`), puerto 9090.
|
|
|
|
### Targets registrados
|
|
|
|
| App | Estrategia | Host | Remote dir | Branch | Build |
|
|
|---|---|---|---|---|---|
|
|
| agents_and_robots | systemd-remote | localhost | /home/ubuntu/CodeProyects/agents_and_robots | master | `bash build.sh` |
|
|
| element_matrix_chat | docker-compose | localhost | /home/ubuntu/CodeProyects/element_matrix_chat | master | — |
|
|
| registry_api | docker-compose | localhost | /opt/fn-registry-build/apps/registry_api | master | docker compose build+up |
|
|
|
|
### Webhooks activos
|
|
|
|
Ambos repos en Gitea (`egutierrez/agents_and_robots`, `egutierrez/element_matrix_chat`) tienen webhooks push apuntando a `http://10.0.17.1:9090/webhook/push` (gateway de la red Docker de Gitea).
|
|
|
|
### Tiempos de deploy medidos
|
|
|
|
| App | Trigger | Duracion |
|
|
|---|---|---|
|
|
| agents_and_robots | webhook | ~8.5s (git pull + tests + compile 4 binarios + restart) |
|
|
| element_matrix_chat | webhook | ~2.7s (git pull + docker compose pull + up -d) |
|
|
|
|
### Servicios en el VPS
|
|
|
|
| Servicio | Tipo | Estado |
|
|
|---|---|---|
|
|
| deploy_server | systemd (deploy_server.service) | enabled, active, :9090 |
|
|
| agents_and_robots | systemd (agents_and_robots.service) | enabled, active (launcher) |
|
|
| registry_api | Docker (registry-api container) | running, :8420, HTTPS via Traefik en registry.organic-machine.com |
|
|
|
|
### Infraestructura relevante
|
|
|
|
- **Gitea**: corre en Docker (container `gitea-dgg044oo04woo4ggcsws4gk0`), red `10.0.17.0/24`, gateway `10.0.17.1`
|
|
- **Coolify**: proxy principal en puertos 80/443/8080, gestiona servicios Docker
|
|
- **UFW**: policy DROP, regla `allow from 10.0.0.0/8 to port 9090` para que Docker alcance deploy_server
|
|
- **Webhook secret**: guardado en `pass agentes/deploy-webhook-secret`
|
|
- **Gitea token**: guardado en `pass agentes/egutierrez-token`
|
|
- **Registry API basicAuth**: guardado en `pass registry/basicauth-user` y `pass registry/basicauth-pass`
|
|
- **Registry API token**: guardado en `pass registry/api-token`
|
|
|
|
## Registro en operations.db
|
|
|
|
Dos tablas:
|
|
|
|
- **deploy_targets** (PK: app + host): configuracion de cada target con strategy, branch, compose_files, etc.
|
|
- **deploy_logs**: un registro por cada deploy con app, host, status (success/failure), trigger (manual/api/webhook), error, duration_ms, started_at.
|
|
|
|
## Notas
|
|
|
|
- Los hosts SSH se resuelven via `~/.ssh/config` — el campo `host` en el target es el alias SSH.
|
|
- Para strategy `docker-compose`: usa `docker compose` (v2, sin guion). Verificar con `docker compose version`.
|
|
- Para strategy `systemd-remote`: el `build_cmd` se ejecuta via SSH en el remote_dir, no localmente.
|
|
- El webhook matching por `repository.name` permite que repos externos (no del monorepo) disparen deploys si el nombre del repo coincide con el nombre del target.
|
|
- deploy_server no se auto-despliega. Actualizaciones: cross-compile local + scp + `sudo systemctl restart deploy_server`.
|