自動プロビジョニング(Cloud)
このドキュメントは、クライアントが契約してから ~3-5 分以内にRoqueOS Cloud VMがゼロから作成される方法 を説明します。使用するために何も理解する必要はありません — これは、デバッグ、サポート、または自分のビジネスのためにフローを適応させたい人のための技術リファレンスです。
フロー図
1. クライアントが /pricing で「Cloud Proを契約」をクリック
│
▼
2. Stripe Checkout(ブラジル)がカード/Pixを処理
│
▼
3. Stripe webhook → Cloud Function `provisionServer`
│
▼
4. Cloud FunctionがHetzner APIを呼び出し(CX32 VMを作成)
+ エフェメラルトークンを生成(TTL 30分)
│
▼
5. VMが起動し、cloud-initが以下を実行
`curl https://roqueos.com.br/install.sh | bash`
--customer-id、--tier、--provisioning-token、
--provisioning-callbackを渡す
│
▼
6. install.shがVM内で実行:
- Dockerイメージをプル
- 永続ボリュームを作成
- roqueos-server + サイドカー(guacd、cloudflared)を起動
- /healthが200を返すのを待つ
- ローカルにPOST /admin/setupでadmin keyを生成
- {customerUid, token, apiKey, apiSecret}でFirebase Functionsにcallback POST
│
▼
7. Cloud Function `provisioningCallback`がトークンを検証し、
サブスクリプションを `active` にマーク、認証情報を保存(TTL 24時間)、
SMTPでウェルカムメールをトリガー
│
▼
8. クライアントはURL + 認証情報のメールを受信
/appへのログインで、新しいサーバー用に既に設定済み関与するコンポーネント
Frontend (roqueos-front)
src/pages/PricingPage.vue— 「契約」ボタンがStripe Checkoutに導くfunctions/index.js— Cloud FunctionsprovisionServerとprovisioningCallbackpublic/install.sh—roqueos-serverの正規インストーラーのミラー
Backend (roqueos-server)
install/install.shv1.1.0-cloud-callback — callbackサポート付きのワンライナーインストーラーsrc/modules/admin/admin.controller.ts— べき等エンドポイントPOST /admin/setup(admin keyを生成)
外部インフラ
- Hetzner Cloud API (
https://api.hetzner.cloud/v1) — サーバー作成 - Cloudflare API — サブドメイン
<id>.cloud.roqueos.com.brの作成(v1.2予定) - Firebase Secret Manager —
HCLOUD_TOKEN、PROVISIONER_SSH_KEY_ID、STANDARD_FIREWALL_IDを保管 - Stripe Brasil — カード/Pix経由のBRL checkout
Tier → Hetzner spec
| RoqueOS Tier | Hetzner SKU | vCPU | RAM | SSD ディスク | COGS €/月 |
|---|---|---|---|---|---|
| Starter | cx22 | 2 shared | 4 GB | 40 GB | 4.51 |
| Pro | cx32 | 4 shared | 8 GB | 80 GB | 7.55 |
| Power | ccx23 | 4 dedicated | 16 GB | 160 GB | 31.20 |
| Enterprise | ad-hoc | varia | varia | varia | varia |
ロケーション: ファルケンシュタイン、ドイツ (fsn1)。イメージ: ubuntu-22.04。
Cloud Function エンドポイント
provisionServer(admin onCall)
トリガー: Stripe webhook customer.subscription.created。内部的には context.auth.token.admin === true を必要とする https.onCall です。
Input:
{
"customerUid": "abc123",
"tier": "pro"
}Output:
{
"ok": true,
"vmId": "12345678",
"vmIp": "65.108.x.x",
"provisioningToken": "<64 hex chars>",
"callbackUrl": "https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback",
"tokenExpiresAt": 1716800000000
}provisioningToken は admin に 一度だけ 表示されます(ログには残りません)。そのSHA-256ハッシュはFirestoreの subscriptions/{uid}.provisioning.tokenHash に TTL 30 分で保持されます。
provisioningCallback(HTTP 公開)
トリガー: 新しく作成されたVMが install.sh 経由でこのエンドポイントに POST します。
公開URL: https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback
なぜFirebase認証なしで公開: 新しいVMはFirebase認証情報を持っていません。認証はエフェメラルトークン(ワンタイム、SHA-256ハッシュ比較)で行われます。
Input(POST JSON):
{
"customerUid": "abc123",
"token": "<64 hex chars>",
"apiKey": "ros_xxxx",
"apiSecret": "ros_secret_xxxx"
}検証:
- ペイロードのシェイプ(token + apiKey + apiSecretのregex)
tokenHashがFirestoreに記録されたものと一致- トークンが期限切れでない
- サブスクリプションがまだ
provisioning状態(以前に完了していない)
Output ok: 200 { "ok": true }Output エラー: HTTP 400/401/404/409/410 + { "error": "<code>" }
可能なエラー:
400 missing_fields— ペイロード不完全400 invalid_*— シェイプ無効401 token_mismatch— トークン誤り(潜在的攻撃、IPをログ)404 subscription_not_found— customerUid が存在しない409 already_completed— callbackが既に成功裏に呼び出された(dedup)409 no_pending_provisioning— サブスクリプションがprovisioning状態にない410 token_expired— 30分を超過
cloud-init の install.sh フラグ
Cloud Function provisionServer は次を実行する user_data(cloud-init)を生成します:
#!/bin/bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y curl ca-certificates
curl -fsSL https://roqueos.com.br/install.sh | bash -s -- \
--customer-id "${CUSTOMER_UID}" \
--tier "${TIER}" \
--provisioning-token "${TOKEN}" \
--provisioning-callback "${CALLBACK_URL}" \
--skip-prompts4つのフラグ --customer-id、--tier、--provisioning-token、--provisioning-callback は install.sh v1.1.0-cloud-callback で追加されました(スクリプトの先頭の INSTALLER_VERSION)。4つすべてが存在する場合、インストーラーは wait_for_server がhealthyを返した後 do_provisioning_callback() をトリガーします。
オペレーター側で検証
callbackを手動でテスト(customerUid/tokenを偽物に置き換え):
curl -sS -X POST \
"https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback" \
-H 'Content-Type: application/json' \
-d '{
"customerUid":"smoke-test",
"token":"0000000000000000000000000000000000000000000000000000000000000000",
"apiKey":"ros_smoketestkey",
"apiSecret":"ros_secret_smoketest_secret_xyz"
}'
# 期待値: HTTP 404 {"error":"subscription_not_found"}トラブルシューティング
| 症状 | 考えられる原因 | 修正 |
|---|---|---|
| ウェルカムメールが届かない | Secret ManagerにSMTP_HOST/USER/PASSが欠落 | gcloud secrets list --project=roqueos | grep SMTP + 再設定 |
VMは作成されたがサブスクリプションが provisioning | install.shがVM内で失敗(callback呼ばず) | VMにSSH: journalctl -u cloud-init -f + docker logs roqueos-server |
Callbackが token_expired を返す | プロビジョニングに30分以上かかった(VM遅延) | provisionServer を再実行してトークン + cloud-initを再生成 |
Callbackが token_mismatch を返す | バグまたは攻撃の試み | Functionのログで送信元IPを確認;疑わしい場合はローテートしてアラート |
Firestoreドキュメントで vmIp: null | Hetzner APIタイムアウトまたは public_net なしの応答 | provisionServerを再試行;ログは gcloud functions logs read provisionServer |
ここに文書化されていない(しかし関連)
- サブスクリプションの自動更新: Stripeは
customer.subscription.updatedwebhook経由で単独で処理。VMは生き続けます。 - キャンセル: webhook
customer.subscription.deleted→ 将来の関数decommissionServerがVMを停止 + 最終snapshot + "あなたのデータは30日間保存されます" メール。 - Tierアップグレード(Pro → Power): v1.2予定 — おそらく
hcloud server change-type経由のマイグレーション。 - カスタマー毎のCloudflare Tunnel: 予定 — 今日各VMはパブリックIP + ポート80/443で公開。Tunnelはedge SP経由でTLSを追加(BRへの低レイテンシ)。
クロスリファレンス
- RoqueOS Cloud overview — プラン、価格、理由
- セルフホストインストール — cloud-init内で実行されるのと同じ
install.sh - Server Admin Panel — 新しくプロビジョニングされたサーバーを管理
- 技術メモリ:
hetzner_architecture_decision.mdリポジトリ内(内部)