Files
Cerbero-mcp/DEPLOYMENT.md
T
AdrianoDev 019b7e3298
ci / ruff lint (push) Successful in 14s
ci / mypy mcp_common (push) Successful in 23s
ci / pytest (push) Successful in 30s
ci / validate compose + Caddyfile (push) Successful in 2m2s
ci / build & push to registry (push) Successful in 1m32s
docs: README + DEPLOYMENT con stato CI/CD funzionante
README aggiunge sezione 'CI/CD pipeline' che descrive i 5 job e i tag
image. DEPLOYMENT espande sez. 1 con dettagli runner Gitea (network
gitea_gitea-internal, image runner-images, label ubuntu-latest) e
configurazione secret user-level REGISTRY_TOKEN con scope write:package.
2026-04-29 09:18:30 +02:00

9.5 KiB
Raw Blame History

Deployment Cerbero_mcp

Guida operativa per il deploy della suite MCP su un VPS pubblico. L'architettura è: Gitea ospita codice + container registry; il VPS produzione non builda nulla, ma fa pull dei container già pronti dalla registry e usa Watchtower per il rollover automatico delle versioni.

┌─────────────────────────┐         ┌──────────────────────────────────┐
│   Gitea git.tielogic.xyz │         │   VPS produzione                 │
│                         │         │   cerbero-mcp.tielogic.xyz       │
│  ┌──────────────────┐   │  push   │                                  │
│  │ Cerbero-mcp repo │───┼─CI/CD──▶│  ┌────────────────────────────┐  │
│  └──────────────────┘   │ image   │  │ docker compose             │  │
│  ┌──────────────────┐   │         │  │ (docker-compose.prod.yml)  │  │
│  │ Container reg.   │◀──┼─ pull ──┤  │   gateway, mcp-*           │  │
│  └──────────────────┘   │         │  │   watchtower (poll 5min)   │  │
│  ┌──────────────────┐   │         │  └────────────────────────────┘  │
│  │ Actions runner   │   │         │                                  │
│  └──────────────────┘   │         │                                  │
└─────────────────────────┘         └──────────────────────────────────┘

1. Pipeline CI/CD (Gitea Actions)

.gitea/workflows/ci.yml ad ogni push su main esegue 5 job:

  1. lint (ruff check services/) — gating, 0 violations.
  2. typecheck (mypy services/common/src/mcp_common) — gating sul modulo comune, warn-only sui servizi.
  3. test (pytest services/) — gating, 478 test verdi.
  4. validate-configdocker compose config -q su docker-compose.yml e docker-compose.prod.yml + caddy validate --config Caddyfile.
  5. build-and-push — solo su push a main (skip su PR):
    • docker login git.tielogic.xyz con ${{ secrets.REGISTRY_TOKEN }} (PAT scope write:package, configurato user-level su Gitea).
    • Builda e pusha 8 image al registry con tag :latest + :sha-X:
      • git.tielogic.xyz/adriano/cerbero-mcp/base
      • git.tielogic.xyz/adriano/cerbero-mcp/gateway
      • git.tielogic.xyz/adriano/cerbero-mcp/mcp-{deribit,bybit,hyperliquid,alpaca,macro,sentiment}
    • Cache Docker buildx via registry stesso (buildcache:<name> per ognuna) → run successivi 5-10× più veloci.

Le PR fanno girare solo lint+typecheck+test+validate-config, niente build/push (gating per merge).

Setup runner Gitea

Il runner act_runner deve girare:

  • Sulla stessa Docker network di Gitea (gitea_gitea-internal o equivalente) altrimenti actions/checkout@v4 non risolve gitea:3000 per il clone.
  • Con bind di /var/run/docker.sock per buildare image dai workflow.
  • Image dei job mappata a docker.gitea.com/runner-images:ubuntu-22.04 (default Gitea, contiene docker CLI + buildx + git + curl + node).
  • Label: almeno ubuntu-latest (usato dal workflow). Altre label (tielogic-ci, ubuntu-22.04, ecc.) opzionali.

Setup secret REGISTRY_TOKEN

Crea un Personal Access Token Gitea con scope write:package (User Settings → Applications → Generate Token). Aggiungilo come secret a livello user (User Settings → Secrets → New Secret) con nome REGISTRY_TOKEN. Tutti i tuoi repo ereditano il secret automaticamente.

2. Setup iniziale del VPS

Pre-requisiti: Docker Engine ≥ 24, docker compose plugin, accesso SSH sudo, dominio DNS A record cerbero-mcp.tielogic.xyz → IP del VPS, porte 80 e 443 aperte sul firewall (per ACME challenge + traffico HTTPS).

a) Login al registry Gitea

Crea un Personal Access Token su Gitea (Settings → Applications → Generate new token) con scope read:package. Quindi sul VPS:

echo "$GITEA_PAT" | docker login git.tielogic.xyz -u <gitea-username> --password-stdin

Le credenziali vengono salvate in ~/.docker/config.json. Watchtower lo bind-monta in sola lettura per fare i pull autenticati.

b) Clona repository (solo per i file di compose, secret e Caddyfile)

sudo mkdir -p /opt/cerbero-mcp && sudo chown $USER /opt/cerbero-mcp
cd /opt/cerbero-mcp
git clone ssh://git@git.tielogic.xyz:222/Adriano/Cerbero-mcp.git .

