RAILSHOT.
Zurück zum Saal

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.

2026.09.28 bis 2026.10.04 · Abgeschlossen

Teamprojekt · mein Anteil: CI-Planung und -Implementierung, CSP-Cluster und Ansible-Schnittstelle, Betrieb, Code-Review und Integration im Team

SoftBank Hackathon 2026 powered by KOREC & ProgateTeilnahme an der Vorrunde · Bekanntgabe der Finalauswahl am 2026.10.26 (Stand 2026.10.07)

  • WebImplementiert · Hackathon
  • MCP-ServerImplementiert · remote · lokal

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.

Clusterkarte · gezeichnet nach den Deklarationen und dem Code im öffentlichen Commit 645e28a
Clusterkarte von Railshot: Betrieb und KundenDer Betriebs-K3s-Cluster und ein registrierter Kunden-App-Cluster. Im Betriebscluster platziert der Namespace railshot-system Dashboard, Produkt-API, Remote-MCP (nur wenn sein Image bereitgestellt wird) und Build-Controller auf Knoten mit platform-Rolle; Runner-Jobs im Namespace railshot-build übernehmen GitHub-Actions-Jobs auf dem getainteten Knoten mit build-Rolle. Ein auf GitHub gehosteter Release-Job veröffentlicht das geprüfte Bundle in GHCR. Die Produkt-API committet den Workload in das Config-Git und synchronisiert eine Application im Namespace argocd, und Argo CD wendet ein Deployment, einen NodePort-Service und eine NetworkPolicy auf den im Kunden-Cluster registrierten Namespace an. App-Anfragen kommen über einen L7-Eingang des Providers zu NodePort und Service des Knotens und dann zum App-Pod. Das Deployment-Urteil stützt sich nur auf den Argo-Status und die Prüfung der öffentlichen URL; Pod-Diagnose, Logs und Metriken werden als getrennte Signale gelesen. Installations- und Registrierungseingaben des Operators bleiben von der Payload der Tenant-App getrennt.Railshot-NutzerDashboard · MCPOperator-BootstrapInstallieren · Registrieren · SecretGHCRDigest-ImagesConfig-Git-Repositoryworkload.json · revisionL7-Eingang des ProvidersALB · GCP LB · AmphoraApp-Nutzeröffentliches HTTPSBeobachter-VMaußerhalb des Kunden-K3s · Composerailshot-systemdashboard · nginxRemote-MCP · nur mit Imagebuild controller · CronJobProdukt-API · cd.js → bridge.pyargocdArgo CD · ApplicationAppProject · cluster SecretErneuerungs-CronJob · alle 2 hrailshot-buildrunner Job · hostNetwork · Docker socketrailshot-deploy.ymlloop job · self-hosted runnerrelease job · GitHub-hosted<app namespace>Deployment · Pod · Digest-ImageNodePort Service · LocalNetworkPolicy · gelistete CIDRsrailshot-argocd SA · Tokenkube-systemCilium · Helm · VXLANrailshot-observabilitynode-exporterkube-state-metricsBetriebscluster · K3sAWS · RollenknotenKnoten mit platform-RollenodeSelectorKnoten mit build-RolleNoSchedule taintGitHubActions · GHCRKunden-App-Cluster · Beispieleiner pro registriertem ZielEin K3s-Knoten · serverCilium · kube-proxy beibehalten1workflow_dispatch2Runner-Job nachfüllen3Job übernehmen4geprüftes Bundle5 per Digest veröffentlichen · release job · GitHub-hosted → GHCR51workload commit · push2revision3 Application sync · Produkt-API · cd.js → bridge.py → Argo CD · Application34auf Namespace anwenden5Pull per Digest1HTTPS 4432NodePort3 zum Pod · NodePort Service · Local → Deployment · Pod · Digest-Image34 Policy durchsetzen · Cilium · Helm · VXLAN → NetworkPolicy · gelistete CIDRs45 Dashboard · API · Railshot-Nutzer → dashboard · nginx51 Revision · Health · Images · Produkt-API · cd.js → bridge.py → Argo CD · Application12öffentliche HTTPS-Prüfung3Pod-Diagnose · nicht das Urteil4Log-Tail · 100 Zeilen pro Pod5Scrape · NodePorts6Blackbox probe1Plattform rendern · Secret2Registrierungs-Bootstrap3K3s · Cilium installieren4TokenRequest · 6 h

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.

  1. Produkt-API · cd.js → bridge.pyrailshot-systemAusführen · Anwenden: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.yml

    Die Produkt-API fordert einen GitHub-Actions-Lauf mit tenant, app, source commit und target an. Die API ruft nie direkt einen Runner-Job auf.

  2. build controller · CronJobrailshot-systemAusführen · Anwenden: Runner-Job nachfüllenrunner Job · hostNetwork · Docker socketrailshot-build

    Der 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.

  3. runner Job · hostNetwork · Docker socketrailshot-buildAusführen · Anwenden: Job übernehmenloop job · self-hosted runnerrailshot-deploy.yml

    Der 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.

  4. runner Job · hostNetwork · Docker socketrailshot-buildDaten: geprüftes Bundlerelease job · GitHub-hostedrailshot-deploy.yml

    Der loop-Job hinterlässt das Bundle, das dieselben Prüfungen bestanden hat, und der Release-Job übernimmt nur dieses Bundle.

  5. release job · GitHub-hostedrailshot-deploy.ymlDaten: per Digest veröffentlichenGHCR

    Ein 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.

  1. Produkt-API · cd.js → bridge.pyrailshot-systemDaten: workload commit · pushConfig-Git-Repository

    cd.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.

  2. Config-Git-RepositoryDaten: revisionArgo CD · Applicationargocd

    Die Application liest nur ihr eingeschränktes Config-Repository, ihren Pfad und ihre Revision.

  3. Produkt-API · cd.js → bridge.pyrailshot-systemAusführen · Anwenden: Application syncArgo CD · Applicationargocd

    Die 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.

  4. Argo CD · ApplicationargocdAusfü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.

  5. Deployment · Pod · Digest-Image<app namespace>Daten: Pull per DigestGHCR

    Das 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.

  1. 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.

  2. 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.

  3. 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.

  4. Cilium · Helm · VXLANkube-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.

  5. Railshot-NutzerDaten: Dashboard · APIdashboard · nginxrailshot-system

    Nutzer 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.

  1. Produkt-API · cd.js → bridge.pyrailshot-systemBeobachten: Revision · Health · ImagesArgo CD · Applicationargocd

    Deployed 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.

  2. Produkt-API · cd.js → bridge.pyrailshot-systemBeobachten: öffentliche HTTPS-PrüfungL7-Eingang des Providers

    Die registrierte öffentliche URL wird ohne Weiterleitungen geprüft. Succeeded wird nur mit Argo-Urteil und dieser Prüfung zusammen aufgezeichnet.

  3. Produkt-API · cd.js → bridge.pyrailshot-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.

  4. Produkt-API · cd.js → bridge.pyrailshot-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.

  5. 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.

  6. 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.

  1. 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.

  2. Operator-BootstrapAusführen · Anwenden: Registrierungs-BootstrapAppProject · cluster Secretargocd

    Der 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.

  3. Operator-BootstrapAusführen · Anwenden: K3s · Cilium installierenCilium · Helm · VXLANkube-system

    install-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).

  4. Erneuerungs-CronJob · alle 2 hargocdAusfü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.