RAILSHOT.
Voltar à sala

Railshot

AI deployment & operations platform

Implantar um app web criado localmente em ambientes de nuvem e on-premises preparados e depois operá-lo.

Uma plataforma de IA de implantação e operação que leva um app web criado localmente a ambientes de nuvem ou on-premises preparados e continua a operá-lo.

2026.09.28 — 2026.10.04 · Concluído

Projeto em equipe · minha parte: planejamento e implementação da CI, clusters CSP e interface Ansible, operação, revisão de código da equipe e integração

SoftBank Hackathon 2026 powered by KOREC & ProgateParticipante da fase preliminar · seleção para a final a ser anunciada em 2026.10.26 (até 2026.10.07)

  • WebImplementado · hackathon
  • Servidor MCPImplementado · remoto · local

Os clusters de operações e de apps de clientes são separados, e o caminho público fica à parte do controle de implantação.

No K3s de operações, o painel, a API do produto e o build controller ficam em nós com papel platform e os Jobs runner em um nó com papel build, enquanto o Argo CD cuida da implantação. Os apps de clientes rodam em um namespace designado de um cluster K3s + Cilium de nó único, separado para cada destino registrado.

Mapa de clusters · desenhado a partir das declarações e do código no commit público 645e28a
Mapa de clusters de operações e de clientes do RailshotO cluster K3s de operações e um cluster de app de cliente registrado. No cluster de operações, o namespace railshot-system coloca o painel, a API do produto, o MCP remoto (só quando sua imagem é fornecida) e o build controller em nós com papel platform; os Jobs runner do namespace railshot-build pegam jobs do GitHub Actions no nó com papel build, que tem taint. Um job de release hospedado no GitHub publica o bundle testado no GHCR. A API do produto faz commit do workload no Git de config e sincroniza uma Application do namespace argocd, e o Argo CD aplica um Deployment, um Service NodePort e uma NetworkPolicy ao namespace registrado no cluster do cliente. As requisições ao app entram por um ponto de entrada L7 do provedor até o NodePort e o Service do nó, e depois o Pod do app. O veredito de implantação se baseia só no estado do Argo e na verificação da URL pública; diagnósticos de Pod, logs e métricas são lidos como sinais separados. As entradas de instalação e registro do operador ficam separadas do payload do app do tenant.Usuário do Railshotpainel · MCPBootstrap do operadorinstalar · registrar · SecretGHCRimagens por digestrepositório Git de configworkload.json · revisionPonto de entrada L7 do provedorALB · GCP LB · AmphoraUsuário do appHTTPS públicoVM de observaçãofora do K3s do cliente · Composerailshot-systemdashboard · nginxMCP remoto · só com uma imagembuild controller · CronJobAPI do produto · cd.js → bridge.pyargocdArgo CD · ApplicationAppProject · cluster SecretCronJob de renovação · a cada 2 hrailshot-buildrunner Job · hostNetwork · Docker socketrailshot-deploy.ymlloop job · self-hosted runnerrelease job · GitHub-hosted<app namespace>Deployment · Pod · imagem por digestNodePort Service · LocalNetworkPolicy · CIDRs listadosSA railshot-argocd · tokenkube-systemCilium · Helm · VXLANrailshot-observabilitynode-exporterkube-state-metricsCluster de operações · K3sAWS · nós por papelnós com papel platformnodeSelectornó com papel buildNoSchedule taintGitHubActions · GHCRCluster de app do cliente · exemploum por destino registradoUm nó K3s · serverCilium · kube-proxy mantido1workflow_dispatch2repor Jobs runner3pegar o job4bundle testado5 publicar por digest · release job · GitHub-hosted → GHCR51workload commit · push2revision3 Application sync · API do produto · cd.js → bridge.py → Argo CD · Application34aplicar ao namespace5pull por digest1HTTPS 4432NodePort3 para o Pod · NodePort Service · Local → Deployment · Pod · imagem por digest34 aplicar a política · Cilium · Helm · VXLAN → NetworkPolicy · CIDRs listados45 painel · API · Usuário do Railshot → dashboard · nginx51 revisão · health · imagens · API do produto · cd.js → bridge.py → Argo CD · Application12verificação HTTPS pública3Diagnóstico do Pod · fora do veredito4tail de logs · 100 linhas por Pod5coleta · NodePorts6Blackbox probe1renderizar a plataforma · Secret2bootstrap de registro3instalar K3s · Cilium4TokenRequest · 6 h

