RAILSHOT.
展示室へ戻る

Railshot

AI deployment & operations platform

ローカルで作ったWebアプリを、用意されたクラウドとオンプレミス環境にデプロイし、運用します。

ローカルで作ったWebアプリを、用意されたクラウドやオンプレミス環境にデプロイし、運用まで続けるAIデプロイ・運用プラットフォームです。

2026.09.28 — 2026.10.04 · 終了

チームプロジェクト · 担当:CIの企画・実装、CSPクラスターとAnsibleインターフェース、運用、チームのコードレビューと統合

SoftBank Hackathon 2026 powered by KOREC & Progate予選参加 · 本選進出の発表は2026年10月26日予定(2026年10月7日時点)

  • Web実装済み · ハッカソン
  • MCPサーバー実装済み · リモート · ローカル

運用クラスターと顧客アプリのクラスターを分け、公開経路をデプロイ制御から切り離しています。

運用K3sでは、ダッシュボード、製品API、build controllerをplatformロールのノードに、runner Jobをbuildロールのノードに配置し、Argo CDがデプロイを担います。顧客アプリは、登録済みの対象ごとに分けた単一ノードK3s + Ciliumクラスターの指定namespaceで動きます。

クラスター構成図 · 公開コミット645e28aの宣言とコードから作図
Railshotの運用・顧客クラスター構成図運用K3sクラスターと、登録済みの顧客アプリクラスターです。運用クラスターでは、railshot-system namespaceがダッシュボード、製品API、リモートMCP(イメージが与えられた場合のみ)、build controllerをplatformロールのノードに配置し、railshot-build namespaceのrunner Jobが、taintを設定したbuildロールのノードでGitHub Actionsのjobを受け取ります。GitHub-hostedのrelease jobが、検査済みのbundleをGHCRに公開します。製品APIはworkloadをconfig Gitにコミットしてargocd namespaceのApplicationをsyncし、Argo CDが顧客クラスターに登録されたnamespaceへDeployment、NodePort Service、NetworkPolicyを適用します。アプリへのリクエストは、プロバイダーのL7入口からノードのNodePortとServiceを経て、アプリのPodに入ります。デプロイの判定はArgoの状態と公開URLの確認だけに基づき、Podの診断、ログ、メトリクスは別のシグナルとして読み取ります。運用者のインストールと登録の入力は、テナントアプリのpayloadから切り離されています。Railshotユーザーダッシュボード · MCP運用者のbootstrapインストール · 登録 · SecretGHCRダイジェスト指定のイメージconfig Gitリポジトリworkload.json · revisionプロバイダーのL7入口ALB · GCP LB · Amphoraアプリのユーザー公開HTTPS観測VM顧客K3sの外 · Composerailshot-systemdashboard · nginxリモートMCP · イメージがある場合のみbuild controller · CronJob製品API · cd.js → bridge.pyargocdArgo CD · ApplicationAppProject · cluster Secret更新CronJob · 2時間ごとrailshot-buildrunner Job · hostNetwork · Docker socketrailshot-deploy.ymlloop job · self-hosted runnerrelease job · GitHub-hosted<app namespace>Deployment · Pod · ダイジェスト指定のイメージNodePort Service · LocalNetworkPolicy · 登録済みCIDRのみrailshot-argocd SA · トークンkube-systemCilium · Helm · VXLANrailshot-observabilitynode-exporterkube-state-metrics運用クラスター · K3sAWS · ロール別ノードplatformロールのノードnodeSelectorbuildロールのノードNoSchedule taintGitHubActions · GHCR顧客アプリクラスター · 例登録済みの対象ごとに1つK3sノード1台 · serverCilium · kube-proxyは維持1workflow_dispatch2runner Jobを補充3jobを受け取る4検査済みのbundle5 ダイジェストで公開 · release job · GitHub-hosted → GHCR51workload commit · push2revision3 Application sync · 製品API · cd.js → bridge.py → Argo CD · Application34namespaceへ適用5ダイジェストでpull1HTTPS 4432NodePort3 Podへ · NodePort Service · Local → Deployment · Pod · ダイジェスト指定のイメージ34 ポリシーの強制 · Cilium · Helm · VXLAN → NetworkPolicy · 登録済みCIDRのみ45 ダッシュボード · API · Railshotユーザー → dashboard · nginx51 revision · health · イメージ · 製品API · cd.js → bridge.py → Argo CD · Application12公開HTTPS確認3Pod診断 · 判定外4ログtail · Podごとに100行5scrape · NodePort6Blackbox probe1platformのレンダリング · Secret2登録bootstrap3K3s · Ciliumのインストール4TokenRequest · 6時間

