Railshot
AI deployment & operations platform
Desplegar en entornos en la nube y on-premises preparados una aplicación web creada en local, y luego operarla.
Una plataforma de IA de despliegue y operación que lleva una aplicación web creada en local a entornos en la nube u on-premises preparados y la mantiene en funcionamiento.
Los clústeres de operaciones y de aplicaciones de clientes están separados, y la ruta pública se mantiene aparte del control de despliegue.
En el K3s de operaciones, el panel, la API del producto y el build controller se ubican en nodos con rol platform y los Jobs runner en un nodo con rol build, mientras Argo CD se encarga del despliegue. Las aplicaciones de clientes se ejecutan en un namespace designado de un clúster K3s + Cilium de un solo nodo, aparte para cada destino registrado.
Build → publicación de la imagen
La API del producto solicita una ejecución de GitHub Actions, y el runner del nodo build toma y comprueba el job. Solo un bundle que superó las mismas comprobaciones lo publica en GHCR un job de release alojado en GitHub.
API del producto · cd.js → bridge.py
railshot-systemejecutar · aplicar: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlLa API del producto solicita una ejecución de GitHub Actions con tenant, app, commit de origen y destino. La API nunca llama directamente a un Job runner.
build controller · CronJob
railshot-systemejecutar · aplicar: reponer Jobs runnerrunner Job · hostNetwork · Docker socketrailshot-buildEl CronJob del build controller (cada minuto, en un nodo platform) crea Jobs runner en el namespace railshot-build. Su Role solo permite get, list y create sobre Jobs, más get, update y patch sobre el único Secret de registro del runner; no puede crear namespaces ni otros Secrets.
runner Job · hostNetwork · Docker socket
railshot-buildejecutar · aplicar: tomar el jobloop job · self-hosted runnerrailshot-deploy.ymlEl runner del nodo con rol build (hostname exacto, tolerancia NoSchedule) toma el job loop por sus etiquetas self-hosted y lo ejecuta. El código fuente del cliente se ejecuta en los contenedores restringidos existentes de build Docker y comprobación.
runner Job · hostNetwork · Docker socket
railshot-builddatos: bundle probadorelease job · GitHub-hostedrailshot-deploy.ymlEl job loop deja el bundle que superó las mismas comprobaciones, y el job de release solo toma ese bundle.
release job · GitHub-hosted
railshot-deploy.ymldatos: publicar por digestGHCRUn job de release alojado en GitHub (ubuntu-24.04) publica en GHCR la imagen del bundle probado sin reconstruirla. La publicación de imágenes nunca ocurre dentro del clúster de aplicación del cliente.
Git de config → Argo → namespace del cliente
El Git de config lleva los recursos deseados y GHCR los bytes de la imagen. Argo CD solo aplica al namespace del cliente registrado.
API del producto · cd.js → bridge.py
railshot-systemdatos: workload commit · pushrepositorio Git de configcd.js ejecuta bridge.py, que solo hace commit y push de workload.json cuando el registro de publicación y el destino registrado coinciden, y luego vuelve a leer la revisión remota. Git lleva los recursos deseados; GHCR lleva los bytes de la imagen.
repositorio Git de configdatos: revisionArgo CD · Application
argocdLa Application solo lee su repositorio de config, su ruta y su revisión restringidos.
API del producto · cd.js → bridge.py
railshot-systemejecutar · aplicar: Application syncArgo CD · ApplicationargocdLa Application con nombre se fija por server-side apply, y se solicita una sync de una revisión exacta sin prune. El ServiceAccount railshot-product de la API del producto tiene a la vez el bootstrap de registro instalado por el operador (create sobre Secrets, AppProjects y Applications en argocd; get, update y escalate sobre Roles con nombre) y permisos get y patch añadidos por nombre exacto para cada destino validado. El create de Kubernetes no se puede limitar por nombre, así que la validación de entradas del producto también mantiene los nombres dentro del alcance.
Argo CD · Application
argocdejecutar · aplicar: aplicar al namespace<app namespace>Argo CD aplica al namespace designado del clúster del cliente con la credencial del Secret de clúster registrado. El AppProject solo permite kinds de ámbito namespace y ningún recurso de ámbito clúster.
Deployment · Pod · imagen por digest
<app namespace>datos: pull por digestGHCRLa imagen publicada por digest se descarga con un Secret de pull referenciado por nombre en el mismo namespace. Los valores de los Secrets nunca entran en el artefacto ni en el Git de config.
Ruta de las solicitudes públicas
Las solicitudes a la aplicación siguen punto de entrada L7 del proveedor → NodePort del nodo → Service → Pod de la aplicación. La aplicación de políticas de Cilium es un control aparte, no un salto que transporte la solicitud. El acceso al panel de operaciones sigue otra ruta.
Usuario de la aplicacióndatos: HTTPS 443Punto de entrada L7 del proveedor
Los usuarios de la aplicación llegan al punto de entrada L7 del proveedor mediante el hostname registrado. Cada proveedor implementa su propio punto de entrada.
Punto de entrada L7 del proveedordatos: NodePortNodePort Service · Local
<app namespace>El punto de entrada reenvía a la dirección privada y al NodePort del nodo. El NodePort es el asignado al registrar el destino.
NodePort Service · Local
<app namespace>datos: al PodDeployment · Pod · imagen por digest<app namespace>El Service envía la solicitud al Pod de la aplicación (Deployment) en el mismo nodo. Con externalTrafficPolicy Local, la dirección de origen llega sin cambios a la comprobación de CIDR registrados de la NetworkPolicy.
Cilium · Helm · VXLAN
kube-systemejecutar · aplicar: aplicar la políticaNetworkPolicy · CIDR enumerados<app namespace>Cilium aplica la NetworkPolicy: entrada solo desde los CIDR registrados al puerto de la aplicación; sin salida por defecto, salvo una dirección de base de datos aprobada. Los firewalls de la nube y las reglas del host siguen siendo necesarios.
Usuario de Railshotdatos: panel · APIdashboard · nginx
railshot-systemLos usuarios entran por el punto de entrada de operaciones al panel de nginx, y nginx hace de fachada de la API del producto. El Service del panel solo es NodePort cuando se da un puerto de nodo aprobado; la API sigue como ClusterIP.
Verificar · observar
El veredicto de despliegue se basa solo en el estado de Argo y la comprobación de la URL pública. Los diagnósticos de Pods, los logs actuales y las métricas de operación son señales aparte que nunca lo cambian.
API del producto · cd.js → bridge.py
railshot-systemobservar: revisión · health · imágenesArgo CD · ApplicationargocdDeployed requiere la revisión de sync de la Application, Synced y Healthy, la operación Succeeded y que las referencias de imagen registradas en el estado de la Application de Argo sean iguales a las imágenes por digest del Deployment revisado. No es una puerta directa sobre el imageID del Pod, y un estado verde de Argo por sí solo no establece la coincidencia.
API del producto · cd.js → bridge.py
railshot-systemobservar: comprobación HTTPS públicaPunto de entrada L7 del proveedorLa URL pública registrada se comprueba sin redirecciones. Succeeded solo se registra con el veredicto de Argo y esta comprobación.
API del producto · cd.js → bridge.py
railshot-systemobservar: Diagnóstico del Pod · fuera del veredictoDeployment · Pod · imagen por digest<app namespace>Tras el veredicto de Argo y la comprobación pública, el Deployment, los ReplicaSets y los Pods se leen por separado con la credencial registrada, y el imageID y la disponibilidad se añaden como diagnóstico ready, progressing o unavailable. Un diagnóstico fallido o distinto nunca cambia el veredicto deployed o succeeded.
API del producto · cd.js → bridge.py
railshot-systemobservar: tail de logs · 100 líneas por PodDeployment · Pod · imagen por digest<app namespace>Solo para la operación desplegada actualmente, con la config de confianza y la credencial registrada. Para cada Pod actual (tres como máximo), el digest de la imagen en ejecución debe coincidir con el esperado; después se leen hasta 100 líneas con datos ocultos por Pod dentro de un límite de bytes. Un intento anterior responde superseded; sin configuración, not_configured. No es una agregación de logs.
VM de observaciónobservar: recolección · NodePortsrailshot-observability
Prometheus en la VM de observación recolecta node-exporter y kube-state-metrics a través de dos NodePorts restringidos por firewall. Los valores ausentes u obsoletos son desconocidos, no cero.
VM de observaciónobservar: Blackbox probePunto de entrada L7 del proveedor
probe_success=1 significa un 2xx en esa ubicación de la sonda. No prueba el acceso de todos los usuarios ni la versión desplegada, y nunca convierte published en deployed.
Instalación · registro del operador
La instalación del clúster, el bootstrap de registro y las entradas del destino, los Secrets y la configuración de renovación de credenciales son entradas del operador, nunca parte de la carga útil de la aplicación del tenant.
Bootstrap del operadorejecutar · aplicar: renderizar la plataforma · Secretrailshot-system
El operador genera la declaración de la plataforma con render-platform.py y aporta la config de operaciones privada y los Secrets. El MCP remoto solo se incluye si se proporciona su imagen, y la pasarela personal (WireGuard) solo con --personal.
Bootstrap del operadorejecutar · aplicar: bootstrap de registroAppProject · cluster Secret
argocdEl operador instala en argocd el Role de bootstrap de registro y un Role de registros vacío, y prepara las entradas del destino. Los helpers de registro que ejecuta la API del producto (environment.py, applications.py) escriben luego el Secret railshot-<target> con una credencial de alcance limitado (clusterResources false, namespaces permitidos enumerados) y añaden los nombres exactos validados al Role de registros. El registro por aplicación vincula un namespace app-<id> a un AppProject del mismo nombre.
Bootstrap del operadorejecutar · aplicar: instalar K3s · CiliumCilium · Helm · VXLAN
kube-systeminstall-k3s.sh instala un servidor K3s de un solo nodo (flannel, network policy, traefik y servicelb desactivados; kube-proxy conservado), y Cilium se instala como release de Helm (kubeProxyReplacement false, VXLAN, Hubble desactivado).
CronJob de renovación · cada 2 h
argocdejecutar · aplicar: TokenRequest · 6 hSA railshot-argocd · token<app namespace>El CronJob railshot-credentials solicita cada dos horas un token nuevo de seis horas para la SA del cliente. El Role del lado del cliente solo permite crear tokens para su propia SA. Seis horas es la TTL de un token, no el momento en que termina el acceso del operador.
Todas las conexiones con el mismo grosor. Elija un flujo para resaltar solo su recorrido, con pasos numerados.
- Continua · dirección en que fluyen solicitudes, bundles, imágenes y declaraciones
- Discontinua · solicitudes de ejecución, aplicación, instalación, credenciales
- Raya larga y punto · lectura de estado, logs, sondas, métricas; aparte del veredicto
- Línea negra gruesa · el flujo seleccionado; los números dan el orden
- Recuadro · un namespace, con su nombre en la cabecera (no un nodo)
- Chip interior · componente de ese namespace
- Recuadro discontinuo · fuera de los clústeres
- railshot-system · producto
- argocd · autoridad de despliegue
- railshot-build · autoridad de Docker del host
- GitHub · clúster del cliente
La decisión de ubicación del October 2 en docs/architecture (dos nodos de rol) y el diagrama del README público son historia; este mapa sigue las declaraciones y el código de 645e28a. Las flechas muestran causa y dirección, no la prueba de que cada ruta de proveedor esté activa ahora.
- Namespaces y nodos
- Un namespace es un ámbito de nombres: ni hardware ni, por sí solo, una frontera de seguridad. Los roles platform y build son una ubicación de scheduling por nodeSelector y taint; este mapa no fija el número de nodos del clúster de operaciones. La ubicación de nodos de Argo CD también es una comprobación aparte.
- Autoridad del nodo build
- Los Jobs runner se ejecutan con hostNetwork, el socket de Docker del host, NET_ADMIN y root. Un namespace, un taint o RBAC no eliminan la autoridad de Docker del host, así que no es un aislamiento respecto del administrador de la VM ni un sandbox para código arbitrario.
- Namespace del cliente
- Cada destino registrado usa un namespace y un AppProject no predeterminados (por ejemplo tenant-demo, o app-<id> por aplicación). Solo se crean el Deployment de un servicio (montaje automático del token de ServiceAccount desactivado), un Service NodePort y una NetworkPolicy, más un PVC o un Job de migración opcionales; los Secrets solo se referencian por nombre (el Secret de pull y un DATABASE_URL aprobado), nunca se crean.
- Entrada de AWS
- Alias de Route 53 → ALB compartido HTTPS 443 → dirección privada y NodePort del nodo del cliente. Varias aplicaciones reutilizan un mismo nodo, cada una con su namespace, Application y NodePort.
- Entrada de GCP
- LB HTTPS global EXTERNAL_MANAGED → NEG zonal GCE_VM_IP_PORT → NodePort privado de la VM existente. Los certificados provienen de Certificate Manager con autorización DNS. El módulo no es dueño de la VM ni de la VPC, y una sola VM sigue siendo un punto único de fallo.
- Entrada de OpenStack
- Listener TLS de Amphora 443 con políticas L7 de Host y path → NodePort HTTP. La dirección por defecto es una VIP privada; Octavia, K3s, la base de datos y el DNS están fuera del módulo. La accesibilidad pública y la preparación de Amphora en sitio requieren una aceptación aparte, y todavía no se ha verificado ninguna aplicación en sitio con este módulo.
- Entre proveedores
- Las tres entradas son implementaciones distintas, sin failover ni replicación automáticos entre proveedores. El clúster de aplicación del cliente es un ejemplo de destino registrado, no la afirmación de que el mismo despliegue corra en todas las nubes a la vez.
- Opciones del operador
- La pasarela personal (WireGuard) solo se une a la plataforma cuando el operador activa --personal; no es una ruta obligatoria para las nubes de clientes. En el modo direct compatible, la VM de observación puede compartir la VM de control de operaciones.