Build → publicação da imagem

A API do produto pede uma execução do GitHub Actions, e o runner do nó build pega e verifica o job. Só um bundle que passou nas mesmas verificações é publicado no GHCR por um job de release hospedado no GitHub.

  1. API do produto · cd.js → bridge.pyrailshot-systemexecutar · aplicar: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.yml

    A API do produto pede uma execução do GitHub Actions com tenant, app, commit de origem e destino. A API nunca chama um Job runner diretamente.

  2. build controller · CronJobrailshot-systemexecutar · aplicar: repor Jobs runnerrunner Job · hostNetwork · Docker socketrailshot-build

    O CronJob do build controller (a cada minuto, em um nó platform) cria Jobs runner no namespace railshot-build. Seu Role só permite get, list e create em Jobs, mais get, update e patch no único Secret de registro do runner; ele não pode criar namespaces nem outros Secrets.

  3. runner Job · hostNetwork · Docker socketrailshot-buildexecutar · aplicar: pegar o jobloop job · self-hosted runnerrailshot-deploy.yml

    O runner do nó com papel build (hostname exato, toleration NoSchedule) pega o job loop pelos rótulos self-hosted e o executa. O código do cliente roda nos contêineres restritos existentes de build Docker e verificação.

  4. runner Job · hostNetwork · Docker socketrailshot-builddados: bundle testadorelease job · GitHub-hostedrailshot-deploy.yml

    O job loop deixa o bundle que passou nas mesmas verificações, e o job de release só pega esse bundle.

  5. release job · GitHub-hostedrailshot-deploy.ymldados: publicar por digestGHCR

    Um job de release hospedado no GitHub (ubuntu-24.04) publica no GHCR a imagem do bundle testado sem reconstruí-la. A publicação de imagens nunca acontece dentro do cluster de app do cliente.

Git de config → Argo → namespace do cliente

O Git de config carrega os recursos desejados e o GHCR os bytes da imagem. O Argo CD só aplica ao namespace do cliente registrado.

  1. API do produto · cd.js → bridge.pyrailshot-systemdados: workload commit · pushrepositório Git de config

    cd.js executa bridge.py, que só faz commit e push de workload.json quando o registro de publicação e o destino registrado coincidem, e depois relê a revisão remota. O Git carrega os recursos desejados; o GHCR carrega os bytes da imagem.

  2. repositório Git de configdados: revisionArgo CD · Applicationargocd

    A Application só lê seu repositório de config, caminho e revisão restritos.

  3. API do produto · cd.js → bridge.pyrailshot-systemexecutar · aplicar: Application syncArgo CD · Applicationargocd

    A Application nomeada é definida por server-side apply, e uma sync de uma revisão exata é pedida sem prune. O ServiceAccount railshot-product da API do produto tem ao mesmo tempo o bootstrap de registro instalado pelo operador (create em Secrets, AppProjects e Applications no argocd; get, update e escalate em Roles nomeados) e permissões get e patch acrescentadas por nome exato para cada destino validado. O create do Kubernetes não pode ser limitado por nome, então a validação de entradas do produto também mantém os nomes dentro do escopo.

  4. Argo CD · Applicationargocdexecutar · aplicar: aplicar ao namespace<app namespace>

    O Argo CD aplica ao namespace designado no cluster do cliente com a credencial do Secret de cluster registrado. O AppProject só permite kinds de escopo de namespace e nenhum recurso de escopo de cluster.

  5. Deployment · Pod · imagem por digest<app namespace>dados: pull por digestGHCR

    A imagem publicada por digest é baixada com um Secret de pull referenciado pelo nome no mesmo namespace. Os valores dos Secrets nunca entram no artefato nem no Git de config.

Caminho das requisições públicas