ビルド → イメージ公開

製品APIがGitHub Actionsの実行を要求し、buildノードのrunnerがそのjobを受け取って検査します。同じ検査を通過したbundleだけを、GitHub-hostedのrelease jobがGHCRに公開します。

  1. 製品API · cd.js → bridge.pyrailshot-system実行 · 適用: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.yml

    製品APIが、tenant、app、ソースコミット、対象でGitHub Actionsの実行を要求します。APIがrunner Jobを直接呼び出すことはありません。

  2. build controller · CronJobrailshot-system実行 · 適用: runner Jobを補充runner Job · hostNetwork · Docker socketrailshot-build

    build controllerのCronJob(毎分、platformノード上)がrailshot-build namespaceにrunner Jobを作成します。そのRoleが許可するのは、Jobのget、list、createと、runner登録用Secret 1つに対するget、update、patchだけで、namespaceや他のSecretは作成できません。

  3. runner Job · hostNetwork · Docker socketrailshot-build実行 · 適用: jobを受け取るloop job · self-hosted runnerrailshot-deploy.yml

    buildロールのノード(正確なhostname、NoSchedule toleration)上のrunnerが、self-hostedラベルでloop jobを受け取って実行します。顧客のソースは、既存の制限されたDockerのビルド・検査コンテナで動きます。

  4. runner Job · hostNetwork · Docker socketrailshot-buildデータ: 検査済みのbundlerelease job · GitHub-hostedrailshot-deploy.yml

    loop jobは同じ検査を通過したbundleを残し、release jobはそのbundleだけを受け取ります。

  5. release job · GitHub-hostedrailshot-deploy.ymlデータ: ダイジェストで公開GHCR

    GitHub-hostedのrelease job(ubuntu-24.04)が、検査済みbundleのイメージを再ビルドせずにGHCRに公開します。イメージの公開が顧客アプリクラスター内で行われることはありません。

config Git → Argo → 顧客namespace

config Gitは望ましいリソースを、GHCRはイメージのバイトを運びます。Argo CDは登録済みの顧客namespaceにのみ適用します。

  1. 製品API · cd.js → bridge.pyrailshot-systemデータ: workload commit · pushconfig Gitリポジトリ

    cd.jsがbridge.pyを実行し、bridge.pyは公開記録と登録済みの対象が一致する場合にのみworkload.jsonをコミットしてpushし、リモートのrevisionを読み戻します。Gitは望ましいリソースを、GHCRはイメージのバイトを運びます。

  2. config Gitリポジトリデータ: revisionArgo CD · Applicationargocd

    Applicationは、制限されたconfigリポジトリ、パス、revisionだけを読み取ります。

  3. 製品API · cd.js → bridge.pyrailshot-system実行 · 適用: Application syncArgo CD · Applicationargocd

    指定のApplicationをserver-side applyで設定し、正確なrevision 1つのsyncをpruneなしで要求します。製品APIのrailshot-product ServiceAccountは、運用者がインストールした登録bootstrap(argocdでのSecret、AppProject、Applicationのcreate、名前を指定したRoleに対するget、update、escalate)と、検証済みの対象ごとに正確な名前で追加されるgetとpatchの権限の両方を持ちます。Kubernetesのcreateは名前で制限できないため、製品の入力検証でも名前をスコープ内に保ちます。

  4. Argo CD · Applicationargocd実行 · 適用: namespaceへ適用<app namespace>

    Argo CDが、登録済みのcluster Secretの認証情報で、顧客クラスターの指定namespaceに適用します。AppProjectはnamespace単位のkindだけを許可し、クラスター単位のリソースは許可しません。

  5. Deployment · Pod · ダイジェスト指定のイメージ<app namespace>データ: ダイジェストでpullGHCR

    同じnamespaceのpull Secretを名前で参照し、公開されたダイジェスト指定のイメージをpullします。Secretの値が成果物やconfig Gitに入ることはありません。

公開リクエストの経路

