Railshot
AI deployment & operations platform
Déployer une application web construite en local sur des environnements cloud et sur site préparés, puis l’exploiter.
Une plateforme IA de déploiement et d’exploitation qui mène une application web construite en local vers des environnements cloud ou sur site préparés, puis continue de l’exploiter.
Les clusters d’exploitation et d’applications clientes sont séparés, et le chemin public est tenu à l’écart du contrôle des déploiements.
Dans le K3s d’exploitation, le tableau de bord, l’API produit et le build controller sont placés sur des nœuds de rôle platform et les Jobs runner sur un nœud de rôle build, tandis qu’Argo CD gère le déploiement. Les applications clientes tournent dans un namespace désigné d’un cluster K3s + Cilium à nœud unique, distinct pour chaque cible enregistrée.
Build → publication de l’image
L’API produit demande une exécution GitHub Actions, et le runner du nœud build prend et contrôle le job. Seul un bundle qui a passé les mêmes contrôles est publié sur GHCR par un job de release hébergé par GitHub.
API produit · cd.js → bridge.py
railshot-systemexécuter · appliquer: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlL’API produit demande une exécution GitHub Actions avec tenant, app, commit source et cible. L’API n’appelle jamais directement un Job runner.
build controller · CronJob
railshot-systemexécuter · appliquer: réalimenter les Jobs runnerrunner Job · hostNetwork · Docker socketrailshot-buildLe CronJob du build controller (chaque minute, sur un nœud platform) crée des Jobs runner dans le namespace railshot-build. Son Role n’autorise que get, list et create sur les Jobs, plus get, update et patch sur l’unique Secret d’enregistrement du runner ; il ne peut créer ni namespaces ni autres Secrets.
runner Job · hostNetwork · Docker socket
railshot-buildexécuter · appliquer: prendre le jobloop job · self-hosted runnerrailshot-deploy.ymlLe runner du nœud de rôle build (hostname exact, tolération NoSchedule) prend le job loop par ses labels self-hosted et l’exécute. La source du client tourne dans les conteneurs restreints existants de build Docker et de contrôle.
runner Job · hostNetwork · Docker socket
railshot-builddonnées: bundle testérelease job · GitHub-hostedrailshot-deploy.ymlLe job loop laisse le bundle qui a passé les mêmes contrôles, et le job de release ne prend que ce bundle.
release job · GitHub-hosted
railshot-deploy.ymldonnées: publier par digestGHCRUn job de release hébergé par GitHub (ubuntu-24.04) publie l’image du bundle testé sur GHCR sans la reconstruire. La publication d’images n’a jamais lieu dans le cluster d’application client.
Git de config → Argo → namespace client
Le Git de config porte les ressources souhaitées et GHCR les octets de l’image. Argo CD n’applique qu’au namespace client enregistré.
API produit · cd.js → bridge.py
railshot-systemdonnées: workload commit · pushdépôt Git de configcd.js exécute bridge.py, qui ne commite et ne pousse workload.json que si l’enregistrement de publication et la cible enregistrée concordent, puis relit la révision distante. Git porte les ressources souhaitées ; GHCR porte les octets de l’image.
dépôt Git de configdonnées: revisionArgo CD · Application
argocdL’Application ne lit que son dépôt de config, son chemin et sa révision restreints.
API produit · cd.js → bridge.py
railshot-systemexécuter · appliquer: Application syncArgo CD · ApplicationargocdL’Application nommée est posée par server-side apply, et une sync d’une révision exacte est demandée sans prune. Le ServiceAccount railshot-product de l’API produit détient à la fois le bootstrap d’enregistrement installé par l’opérateur (create sur les Secrets, AppProjects et Applications dans argocd ; get, update et escalate sur des Roles nommés) et des droits get et patch ajoutés par nom exact pour chaque cible validée. Le create de Kubernetes ne peut pas être limité par nom ; la validation des entrées du produit maintient donc aussi les noms dans le périmètre.
Argo CD · Application
argocdexécuter · appliquer: appliquer au namespace<app namespace>Argo CD applique au namespace désigné du cluster client avec l’identifiant du Secret de cluster enregistré. L’AppProject n’autorise que des kinds à portée namespace, sans ressource à portée cluster.
Deployment · Pod · image par digest
<app namespace>données: pull par digestGHCRL’image publiée par digest est tirée avec un Secret de pull référencé par son nom dans le même namespace. Les valeurs des Secrets n’entrent jamais dans l’artefact ni dans le Git de config.
Chemin des requêtes publiques
Les requêtes vers l’application suivent point d’entrée L7 du fournisseur → NodePort du nœud → Service → Pod de l’application. L’application des politiques par Cilium est un contrôle distinct, pas une étape qui transporte la requête. L’accès au tableau de bord d’exploitation suit un autre chemin.
Utilisateur de l’applicationdonnées: HTTPS 443Point d’entrée L7 du fournisseur
Les utilisateurs de l’application atteignent le point d’entrée L7 du fournisseur par le hostname enregistré. Chaque fournisseur implémente son propre point d’entrée.
Point d’entrée L7 du fournisseurdonnées: NodePortNodePort Service · Local
<app namespace>Le point d’entrée transmet à l’adresse privée et au NodePort du nœud. Le NodePort est celui alloué lors de l’enregistrement de la cible.
NodePort Service · Local
<app namespace>données: vers le PodDeployment · Pod · image par digest<app namespace>Le Service envoie la requête au Pod de l’application (Deployment) sur le même nœud. Avec externalTrafficPolicy Local, l’adresse source atteint inchangée le contrôle des CIDR enregistrés de la NetworkPolicy.
Cilium · Helm · VXLAN
kube-systemexécuter · appliquer: appliquer la politiqueNetworkPolicy · CIDR listés<app namespace>Cilium applique la NetworkPolicy : entrée seulement depuis les CIDR enregistrés vers le port de l’application ; aucune sortie par défaut, hormis une adresse de base de données approuvée. Pare-feu cloud et règles d’hôte restent nécessaires.
Utilisateur de Railshotdonnées: tableau de bord · APIdashboard · nginx
railshot-systemLes utilisateurs passent par le point d’entrée d’exploitation jusqu’au tableau de bord nginx, et nginx sert de façade à l’API produit. Le Service du tableau de bord n’est un NodePort que si un port de nœud approuvé est fourni ; l’API reste en ClusterIP.
Vérifier · observer
Le verdict de déploiement ne repose que sur l’état Argo et le contrôle de l’URL publique. Diagnostics de Pod, journaux actuels et métriques d’exploitation sont des signaux distincts qui ne le modifient jamais.
API produit · cd.js → bridge.py
railshot-systemobserver: révision · health · imagesArgo CD · ApplicationargocdDeployed exige la révision de sync de l’Application, Synced et Healthy, une opération Succeeded, et que les références d’image consignées dans le statut de l’Application Argo égalent les images par digest du Deployment revu. Ce n’est pas une porte directe sur l’imageID du Pod, et un statut Argo vert ne suffit pas à établir la correspondance.
API produit · cd.js → bridge.py
railshot-systemobserver: contrôle HTTPS publicPoint d’entrée L7 du fournisseurL’URL publique enregistrée est vérifiée sans redirection. Succeeded n’est consigné qu’avec à la fois le verdict Argo et ce contrôle.
API produit · cd.js → bridge.py
railshot-systemobserver: Diagnostic de Pod · hors verdictDeployment · Pod · image par digest<app namespace>Après le verdict Argo et le contrôle public, le Deployment, les ReplicaSets et les Pods sont lus séparément avec l’identifiant enregistré, et l’imageID et la disponibilité sont ajoutés comme diagnostic ready, progressing ou unavailable. Un diagnostic en échec ou divergent ne change jamais le verdict deployed ou succeeded.
API produit · cd.js → bridge.py
railshot-systemobserver: fin des journaux · 100 lignes par PodDeployment · Pod · image par digest<app namespace>Seulement pour l’opération actuellement déployée, avec la config de confiance et l’identifiant enregistré. Pour chaque Pod actuel (trois au plus), le digest de l’image en cours doit égaler le digest attendu ; puis jusqu’à 100 lignes caviardées par Pod sont lues dans une limite d’octets. Une tentative plus ancienne répond superseded ; sans configuration, not_configured. Ce n’est pas une agrégation de journaux.
VM d’observationobserver: collecte · NodePortsrailshot-observability
Prometheus sur la VM d’observation collecte node-exporter et kube-state-metrics via deux NodePorts restreints par pare-feu. Les valeurs manquantes ou périmées sont inconnues, pas nulles.
VM d’observationobserver: Blackbox probePoint d’entrée L7 du fournisseur
probe_success=1 signifie un 2xx à cet emplacement de sonde. Cela ne prouve ni l’accès de chaque utilisateur ni la version déployée, et ne fait jamais passer published à deployed.
Installation · enregistrement par l’opérateur
Installation du cluster, bootstrap d’enregistrement et entrées de cible, Secrets et configuration du renouvellement des identifiants sont des entrées de l’opérateur, jamais une partie de la charge utile de l’application du tenant.
Bootstrap de l’opérateurexécuter · appliquer: rendu de la plateforme · Secretrailshot-system
L’opérateur produit la déclaration de plateforme avec render-platform.py et fournit la config d’exploitation privée et les Secrets. Le MCP distant n’est inclus que si son image est fournie, la passerelle personnelle (WireGuard) seulement avec --personal.
Bootstrap de l’opérateurexécuter · appliquer: bootstrap d’enregistrementAppProject · cluster Secret
argocdL’opérateur installe dans argocd le Role de bootstrap d’enregistrement et un Role d’enregistrements vide, et prépare les entrées de cible. Les helpers d’enregistrement qu’exécute l’API produit (environment.py, applications.py) écrivent ensuite le Secret railshot-<target> avec un identifiant à portée limitée (clusterResources false, namespaces autorisés listés) et ajoutent les noms exacts validés au Role d’enregistrements. L’enregistrement par application lie un namespace app-<id> à un AppProject du même nom.
Bootstrap de l’opérateurexécuter · appliquer: installer K3s · CiliumCilium · Helm · VXLAN
kube-systeminstall-k3s.sh installe un serveur K3s à nœud unique (flannel, network policy, traefik et servicelb désactivés ; kube-proxy conservé), et Cilium est installé comme release Helm (kubeProxyReplacement false, VXLAN, Hubble désactivé).
CronJob de renouvellement · toutes les 2 h
argocdexécuter · appliquer: TokenRequest · 6 hSA railshot-argocd · token<app namespace>Le CronJob railshot-credentials demande toutes les deux heures un nouveau token de six heures pour le SA client. Le Role côté client n’autorise que la création de token pour son propre SA. Six heures est la TTL d’un token, pas le moment où l’accès de l’opérateur prend fin.
Toutes les connexions avec la même épaisseur. Choisissez un flux pour ne mettre en évidence que son chemin, avec des étapes numérotées.
- Trait plein · sens de circulation des requêtes, bundles, images et déclarations
- Tirets · demandes d’exécution, application, installation, identifiants
- Trait mixte long · lecture d’état, journaux, sondes, métriques ; distinct du verdict
- Trait noir épais · le flux sélectionné ; les numéros donnent l’ordre
- Cadre · un namespace, son nom dans l’en-tête (pas un nœud)
- Pastille intérieure · composant de ce namespace
- Cadre en tirets · hors des clusters
- railshot-system · produit
- argocd · autorité de déploiement
- railshot-build · autorité Docker de l’hôte
- GitHub · cluster client
La décision de placement du October 2 dans docs/architecture (deux nœuds de rôle) et le schéma du README public relèvent de l’historique ; cette carte suit les déclarations et le code de 645e28a. Les flèches indiquent cause et direction, pas la preuve que chaque chemin de fournisseur est actif aujourd’hui.
- Namespaces et nœuds
- Un namespace est une portée de nommage : ni du matériel ni, à lui seul, une frontière de sécurité. Les rôles platform et build sont un placement de scheduling par nodeSelector et taint ; cette carte ne fixe pas le nombre de nœuds du cluster d’exploitation. Le placement des nœuds d’Argo CD est lui aussi un contrôle distinct.
- Autorité du nœud build
- Les Jobs runner tournent avec hostNetwork, le socket Docker de l’hôte, NET_ADMIN et root. Un namespace, un taint ou le RBAC ne retirent pas l’autorité Docker de l’hôte ; ce n’est donc ni une isolation vis-à-vis de l’administrateur de la VM ni un bac à sable pour du code arbitraire.
- Namespace client
- Chaque cible enregistrée utilise un namespace et un AppProject non par défaut (par exemple tenant-demo, ou app-<id> par application). Seuls sont créés le Deployment d’un service (montage automatique du token ServiceAccount désactivé), un Service NodePort et une NetworkPolicy, plus un PVC ou un Job de migration optionnels ; les Secrets ne sont que référencés par nom (le Secret de pull et un DATABASE_URL approuvé), jamais créés.
- Entrée AWS
- Alias Route 53 → ALB partagé HTTPS 443 → adresse privée et NodePort du nœud client. Plusieurs applications réutilisent un même nœud, chacune avec son namespace, son Application et son NodePort.
- Entrée GCP
- LB HTTPS global EXTERNAL_MANAGED → NEG zonal GCE_VM_IP_PORT → NodePort privé de la VM existante. Les certificats viennent de Certificate Manager avec autorisation DNS. Le module ne détient ni la VM ni le VPC, et une VM unique reste un point de défaillance unique.
- Entrée OpenStack
- Listener TLS Amphora 443 avec politiques L7 Host et path → NodePort HTTP. L’adresse par défaut est une VIP privée ; Octavia, K3s, la base de données et le DNS sont hors du module. L’accessibilité publique et la préparation d’Amphora sur site exigent une recette distincte, et aucune application sur site n’a encore été vérifiée avec ce module.
- Entre fournisseurs
- Les trois entrées sont des implémentations distinctes, sans basculement ni réplication automatiques entre fournisseurs. Le cluster d’application client est un exemple de cible enregistrée, pas l’affirmation que le même déploiement tourne sur tous les clouds à la fois.
- Options de l’opérateur
- La passerelle personnelle (WireGuard) ne rejoint la plateforme que si l’opérateur active --personal ; ce n’est pas un chemin requis pour les clouds clients. Dans le mode direct pris en charge, la VM d’observation peut partager la VM de contrôle d’exploitation.