As requisições ao app seguem ponto de entrada L7 do provedor → NodePort do nó → Service → Pod do app. A aplicação de políticas pelo Cilium é um controle separado, não um salto que carrega a requisição. O acesso ao painel de operações segue outro caminho.

  1. Usuário do appdados: HTTPS 443Ponto de entrada L7 do provedor

    Os usuários do app chegam ao ponto de entrada L7 do provedor pelo hostname registrado. Cada provedor implementa seu próprio ponto de entrada.

  2. Ponto de entrada L7 do provedordados: NodePortNodePort Service · Local<app namespace>

    O ponto de entrada encaminha ao endereço privado e ao NodePort do nó. O NodePort é o alocado no registro do destino.

  3. NodePort Service · Local<app namespace>dados: para o PodDeployment · Pod · imagem por digest<app namespace>

    O Service envia a requisição ao Pod do app (Deployment) no mesmo nó. Com externalTrafficPolicy Local, o endereço de origem chega inalterado à verificação de CIDRs registrados da NetworkPolicy.

  4. Cilium · Helm · VXLANkube-systemexecutar · aplicar: aplicar a políticaNetworkPolicy · CIDRs listados<app namespace>

    O Cilium aplica a NetworkPolicy: entrada só a partir dos CIDRs registrados para a porta do app; nenhuma saída por padrão, exceto um endereço de banco de dados aprovado. Firewalls de nuvem e regras de host continuam necessários.

  5. Usuário do Railshotdados: painel · APIdashboard · nginxrailshot-system

    Os usuários entram pelo ponto de entrada de operações até o painel nginx, e o nginx fica na frente da API do produto. O Service do painel só é NodePort quando um node port aprovado é informado; a API continua ClusterIP.

Verificar · observar

O veredito de implantação se baseia só no estado do Argo e na verificação da URL pública. Diagnósticos de Pod, logs atuais e métricas de operação são sinais separados que nunca o mudam.

  1. API do produto · cd.js → bridge.pyrailshot-systemobservar: revisão · health · imagensArgo CD · Applicationargocd

    Deployed exige a revisão de sync da Application, Synced e Healthy, a operação Succeeded e que as referências de imagem registradas no status da Application do Argo sejam iguais às imagens por digest do Deployment revisado. Não é um portão direto sobre o imageID do Pod, e um status verde do Argo sozinho não estabelece a correspondência.

  2. API do produto · cd.js → bridge.pyrailshot-systemobservar: verificação HTTPS públicaPonto de entrada L7 do provedor

    A URL pública registrada é verificada sem redirecionamentos. Succeeded só é registrado com o veredito do Argo e esta verificação.

  3. API do produto · cd.js → bridge.pyrailshot-systemobservar: Diagnóstico do Pod · fora do vereditoDeployment · Pod · imagem por digest<app namespace>

    Após o veredito do Argo e a verificação pública, o Deployment, os ReplicaSets e os Pods são lidos separadamente com a credencial registrada, e o imageID e a prontidão são acrescentados como diagnóstico ready, progressing ou unavailable. Um diagnóstico com falha ou divergente nunca muda o veredito deployed ou succeeded.

  4. API do produto · cd.js → bridge.pyrailshot-systemobservar: tail de logs · 100 linhas por PodDeployment · Pod · imagem por digest<app namespace>

    Só para a operação implantada no momento, com a config confiável e a credencial registrada. Para cada Pod atual (no máximo três), o digest da imagem em execução deve ser igual ao digest esperado; depois são lidas até 100 linhas com dados ocultados por Pod, dentro de um limite de bytes. Uma tentativa anterior responde superseded; sem configuração, not_configured. Não é agregação de logs.

  5. VM de observaçãoobservar: coleta · NodePortsrailshot-observability

    O Prometheus na VM de observação coleta node-exporter e kube-state-metrics por dois NodePorts restritos por firewall. Valores ausentes ou desatualizados são desconhecidos, não zero.

  6. VM de observaçãoobservar: Blackbox probePonto de entrada L7 do provedor

    probe_success=1 significa um 2xx naquele ponto de probe. Não prova o acesso de todos os usuários nem a versão implantada, e nunca promove published a deployed.

Instalação · registro pelo operador