アプリへのリクエストは、プロバイダーのL7入口 → ノードのNodePort → Service → アプリPodと進みます。Ciliumのポリシー強制は、リクエストを運ぶ段階ではなく別の制御です。運用ダッシュボードへの入口は別の経路です。

  1. アプリのユーザーデータ: HTTPS 443プロバイダーのL7入口

    アプリのユーザーは、登録済みのhostnameでプロバイダーのL7入口に到達します。入口はプロバイダーごとに個別に実装されます。

  2. プロバイダーのL7入口データ: NodePortNodePort Service · Local<app namespace>

    入口はノードのプライベートアドレスとNodePortへ転送します。NodePortは対象の登録時に割り当てられたものです。

  3. NodePort Service · Local<app namespace>データ: PodへDeployment · Pod · ダイジェスト指定のイメージ<app namespace>

    Serviceは同じノード上のアプリPod(Deployment)へリクエストを送ります。externalTrafficPolicy Localのため、送信元アドレスはそのままNetworkPolicyの登録済みCIDR検査に届きます。

  4. Cilium · Helm · VXLANkube-system実行 · 適用: ポリシーの強制NetworkPolicy · 登録済みCIDRのみ<app namespace>

    CiliumがNetworkPolicyを強制します。受信は登録済みCIDRからアプリのポートへのみ許可し、送信は既定で許可せず、承認されたデータベースのアドレスだけが例外です。クラウドのファイアウォールとホストのルールも引き続き必要です。

  5. Railshotユーザーデータ: ダッシュボード · APIdashboard · nginxrailshot-system

    ユーザーは運用の入口からnginxのダッシュボードに入り、nginxが製品APIの前段に立ちます。dashboard Serviceは承認されたnode portが与えられた場合のみNodePortとなり、APIはClusterIPのままです。

確認 · 観測

デプロイの判定はArgoの状態と公開URLの確認だけに基づきます。Podの診断、現在のログ、運用メトリクスは別のシグナルで、判定を変えることはありません。

  1. 製品API · cd.js → bridge.pyrailshot-system観測: revision · health · イメージArgo CD · Applicationargocd

    deployedとなるには、Applicationのsync revision、SyncedとHealthy、operation Succeeded、そしてArgo Application statusに記録されたイメージ参照が、レビュー済みDeploymentのダイジェスト指定のイメージと一致する必要があります。PodのimageIDを直接確認するゲートではなく、Argoの緑の状態表示だけでは一致は確認されません。

  2. 製品API · cd.js → bridge.pyrailshot-system観測: 公開HTTPS確認プロバイダーのL7入口

    登録済みの公開URLをリダイレクトなしで確認します。Argoの判定とこの確認の両方がそろったときだけsucceededを記録します。

  3. 製品API · cd.js → bridge.pyrailshot-system観測: Pod診断 · 判定外Deployment · Pod · ダイジェスト指定のイメージ<app namespace>

    Argoの判定と公開確認の後、登録済みの認証情報でDeployment、ReplicaSet、Podを別途読み取り、imageIDと準備状態をready、progressing、unavailableの診断として追記します。診断が失敗したり異なったりしても、deployedやsucceededの判定が変わることはありません。

  4. 製品API · cd.js → bridge.pyrailshot-system観測: ログtail · Podごとに100行Deployment · Pod · ダイジェスト指定のイメージ<app namespace>

    現在デプロイされているoperationに限り、信頼済みのconfigと登録済みの認証情報で読み取ります。現在のPod(最大3つ)ごとに、実行中のイメージダイジェストが期待するダイジェストと一致することを確かめたうえで、Podあたり最大100行を、機密値を伏せてバイト上限内で読みます。過去の試行にはsuperseded、設定がなければnot_configuredと応答します。ログ集約ではありません。

  5. 観測VM観測: scrape · NodePortrailshot-observability

    観測VMのPrometheusが、ファイアウォールで制限した2つのNodePortを通じてnode-exporterとkube-state-metricsをscrapeします。欠損や古い値はゼロではなく不明として扱います。

  6. 観測VM観測: Blackbox probeプロバイダーのL7入口

    probe_success=1は、そのプローブ地点で2xxを受け取ったことを意味します。すべてのユーザーのアクセスもデプロイされたバージョンも証明せず、publishedをdeployedに昇格させることもありません。

運用者のインストール · 登録

