Railshot
AI deployment & operations platform
Distribuire una app web costruita in locale in ambienti cloud e on-premises predisposti, e poi gestirla.
Una piattaforma IA di deployment e operazioni che porta una app web costruita in locale in ambienti cloud o on-premises predisposti e continua a gestirla.
Cluster operativo e cluster delle app dei clienti sono separati, e il percorso pubblico resta distinto dal controllo dei deployment.
Nel K3s operativo, dashboard, API di prodotto e build controller sono collocati su nodi con ruolo platform e i Job dei runner su un nodo con ruolo build, mentre Argo CD gestisce il deployment. Le app dei clienti girano in un namespace designato di un cluster K3s + Cilium a nodo singolo, separato per ogni target registrato.
Build → pubblicazione dell’immagine
L’API di prodotto richiede un’esecuzione di GitHub Actions, e il runner sul nodo di build prende e controlla il job. Solo un bundle che ha superato gli stessi controlli viene pubblicato su GHCR da un job di release ospitato da GitHub.
API di prodotto · cd.js → bridge.py
railshot-systemesecuzione · applicazione: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlL’API di prodotto richiede un’esecuzione di GitHub Actions con tenant, app, source commit e target. L’API non chiama mai direttamente un Job dei runner.
build controller · CronJob
railshot-systemesecuzione · applicazione: rifornimento dei Job dei runnerrunner Job · hostNetwork · Docker socketrailshot-buildIl CronJob del build controller (ogni minuto, su un nodo platform) crea i Job dei runner nel namespace railshot-build. Il suo Role consente solo get, list e create sui Job più get, update e patch sull’unico Secret di registrazione dei runner; non può creare namespace né altri Secret.
runner Job · hostNetwork · Docker socket
railshot-buildesecuzione · applicazione: presa del jobloop job · self-hosted runnerrailshot-deploy.ymlIl runner sul nodo con ruolo build (hostname esatto, toleration NoSchedule) prende il job loop tramite le sue etichette self-hosted e lo esegue. Il codice sorgente del cliente gira nei container Docker di build e di controllo già esistenti e limitati.
runner Job · hostNetwork · Docker socket
railshot-builddati: bundle testatorelease job · GitHub-hostedrailshot-deploy.ymlIl job loop lascia il bundle che ha superato gli stessi controlli, e il job di release prende solo quel bundle.
release job · GitHub-hosted
railshot-deploy.ymldati: pubblicazione per digestGHCRUn job di release ospitato da GitHub (ubuntu-24.04) pubblica su GHCR l’immagine del bundle testato senza ricompilarla. La pubblicazione delle immagini non avviene mai all’interno del cluster delle app del cliente.
Git di config → Argo → namespace del cliente
Il Git di config trasporta le risorse desiderate e GHCR i byte delle immagini. Argo CD applica solo al namespace del cliente registrato.
API di prodotto · cd.js → bridge.py
railshot-systemdati: workload commit · pushrepository Git di configcd.js esegue bridge.py, che fa commit e push di workload.json solo se registro di pubblicazione e target registrato concordano, poi rilegge la revisione remota. Git trasporta le risorse desiderate; GHCR i byte delle immagini.
repository Git di configdati: revisionArgo CD · Application
argocdL’Application legge solo il proprio repository di config ristretto, il percorso e la revisione.
API di prodotto · cd.js → bridge.py
railshot-systemesecuzione · applicazione: Application syncArgo CD · ApplicationargocdL’Application nominata viene impostata con server-side apply, e si richiede la sync di un’unica revisione esatta senza prune. Il ServiceAccount railshot-product dell’API di prodotto detiene sia il bootstrap di registrazione installato dall’operatore (create per Secret, AppProject e Application in argocd; get, update ed escalate su Role nominati) sia i permessi get e patch aggiunti per nome esatto per ogni target validato. In Kubernetes create non può essere limitato per nome, quindi anche la validazione degli input del prodotto mantiene i nomi nel perimetro.
Argo CD · Application
argocdesecuzione · applicazione: applicazione al namespace<app namespace>Argo CD applica al namespace designato nel cluster del cliente con la credenziale del Secret del cluster registrato. L’AppProject consente solo kind con namespace e nessuna risorsa a livello di cluster.
Deployment · Pod · immagine per digest
<app namespace>dati: pull per digestGHCRL’immagine per digest pubblicata viene scaricata con un Secret di pull referenziato per nome nello stesso namespace. I valori dei Secret non entrano mai nell’artefatto né nel Git di config.
Percorso pubblico delle richieste
Le richieste all’app vanno ingresso L7 del provider → NodePort del nodo → Service → Pod dell’app. L’applicazione delle policy di Cilium è un controllo separato, non un hop che trasporta la richiesta. L’accesso alla dashboard operativa è un percorso separato.
Utente dell’appdati: HTTPS 443Ingresso L7 del provider
Gli utenti dell’app raggiungono l’ingresso L7 del provider tramite l’hostname registrato. Ogni provider implementa il proprio ingresso.
Ingresso L7 del providerdati: NodePortNodePort Service · Local
<app namespace>L’ingresso inoltra all’indirizzo privato e al NodePort del nodo. Il NodePort è quello assegnato alla registrazione del target.
NodePort Service · Local
<app namespace>dati: verso il PodDeployment · Pod · immagine per digest<app namespace>Il Service invia la richiesta al Pod dell’app (Deployment) sullo stesso nodo. Con externalTrafficPolicy Local, l’indirizzo di origine arriva invariato al controllo dei CIDR registrati della NetworkPolicy.
Cilium · Helm · VXLAN
kube-systemesecuzione · applicazione: applicazione della policyNetworkPolicy · CIDR elencati<app namespace>Cilium applica la NetworkPolicy: ingresso solo dai CIDR registrati verso la porta dell’app; nessun egress per impostazione predefinita, salvo un indirizzo di database approvato. Firewall del cloud e regole dell’host restano comunque necessari.
Utente di Railshotdati: dashboard · APIdashboard · nginx
railshot-systemGli utenti arrivano dall’ingresso operativo alla dashboard nginx, e nginx sta davanti all’API di prodotto. Il Service della dashboard è un NodePort solo se viene indicata una node port approvata; l’API resta ClusterIP.
Verifica · osservazione
Il verdetto di deployment si basa solo sullo stato di Argo e sul controllo dell’URL pubblico. Diagnostica dei Pod, log attuali e metriche operative sono segnali separati che non lo cambiano mai.
API di prodotto · cd.js → bridge.py
railshot-systemosservazione: revisione · health · immaginiArgo CD · ApplicationargocdDeployed richiede la revisione di sync dell’Application, Synced e Healthy, l’operation Succeeded, e riferimenti alle immagini nello stato dell’Application di Argo uguali alle immagini per digest del Deployment esaminato. Non è un gate diretto sull’imageID del Pod, e uno stato verde di Argo da solo non stabilisce la corrispondenza.
API di prodotto · cd.js → bridge.py
railshot-systemosservazione: controllo HTTPS pubblicoIngresso L7 del providerL’URL pubblico registrato viene controllato senza redirect. Succeeded viene registrato solo con il verdetto di Argo e questo controllo insieme.
API di prodotto · cd.js → bridge.py
railshot-systemosservazione: Diagnostica del Pod · non il verdettoDeployment · Pod · immagine per digest<app namespace>Dopo il verdetto di Argo e il controllo pubblico, Deployment, ReplicaSet e Pod vengono letti separatamente con la credenziale registrata, e imageID e prontezza vengono aggiunti come diagnostica ready, progressing o unavailable. Una diagnostica fallita o divergente non cambia mai il verdetto deployed o succeeded.
API di prodotto · cd.js → bridge.py
railshot-systemosservazione: tail dei log · 100 righe per PodDeployment · Pod · immagine per digest<app namespace>Solo per l’operation attualmente distribuita, con la config attendibile e la credenziale registrata. Per ogni Pod attuale (al massimo tre) il digest dell’immagine in esecuzione deve corrispondere a quello atteso; poi si leggono fino a 100 righe oscurate per Pod entro un limite di byte. Un tentativo precedente risponde superseded; senza configurazione, not_configured. Non è un’aggregazione di log.
VM di osservazioneosservazione: scrape · NodePortrailshot-observability
Prometheus sulla VM di osservazione esegue lo scrape di node-exporter e kube-state-metrics tramite due NodePort limitati dal firewall. I valori mancanti o obsoleti sono ignoti, non zero.
VM di osservazioneosservazione: Blackbox probeIngresso L7 del provider
probe_success=1 significa un 2xx in quel punto di probe. Non prova né l’accesso di ogni utente né la versione distribuita, e non promuove mai published a deployed.
Operatore: installazione · registrazione
Installazione del cluster, bootstrap di registrazione e input del target, Secret e configurazione del rinnovo delle credenziali sono input dell’operatore, mai parte del payload dell’app del tenant.
Bootstrap dell’operatoreesecuzione · applicazione: rendering della piattaforma · Secretrailshot-system
L’operatore genera la dichiarazione della piattaforma con render-platform.py e fornisce la config operativa privata e i Secret. L’MCP remoto è incluso solo se ne viene fornita l’immagine, il gateway personale (WireGuard) solo con --personal.
Bootstrap dell’operatoreesecuzione · applicazione: bootstrap di registrazioneAppProject · cluster Secret
argocdL’operatore installa in argocd il Role di bootstrap della registrazione e un Role di registrazione vuoto e prepara gli input del target. Gli helper di registrazione eseguiti dall’API di prodotto (environment.py, applications.py) scrivono poi il Secret railshot-<target> con una credenziale circoscritta (clusterResources false, namespace consentiti elencati) e aggiungono i nomi esatti validati al Role di registrazione. La registrazione per app lega un namespace app-<id> a un AppProject con lo stesso nome.
Bootstrap dell’operatoreesecuzione · applicazione: installazione di K3s · CiliumCilium · Helm · VXLAN
kube-systeminstall-k3s.sh installa un server K3s a nodo singolo (flannel, network policy, traefik e servicelb disattivati; kube-proxy mantenuto), e Cilium viene installato come release Helm (kubeProxyReplacement false, VXLAN, Hubble disattivato).
CronJob di rinnovo · ogni 2 h
argocdesecuzione · applicazione: TokenRequest · 6 hSA railshot-argocd · token<app namespace>Il CronJob railshot-credentials richiede ogni due ore un nuovo token di sei ore per la SA del cliente. Il Role lato cliente consente solo token create per la propria SA. Sei ore sono il TTL di un token, non il momento in cui termina l’accesso dell’operatore.
Tutte le connessioni con lo stesso peso. Scegliendo un flusso se ne evidenzia solo il percorso, con passi numerati.
- Continua · direzione di richieste, bundle, immagini e dichiarazioni
- Tratteggiata · richieste di esecuzione, applicazione, installazione, credenziali
- Tratto lungo e punto · lettura di stato, log, probe, metriche; separata dal verdetto
- Linea nera spessa · il flusso scelto; i numeri danno l’ordine
- Riquadro · un namespace, nome nell’intestazione (non un nodo)
- Chip interno · un componente di quel namespace
- Riquadro tratteggiato · fuori dai cluster
- railshot-system · prodotto
- argocd · autorità di deployment
- railshot-build · autorità Docker dell’host
- GitHub · cluster del cliente
La decisione di collocazione del October 2 in docs/architecture (due nodi per ruolo) e il diagramma del README pubblico sono storia; questa mappa segue le dichiarazioni e il codice di 645e28a. Le frecce mostrano causa e direzione, non la prova che ogni percorso dei provider sia attivo ora.
- Namespace e nodi
- Un namespace è un ambito di nomi: né hardware né, di per sé, un confine di sicurezza. I ruoli platform e build sono una collocazione di scheduling tramite nodeSelector e taint; questa mappa non fissa il numero di nodi del cluster operativo. Anche la collocazione dei nodi di Argo CD è un controllo a parte.
- Autorità del nodo di build
- I Job dei runner girano con hostNetwork, il socket Docker dell’host, NET_ADMIN e root. Namespace, taint o RBAC non tolgono l’autorità Docker dell’host, quindi non è né un isolamento dall’amministratore della VM né una sandbox per codice arbitrario.
- Namespace del cliente
- Ogni target registrato usa un namespace non predefinito e un AppProject (per esempio tenant-demo, o app-<id> per app). Si creano solo il Deployment di un servizio (automount del token del ServiceAccount disattivato), un Service NodePort e una NetworkPolicy, più facoltativamente un PVC o un Job di migrazione; i Secret sono solo referenziati per nome (il Secret di pull e un DATABASE_URL approvato), mai creati.
- Ingresso AWS
- Route 53 Alias → ALB condiviso HTTPS 443 → indirizzo privato e NodePort del nodo del cliente. Molte app riusano un nodo, ciascuna con il proprio namespace, Application e NodePort.
- Ingresso GCP
- LB HTTPS globale EXTERNAL_MANAGED → NEG zonale GCE_VM_IP_PORT → NodePort privato della VM esistente. I certificati provengono da Certificate Manager con autorizzazione DNS. Il modulo non possiede né la VM né la VPC, e una sola VM resta un single point of failure.
- Ingresso OpenStack
- Listener TLS Amphora 443 con policy L7 per host e percorso → NodePort HTTP. L’indirizzo predefinito è una VIP privata; Octavia, K3s, il database e il DNS sono fuori dal modulo. Raggiungibilità pubblica e prontezza di Amphora in sede richiedono un’accettazione separata, e con questo modulo non è stato ancora verificato alcun apply in sede.
- Tra provider
- I tre ingressi sono implementazioni separate, senza failover o replica automatici tra provider. Il cluster delle app del cliente è un esempio di target registrato, non l’affermazione che lo stesso deployment giri contemporaneamente su ogni cloud.
- Opzioni dell’operatore
- Il gateway personale (WireGuard) entra nella piattaforma solo se l’operatore attiva --personal; non è un percorso obbligatorio per i cloud dei clienti. Nella modalità diretta supportata la VM di osservazione può condividere la VM di controllo operativa.