A instalação do cluster, o bootstrap de registro e as entradas do destino, os Secrets e a configuração de renovação de credenciais são entradas do operador, nunca parte do payload do app do tenant.

  1. Bootstrap do operadorexecutar · aplicar: renderizar a plataforma · Secretrailshot-system

    O operador gera a declaração da plataforma com render-platform.py e fornece a config de operações privada e os Secrets. O MCP remoto só é incluído quando sua imagem é fornecida, e o gateway pessoal (WireGuard) só com --personal.

  2. Bootstrap do operadorexecutar · aplicar: bootstrap de registroAppProject · cluster Secretargocd

    O operador instala no argocd o Role de bootstrap de registro e um Role de registros vazio, e prepara as entradas do destino. Os helpers de registro que a API do produto executa (environment.py, applications.py) gravam então o Secret railshot-<target> com uma credencial de escopo limitado (clusterResources false, namespaces permitidos listados) e acrescentam os nomes exatos validados ao Role de registros. O registro por app vincula um namespace app-<id> a um AppProject de mesmo nome.

  3. Bootstrap do operadorexecutar · aplicar: instalar K3s · CiliumCilium · Helm · VXLANkube-system

    install-k3s.sh instala um servidor K3s de nó único (flannel, network policy, traefik e servicelb desligados; kube-proxy mantido), e o Cilium é instalado como release do Helm (kubeProxyReplacement false, VXLAN, Hubble desligado).

  4. CronJob de renovação · a cada 2 hargocdexecutar · aplicar: TokenRequest · 6 hSA railshot-argocd · token<app namespace>

    O CronJob railshot-credentials pede a cada duas horas um token novo de seis horas para a SA do cliente. O Role do lado do cliente só permite criar token para a própria SA. Seis horas é o TTL de um token, não o momento em que o acesso do operador termina.

Todas as conexões com a mesma espessura. Escolha um fluxo para destacar só o caminho dele, com etapas numeradas.

  • Contínua · direção em que fluem requisições, bundles, imagens e declarações
  • Tracejada · pedidos de execução, aplicação, instalação, credenciais
  • Traço longo e ponto · leitura de estado, logs, probes, métricas; separada do veredito
  • Linha preta grossa · o fluxo selecionado; os números dão a ordem
  • Caixa · um namespace, com o nome no cabeçalho (não um nó)
  • Chip interno · componente desse namespace
  • Caixa tracejada · fora dos clusters
  • railshot-system · produto
  • argocd · autoridade de implantação
  • railshot-build · autoridade de Docker do host
  • GitHub · cluster do cliente

A decisão de alocação de October 2 em docs/architecture (dois nós de papel) e o diagrama do README público são histórico; este mapa segue as declarações e o código de 645e28a. As setas mostram causa e direção, não a prova de que cada caminho de provedor está ativo agora.

Namespaces e nós
Um namespace é um escopo de nomes: nem hardware nem, por si só, uma fronteira de segurança. Os papéis platform e build são uma alocação de scheduling por nodeSelector e taint; este mapa não fixa o número de nós do cluster de operações. A alocação de nós do Argo CD também é uma verificação separada.
Autoridade do nó build
Os Jobs runner rodam com hostNetwork, o socket Docker do host, NET_ADMIN e root. Namespace, taint ou RBAC não removem a autoridade de Docker do host, então isso não é isolamento em relação ao administrador da VM nem sandbox para código arbitrário.
Namespace do cliente
Cada destino registrado usa um namespace e um AppProject não padrão (por exemplo tenant-demo, ou app-<id> por app). Só são criados o Deployment de um serviço (montagem automática do token de ServiceAccount desligada), um Service NodePort e uma NetworkPolicy, mais um PVC ou Job de migração opcionais; os Secrets só são referenciados pelo nome (o Secret de pull e um DATABASE_URL aprovado), nunca criados.
Entrada da AWS
Alias do Route 53 → ALB compartilhado HTTPS 443 → endereço privado e NodePort do nó do cliente. Vários apps reutilizam um mesmo nó, cada um com seu namespace, Application e NodePort.
Entrada do GCP
LB HTTPS global EXTERNAL_MANAGED → NEG zonal GCE_VM_IP_PORT → NodePort privado da VM existente. Os certificados vêm do Certificate Manager com autorização DNS. O módulo não é dono da VM nem da VPC, e uma única VM continua sendo ponto único de falha.
Entrada do OpenStack
Listener TLS do Amphora 443 com políticas L7 de Host e path → NodePort HTTP. O endereço padrão é uma VIP privada; Octavia, K3s, o banco de dados e o DNS ficam fora do módulo. A acessibilidade pública e a prontidão do Amphora no local exigem aceitação separada, e nenhuma aplicação no local foi verificada ainda com este módulo.
Entre provedores
As três entradas são implementações distintas, sem failover nem replicação automáticos entre provedores. O cluster de app do cliente é um exemplo de destino registrado, não a afirmação de que a mesma implantação roda em todas as nuvens ao mesmo tempo.
Opções do operador
O gateway pessoal (WireGuard) só entra na plataforma quando o operador ativa --personal; não é um caminho obrigatório para as nuvens de clientes. No modo direct suportado, a VM de observação pode compartilhar a VM de controle de operações.