Il VPS NON ha bisogno di buildare; usa docker-compose.prod.yml che fa solo pull dal registry.

c) Prepara secrets

mkdir -p secrets
# Copia (via scp) i file JSON con cred reali:
#   secrets/deribit.json, bybit.json, alpaca.json, hyperliquid.json,
#   secrets/macro.json, sentiment.json
#   secrets/core.token, observer.token
chmod 600 secrets/*

d) .env con configurazione runtime

Crea /opt/cerbero-mcp/.env:

# Gateway
ACME_EMAIL=adrianodalpastro@tielogic.com
GATEWAY_HTTP_PORT=80
GATEWAY_HTTPS_PORT=443
WRITE_ALLOWLIST="127.0.0.1/32 ::1/128 172.16.0.0/12"

# Image tag — `latest` per auto-update Watchtower, oppure pin a sha-XXXXXXX
IMAGE_TAG=latest
IMAGE_PREFIX=git.tielogic.xyz/adriano/cerbero-mcp

# Environment exchange (true=testnet, false=mainnet)
DERIBIT_TESTNET=true
BYBIT_TESTNET=true
HYPERLIQUID_TESTNET=true
ALPACA_PAPER=true

# Watchtower polling interval (sec). 300=5min default.
WATCHTOWER_POLL_INTERVAL=300

e) Avvio

docker compose -f docker-compose.prod.yml --env-file .env pull
docker compose -f docker-compose.prod.yml --env-file .env up -d
docker compose -f docker-compose.prod.yml logs -f gateway

Caddy chiede automaticamente il certificato Let's Encrypt al primo contatto su https://cerbero-mcp.tielogic.xyz.

3. Auto-update via Watchtower

Watchtower (servizio watchtower nel compose) polla il registry ogni WATCHTOWER_POLL_INTERVAL secondi. Se trova un nuovo digest dietro al tag :latest di un container etichettato com.centurylinklabs.watchtower.enable=true, fa:

  1. docker pull della nuova image
  2. docker stop graceful del container vecchio
  3. docker rm + start del nuovo container con stessa config + secret + volumi
  4. Cleanup image vecchia (WATCHTOWER_CLEANUP=true)

I container con label sono: gateway, mcp-deribit, mcp-bybit, mcp-hyperliquid, mcp-alpaca, mcp-macro, mcp-sentiment. Il container watchtower stesso non si auto-aggiorna (per evitare loop).

Disabilitare auto-update temporaneamente

Pin a uno SHA specifico nel .env:

IMAGE_TAG=sha-6b7b3f7
docker compose -f docker-compose.prod.yml --env-file .env up -d

In questo modo :latest non viene più seguito; per riattivare il rollover automatico ripristina IMAGE_TAG=latest.

Disabilitare auto-update per un singolo servizio

Rimuovi la label com.centurylinklabs.watchtower.enable=true per quel servizio nel compose (oppure imposta =false). Watchtower lo ignora ma continua a tenere aggiornati gli altri.

4. Rollback

# Trova lo SHA della versione precedente
docker images "git.tielogic.xyz/adriano/cerbero-mcp/*" --format "{{.Tag}}"

# Pin nel .env
IMAGE_TAG=sha-XXXXXXX

docker compose -f docker-compose.prod.yml --env-file .env up -d

Watchtower NON downgraderà perché il digest del tag pin corrisponde a quello locale.

5. Smoke test post-deploy

# Da fuori VPS (laptop)
curl -s https://cerbero-mcp.tielogic.xyz/mcp-macro/health
# {"status":"ok",...}

# Test write endpoint allowlist (deve rispondere 403 da IP esterno):
curl -X POST https://cerbero-mcp.tielogic.xyz/mcp-deribit/tools/place_order \
  -H "Authorization: Bearer $(cat secrets/core.token)" \
  -d '{"instrument_name":"BTC-PERPETUAL","side":"buy","amount":1}'
# 403 forbidden: source ip not in allowlist  ← OK

# Sul VPS:
GATEWAY=http://localhost bash tests/smoke/run.sh

6. Sicurezza VPS

  • Firewall ufw: allow 22, 80, 443. Tutto il resto deny in.
  • fail2ban su SSH e (opz) sul log Caddy 401.
  • Secret rotation manuale: aggiorna i file secrets/*.tokendocker compose restart (i token vengono ricaricati al boot di ogni servizio MCP).
  • Audit log in docker compose logs <service> | grep audit_event — per produzione meglio redirezionare a syslog o a un servizio dedicato.

7. Note Traefik / reverse proxy davanti a Gitea

Gitea è esposto via Traefik (ROOT_URL https://git.tielogic.xyz). Per il push di image Docker il reverse proxy deve consentire upload di body grossi (un singolo layer può superare i 100MB).

Traefik default va bene, ma se vedi 413 Request Entity Too Large durante docker push aumenta il limite nel middleware:

# traefik dynamic config
http:
  middlewares:
    gitea-upload:
      buffering:
        maxRequestBodyBytes: 524288000   # 500MB

Applicalo come middleware al router Gitea.

8. Aggiornamento del compose stesso (file YAML)

Watchtower aggiorna le image, non il docker-compose.prod.yml. Se cambi struttura (nuovi servizi, nuove env var) devi:

cd /opt/cerbero-mcp
git pull
docker compose -f docker-compose.prod.yml --env-file .env up -d

Per automatizzare anche questo serve un cron job o uno step CD push-based (vedi backlog).