Railshot
AI deployment & operations platform
Eine lokal gebaute Web-App in vorbereitete Cloud- und On-Premises-Umgebungen deployen und dann betreiben.
Eine KI-Plattform für Deployment und Betrieb, die eine lokal gebaute Web-App in vorbereitete Cloud- oder On-Premises-Umgebungen bringt und weiter betreibt.
Betriebs- und Kunden-App-Cluster sind getrennt, und der öffentliche Pfad bleibt von der Deployment-Steuerung getrennt.
Im Betriebs-K3s liegen Dashboard, Produkt-API und Build-Controller auf Knoten mit platform-Rolle und Runner-Jobs auf einem Knoten mit build-Rolle, während Argo CD das Deployment übernimmt. Kunden-Apps laufen in einem festgelegten Namespace eines separaten Single-Node-Clusters aus K3s + Cilium für jedes registrierte Ziel.
Build → Image veröffentlichen
Die Produkt-API fordert einen GitHub-Actions-Lauf an, und der Runner auf dem Build-Knoten übernimmt und prüft den Job. Nur ein Bundle, das dieselben Prüfungen bestanden hat, wird von einem auf GitHub gehosteten Release-Job in GHCR veröffentlicht.
Produkt-API · cd.js → bridge.py
railshot-systemAusführen · Anwenden: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlDie Produkt-API fordert einen GitHub-Actions-Lauf mit tenant, app, source commit und target an. Die API ruft nie direkt einen Runner-Job auf.
build controller · CronJob
railshot-systemAusführen · Anwenden: Runner-Job nachfüllenrunner Job · hostNetwork · Docker socketrailshot-buildDer CronJob des Build-Controllers (jede Minute, auf einem platform-Knoten) erstellt Runner-Jobs im Namespace railshot-build. Seine Role erlaubt nur get, list und create für Jobs sowie get, update und patch für das eine Runner-Registrierungs-Secret; Namespaces oder andere Secrets kann er nicht erstellen.
runner Job · hostNetwork · Docker socket
railshot-buildAusführen · Anwenden: Job übernehmenloop job · self-hosted runnerrailshot-deploy.ymlDer Runner auf dem Knoten mit build-Rolle (exakter Hostname, NoSchedule-Toleration) übernimmt den loop-Job anhand seiner self-hosted-Labels und führt ihn aus. Kundenquellcode läuft in den bestehenden eingeschränkten Docker-Build- und Prüfcontainern.
runner Job · hostNetwork · Docker socket
railshot-buildDaten: geprüftes Bundlerelease job · GitHub-hostedrailshot-deploy.ymlDer loop-Job hinterlässt das Bundle, das dieselben Prüfungen bestanden hat, und der Release-Job übernimmt nur dieses Bundle.
release job · GitHub-hosted
railshot-deploy.ymlDaten: per Digest veröffentlichenGHCREin auf GitHub gehosteter Release-Job (ubuntu-24.04) veröffentlicht das Image des geprüften Bundles ohne Neubau in GHCR. Images werden nie innerhalb des Kunden-App-Clusters veröffentlicht.
Config-Git → Argo → Kunden-Namespace
Config-Git trägt die gewünschten Ressourcen, GHCR die Image-Bytes. Argo CD wendet nur auf den registrierten Kunden-Namespace an.
Produkt-API · cd.js → bridge.py
railshot-systemDaten: workload commit · pushConfig-Git-Repositorycd.js führt bridge.py aus, das workload.json nur committet und pusht, wenn Publikationsaufzeichnung und registriertes Ziel übereinstimmen, und dann die Remote-Revision zurückliest. Git trägt die gewünschten Ressourcen, GHCR die Image-Bytes.
Config-Git-RepositoryDaten: revisionArgo CD · Application
argocdDie Application liest nur ihr eingeschränktes Config-Repository, ihren Pfad und ihre Revision.
Produkt-API · cd.js → bridge.py
railshot-systemAusführen · Anwenden: Application syncArgo CD · ApplicationargocdDie benannte Application wird per Server-Side Apply gesetzt, und ein Sync genau einer Revision wird ohne Prune angefordert. Der ServiceAccount railshot-product der Produkt-API hält sowohl den vom Operator installierten Registrierungs-Bootstrap (create für Secrets, AppProjects und Applications in argocd; get, update und escalate auf benannte Roles) als auch get- und patch-Rechte, die für jedes validierte Ziel mit exaktem Namen ergänzt werden. Kubernetes-create lässt sich nicht auf Namen beschränken, daher hält auch die Eingabevalidierung des Produkts die Namen im Rahmen.
Argo CD · Application
argocdAusführen · Anwenden: auf Namespace anwenden<app namespace>Argo CD wendet mit den Zugangsdaten des registrierten Cluster-Secrets auf den festgelegten Namespace im Kunden-Cluster an. Das AppProject erlaubt nur Namespace-gebundene Kinds und keine clusterweiten Ressourcen.
Deployment · Pod · Digest-Image
<app namespace>Daten: Pull per DigestGHCRDas veröffentlichte Digest-Image wird mit einem Pull-Secret gezogen, das im selben Namespace per Name referenziert wird. Secret-Werte gelangen nie in das Artefakt oder das Config-Git.
Öffentlicher Anfragepfad
App-Anfragen laufen L7-Eingang des Providers → NodePort des Knotens → Service → App-Pod. Die Policy-Durchsetzung von Cilium ist eine eigene Kontrolle, kein Hop, der die Anfrage trägt. Der Weg ins Betriebs-Dashboard ist ein separater Pfad.
App-NutzerDaten: HTTPS 443L7-Eingang des Providers
App-Nutzer erreichen den L7-Eingang des Providers über den registrierten Hostnamen. Jeder Provider implementiert seinen eigenen Eingang.
L7-Eingang des ProvidersDaten: NodePortNodePort Service · Local
<app namespace>Der Eingang leitet an die private Adresse und den NodePort des Knotens weiter. Der NodePort ist der bei der Zielregistrierung zugewiesene.
NodePort Service · Local
<app namespace>Daten: zum PodDeployment · Pod · Digest-Image<app namespace>Der Service leitet die Anfrage an den App-Pod (Deployment) auf demselben Knoten. Mit externalTrafficPolicy Local erreicht die Quelladresse die Prüfung der registrierten CIDRs in der NetworkPolicy unverändert.
Cilium · Helm · VXLAN
kube-systemAusführen · Anwenden: Policy durchsetzenNetworkPolicy · gelistete CIDRs<app namespace>Cilium setzt die NetworkPolicy durch: Ingress nur von den registrierten CIDRs zum App-Port; standardmäßig kein Egress, außer zu einer freigegebenen Datenbankadresse. Cloud-Firewalls und Host-Regeln bleiben dennoch nötig.
Railshot-NutzerDaten: Dashboard · APIdashboard · nginx
railshot-systemNutzer kommen über den Betriebseingang zum nginx-Dashboard, und nginx steht vor der Produkt-API. Der Dashboard-Service ist nur dann ein NodePort, wenn ein freigegebener Node-Port angegeben ist; die API bleibt ClusterIP.
Verifizieren · beobachten
Das Deployment-Urteil stützt sich nur auf den Argo-Status und die Prüfung der öffentlichen URL. Pod-Diagnose, aktuelle Logs und Betriebsmetriken sind getrennte Signale, die es nie ändern.
Produkt-API · cd.js → bridge.py
railshot-systemBeobachten: Revision · Health · ImagesArgo CD · ApplicationargocdDeployed erfordert die Sync-Revision der Application, Synced und Healthy, die Operation Succeeded sowie Image-Referenzen im Status der Argo-Application, die den Digest-Images des geprüften Deployments entsprechen. Es ist kein direktes Gate auf die imageID des Pods, und ein grüner Argo-Status allein belegt die Übereinstimmung nicht.
Produkt-API · cd.js → bridge.py
railshot-systemBeobachten: öffentliche HTTPS-PrüfungL7-Eingang des ProvidersDie registrierte öffentliche URL wird ohne Weiterleitungen geprüft. Succeeded wird nur mit Argo-Urteil und dieser Prüfung zusammen aufgezeichnet.
Produkt-API · cd.js → bridge.py
railshot-systemBeobachten: Pod-Diagnose · nicht das UrteilDeployment · Pod · Digest-Image<app namespace>Nach Argo-Urteil und öffentlicher Prüfung werden Deployment, ReplicaSets und Pods mit den registrierten Zugangsdaten getrennt gelesen, und imageID und Bereitschaft werden als Diagnose ready, progressing oder unavailable angehängt. Eine fehlgeschlagene oder abweichende Diagnose ändert nie das Urteil deployed oder succeeded.
Produkt-API · cd.js → bridge.py
railshot-systemBeobachten: Log-Tail · 100 Zeilen pro PodDeployment · Pod · Digest-Image<app namespace>Nur für die aktuell deployte Operation, mit der vertrauenswürdigen Konfiguration und den registrierten Zugangsdaten. Für jeden aktuellen Pod (höchstens drei) muss der Digest des laufenden Images dem erwarteten Digest entsprechen; dann werden bis zu 100 geschwärzte Zeilen pro Pod innerhalb eines Byte-Limits gelesen. Ein älterer Versuch antwortet superseded, ohne Konfiguration not_configured. Dies ist keine Log-Aggregation.
Beobachter-VMBeobachten: Scrape · NodePortsrailshot-observability
Prometheus auf der Beobachter-VM scrapt node-exporter und kube-state-metrics über zwei per Firewall eingeschränkte NodePorts. Fehlende oder veraltete Werte sind unbekannt, nicht null.
Beobachter-VMBeobachten: Blackbox probeL7-Eingang des Providers
probe_success=1 bedeutet ein 2xx an diesem Probe-Standort. Es belegt weder den Zugriff jedes Nutzers noch die deployte Version, und es macht aus published nie deployed.
Operator: Installation · Registrierung
Cluster-Installation, Registrierungs-Bootstrap und Zieleingaben, Secrets sowie die Einrichtung der Zugangsdaten-Erneuerung sind Operator-Eingaben, nie Teil der Payload der Tenant-App.
Operator-BootstrapAusführen · Anwenden: Plattform rendern · Secretrailshot-system
Der Operator rendert die Plattform-Deklaration mit render-platform.py und stellt die private Betriebskonfiguration und Secrets bereit. Das Remote-MCP ist nur enthalten, wenn sein Image bereitgestellt wird, das persönliche Gateway (WireGuard) nur mit --personal.
Operator-BootstrapAusführen · Anwenden: Registrierungs-BootstrapAppProject · cluster Secret
argocdDer Operator installiert in argocd die Registrierungs-Bootstrap-Role und eine leere Registrierungs-Role und bereitet die Zieleingaben vor. Die Registrierungs-Helfer, die die Produkt-API ausführt (environment.py, applications.py), schreiben dann das Secret railshot-<target> mit eingeschränkten Zugangsdaten (clusterResources false, erlaubte Namespaces aufgelistet) und ergänzen die validierten exakten Namen in der Registrierungs-Role. Die Registrierung pro App bindet einen Namespace app-<id> an ein gleichnamiges AppProject.
Operator-BootstrapAusführen · Anwenden: K3s · Cilium installierenCilium · Helm · VXLAN
kube-systeminstall-k3s.sh installiert einen Single-Node-K3s-Server (flannel, Network Policy, traefik und servicelb aus; kube-proxy beibehalten), und Cilium wird als Helm-Release installiert (kubeProxyReplacement false, VXLAN, Hubble aus).
Erneuerungs-CronJob · alle 2 h
argocdAusführen · Anwenden: TokenRequest · 6 hrailshot-argocd SA · Token<app namespace>Der CronJob railshot-credentials fordert alle zwei Stunden ein neues Sechs-Stunden-Token für das Kunden-SA an. Die Role auf Kundenseite erlaubt nur token create für ihr eigenes SA. Sechs Stunden sind die TTL eines Tokens, nicht das Ende des Operator-Zugriffs.
Alle Verbindungen in gleicher Stärke. Wird ein Ablauf gewählt, ist nur sein Pfad hervorgehoben, mit nummerierten Schritten.
- Durchgezogen · Richtung, in der Anfragen, Bundles, Images und Deklarationen fließen
- Gestrichelt · Ausführungsanfragen, Anwenden, Installation, Zugangsdaten
- Lang strichpunktiert · Lesen von Status, Logs, Probes, Metriken; getrennt vom Urteil
- Dicke schwarze Linie · der gewählte Ablauf; Nummern geben die Reihenfolge an
- Kasten · ein Namespace, Name in der Kopfzeile (kein Knoten)
- Innerer Chip · eine Komponente in diesem Namespace
- Gestrichelter Kasten · außerhalb der Cluster
- railshot-system · Produkt
- argocd · Deployment-Befugnis
- railshot-build · Host-Docker-Befugnis
- GitHub · Kunden-Cluster
Die Platzierungsentscheidung vom October 2 in docs/architecture (zwei Rollenknoten) und das Diagramm im öffentlichen README sind Geschichte; diese Karte folgt den Deklarationen und dem Code von 645e28a. Pfeile zeigen Ursache und Richtung, keinen Beleg, dass jeder Provider-Pfad jetzt aktiv ist.
- Namespaces und Knoten
- Ein Namespace ist ein Namensraum: weder Hardware noch für sich eine Sicherheitsgrenze. Die Rollen platform und build sind eine Scheduling-Platzierung per nodeSelector und Taint; diese Karte legt die Knotenzahl des Betriebsclusters nicht fest. Auch die Knotenplatzierung von Argo CD ist eine eigene Prüfung.
- Befugnisse des Build-Knotens
- Runner-Jobs laufen mit hostNetwork, dem Docker-Socket des Hosts, NET_ADMIN und root. Namespace, Taint oder RBAC entfernen die Host-Docker-Befugnis nicht, daher ist dies weder eine Isolation vom VM-Administrator noch eine Sandbox für beliebigen Code.
- Kunden-Namespace
- Jedes registrierte Ziel nutzt einen Nicht-Standard-Namespace und ein AppProject (etwa tenant-demo oder app-<id> pro App). Erstellt werden nur das Deployment eines Dienstes (Automount des ServiceAccount-Tokens aus), ein NodePort-Service und eine NetworkPolicy, dazu optional ein PVC oder ein Migrations-Job; Secrets werden nur per Name referenziert (das Pull-Secret und eine freigegebene DATABASE_URL), nie erstellt.
- AWS-Eingang
- Route 53 Alias → gemeinsamer ALB HTTPS 443 → private Adresse und NodePort des Kundenknotens. Viele Apps nutzen einen Knoten gemeinsam, jede mit eigenem Namespace, eigener Application und eigenem NodePort.
- GCP-Eingang
- Globaler EXTERNAL_MANAGED HTTPS LB → zonale GCE_VM_IP_PORT NEG → privater NodePort der bestehenden VM. Zertifikate stammen aus dem Certificate Manager mit DNS-Autorisierung. Das Modul besitzt weder die VM noch die VPC, und eine VM bleibt ein Single Point of Failure.
- OpenStack-Eingang
- Amphora-TLS-Listener 443 mit L7-Policies für Host und Pfad → HTTP-NodePort. Die Standardadresse ist eine private VIP; Octavia, K3s, die Datenbank und DNS liegen außerhalb des Moduls. Öffentliche Erreichbarkeit und Amphora-Bereitschaft vor Ort brauchen eine eigene Abnahme, und mit diesem Modul wurde noch kein Apply vor Ort verifiziert.
- Zwischen Providern
- Die drei Eingänge sind getrennte Implementierungen, ohne automatisches Failover oder Replikation zwischen Providern. Der Kunden-App-Cluster ist ein Beispiel für ein registriertes Ziel, keine Behauptung, dass dasselbe Deployment gleichzeitig in jeder Cloud läuft.
- Optionen des Operators
- Das persönliche Gateway (WireGuard) wird nur Teil der Plattform, wenn der Operator --personal aktiviert; es ist kein Pflichtpfad für Kunden-Clouds. Im unterstützten Direktmodus kann die Beobachter-VM die Betriebs-Control-VM mitnutzen.