chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
---
|
||||
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:
|
||||
- ssh_check_go_infra
|
||||
- ssh_exec_go_infra
|
||||
- ssh_upload_go_infra
|
||||
- rsync_deploy_bash_infra
|
||||
- systemd_generate_unit_go_infra
|
||||
- systemd_install_go_infra
|
||||
- systemd_restart_go_infra
|
||||
- systemd_status_go_infra
|
||||
- vps_setup_app_go_infra
|
||||
- health_check_http_go_infra
|
||||
- ssh_config_read_go_infra
|
||||
- ssh_config_find_go_infra
|
||||
- docker_compose_remote_deploy_bash_infra
|
||||
uses_types:
|
||||
- ssh_conn_go_infra
|
||||
- ssh_config_entry_go_infra
|
||||
- DeployConfig_go_infra
|
||||
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`.
|
||||
Reference in New Issue
Block a user