クラスターのインストール、登録bootstrapと対象の入力、Secret、認証情報の更新設定は運用者の入力であり、テナントアプリのpayloadには含まれません。

  1. 運用者のbootstrap実行 · 適用: platformのレンダリング · Secretrailshot-system

    運用者がrender-platform.pyでplatformの宣言をレンダリングし、非公開の運用configとSecretを用意します。リモートMCPはイメージが与えられた場合のみ、個人用gateway(WireGuard)は--personalの場合のみ含まれます。

  2. 運用者のbootstrap実行 · 適用: 登録bootstrapAppProject · cluster Secretargocd

    運用者がargocdに登録bootstrap Roleと空のregistrations Roleをインストールし、対象の入力を用意します。その後、製品APIが実行する登録ヘルパー(environment.py、applications.py)が、スコープを限った認証情報(clusterResources false、許可namespaceを列挙)でrailshot-<target> Secretを書き込み、検証済みの正確な名前をregistrations Roleに追記します。アプリごとの登録では、app-<id> namespaceを同名のAppProjectに結び付けます。

  3. 運用者のbootstrap実行 · 適用: K3s · CiliumのインストールCilium · Helm · VXLANkube-system

    install-k3s.shが単一ノードのK3s serverをインストールし(flannel、network policy、traefik、servicelbはオフ、kube-proxyは維持)、CiliumをHelm releaseとしてインストールします(kubeProxyReplacement false、VXLAN、Hubbleはオフ)。

  4. 更新CronJob · 2時間ごとargocd実行 · 適用: TokenRequest · 6時間railshot-argocd SA · トークン<app namespace>

    railshot-credentials CronJobが、2時間ごとに顧客SAの新しい6時間トークンを要求します。顧客側のRoleは、自身のSAに対するtoken createだけを許可します。6時間はトークン1つのTTLであり、運用者のアクセスが終わる時点ではありません。

すべての接続を同じ太さで表示します。フローを選ぶと、その経路だけを番号付きで強調します。

  • 実線 · リクエスト、bundle、イメージ、宣言が流れる方向
  • 破線 · 実行要求、適用、インストール、認証情報
  • 長い一点鎖線 · 状態、ログ、プローブ、メトリクスの読み取り。判定とは別
  • 太い黒線 · 選んだフロー。番号は順序
  • ボックス · namespace、見出しに名前(ノードではない)
  • 内側のチップ · そのnamespace内の構成要素
  • 破線ボックス · クラスター外
  • railshot-system · 製品
  • argocd · デプロイ権限
  • railshot-build · ホストDocker権限
  • GitHub · 顧客クラスター

docs/architectureにある10月2日の配置決定(2つのロールノード)と公開READMEの図は履歴で、この構成図は645e28aの宣言とコードに従います。矢印は原因と方向を示すもので、すべてのプロバイダー経路が現在稼働していることの証明ではありません。

namespaceとノード
namespaceは名前のスコープです。ハードウェアでも、それ自体でセキュリティ境界でもありません。platformとbuildのロールはnodeSelectorとtaintによるスケジューリング上の配置で、この構成図は運用クラスターのノード数を定めていません。Argo CDのノード配置も別途確認する対象です。
buildノードの権限
runner JobはhostNetwork、ホストのDocker socket、NET_ADMIN、rootで実行されます。namespace、taint、RBACではホストのDocker権限を取り除けないため、これはVM管理者レベルの隔離でも、任意コードのためのサンドボックスでもありません。
顧客namespace
登録済みの対象ごとに、defaultではないnamespaceとAppProjectを使います(例:tenant-demo、アプリごとのapp-<id>)。作成するのは1つのサービスのDeployment(ServiceAccountトークンの自動マウントはオフ)、NodePort Service、NetworkPolicyと、任意のPVCまたは移行Jobだけです。Secretは名前で参照するだけで(pull Secretと承認済みのDATABASE_URL)、作成しません。
AWSの入口
Route 53 Alias → 共有ALB HTTPS 443 → 顧客ノードのプライベートアドレスとNodePort。複数のアプリが1つのノードを再利用し、アプリごとにnamespace、Application、NodePortを個別に持ちます。
GCPの入口
グローバルEXTERNAL_MANAGED HTTPS LB → ゾーンのGCE_VM_IP_PORT NEG → 既存VMのプライベートNodePort。証明書はCertificate ManagerからDNS authorizationで発行されます。モジュールはVMもVPCも所有せず、VM 1台は単一障害点として残ります。
OpenStackの入口
HostとpathのL7ポリシーを持つAmphora TLS listener 443 → HTTP NodePort。既定のアドレスはプライベートVIPで、Octavia、K3s、データベース、DNSはモジュールの外です。公開到達性と現地のAmphoraの準備には別途の受け入れ確認が必要で、このモジュールによる現地での適用はまだ確認されていません。
プロバイダー間
3つの入口はそれぞれ別の実装で、プロバイダー間の自動フェイルオーバーや複製はありません。顧客アプリクラスターは登録済みの対象の一例であり、同じデプロイがすべてのクラウドで同時に動いているという主張ではありません。
運用者のオプション
個人用gateway(WireGuard)は、運用者が--personalを有効にした場合にのみplatformに加わり、顧客クラウドに必須の経路ではありません。サポートされるdirectモードでは、観測VMを運用のcontrol VMと共用できます。