
ワークショップ参加者ごとのNocoBase環境をn8nで自動化する方法を考えてみたよ
はじめに
社内でNocoBaseのハンズオンワークショップを開催することになりました。
NocoBaseは、オープンソースのノーコード開発プラットフォームです。ブラウザ上でデータベースのテーブル設計やUI構築ができるツールで、Airtableのセルフホスト版をイメージしてもらうとわかりやすいかもしれません。
ワークショップでは参加者それぞれが自由にテーブルを作ったりUIをカスタマイズしたりするので、1人1環境が必要です。共有環境だと他の人のデータが見えたり、設定を壊し合ったりしてカオスになります。
とはいえ、参加者の数だけ手作業でECSサービスやDBを作っていくのは現実的ではありません。
じゃあ自動化するか、ということで構築したのが今回のプロビジョニングシステムです。
どんな環境を作りたいのか
要件を整理するとこんな感じです。
- 参加者ごとに独立したURLを持つ
- 参加者ごとに独立したDatabaseを持つ
- 参加者ごとに独立したファイルストレージを持つ
- ワークショップ終了後にまとめて削除できる
- なるべく手作業をしないで環境の作成・削除ができる
URL形式は https://{envId}.noco-workshop.example.com です。envIdは英小文字+数字の6文字で、ランダムに生成します(例: k7x9m2)。
全体の構成はこうなっています。
*.noco-workshop.example.com
│
▼
┌─────────────────┐
│ Cloudflare │
│ Access │
│ (Warp認証) │
└────────┬────────┘
│ HTTPS
▼
┌─────────────────┐
│ ALB │
│ (ホストベース │
│ ルーティング) │
└────────┬────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ k7x9m2 │ │ m3p8x1 │ │ q9w2n5 │
│ Fargate │ │ Fargate │ │ Fargate │
│ (NocoBase) │ │ (NocoBase) │ │ (NocoBase) │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
EFS /k7x9m2 EFS /m3p8x1 EFS /q9w2n5
│ │ │
└─────────────────┼─────────────────┘
▼
┌─────────────────┐
│ RDS MySQL │
│ (開発環境間借り) │
└─────────────────┘
DB: noco_k7x9m2
DB: noco_m3p8x1
DB: noco_q9w2n5
Cloudflare Access(Warp認証)でアクセス制御し、ALBのホストベースルーティングで各envIdのFargateタスクに振り分けます。ストレージはEFSのAccess Pointで分離し、DBはRDSにenvIdごとのデータベースを作成します。
これらのAWSリソースの構築にはIaC(Infrastructure as Code)ツールを使います。共有の土台部分をTerraform、参加者ごとの環境をAWS CloudFormationで管理する二層構成にしました。
設計のポイント: 1スタック = 1環境
このシステムの核になる設計判断は、CloudFormationスタック1つ = 参加者環境1つという対応関係です。
つまり、参加者k7x9m2の環境を作りたければ nocobase-workshop-k7x9m2 というCloudFormationスタックを作成し、削除したければそのスタックを削除する。それだけです。
この設計のメリットは3つあります。
- 作成と削除がアトミック — スタックの作成・削除1回で、ECSサービスやターゲットグループ、DB、ストレージなど関連リソースがまとめて操作される
- 依存関係を自動管理 — CloudFormationがリソース間の依存関係を解決してくれるので、削除順序を気にしなくていい
- 後片付けが確実 —
DeleteStackを呼ぶだけで、データベースのDROP DATABASEまで含めて全部消える
TerraformとCloudFormationの二層構成
「Terraformを使っているのに、なぜCloudFormationも使うの?」と思われるかもしれません。
理由は、リソースの性質が2種類あるからです。
| 層 | ツール | 対象 | ライフサイクル |
|---|---|---|---|
| 共有基盤 | Terraform | ALB, ECS Cluster, EFS, RDS, Lambda等 | ワークショップ全体で1回だけ作成 |
| 参加者環境 | CloudFormation | ECS Service, Task Def, Target Group等 | 参加者ごとに動的に作成・削除 |
ALBやECSクラスターはワークショップを通じて共有する「土台」です。これらはTerraformで事前に構築します。
一方、参加者ごとのECSサービスやターゲットグループは動的に作ったり消したりするもの。Terraformは宣言的なツールなので、こういう動的な作成・削除には向いていません。ここにCloudFormationスタックの「1スタック = 1環境」が効いてきます。
事前に構築しておくもの(Terraform)
Terraformで事前に構築する共有リソースの一覧です。
| リソース | 役割 |
|---|---|
| ALB + Listener | HTTPS受付、ホストベースルーティングの土台 |
| ECS Cluster | Fargateタスクを動かすクラスター |
| EFS + Mount Target | ファイルストレージ(各AZにマウントターゲット配置) |
| Security Groups | ALB用、ECS用、EFS用、Lambda用 |
| IAM Roles | ECS実行ロール、タスクロール、CFn実行ロール等 |
| Lambda (db-manager) | DB作成/削除 + APP_KEY管理(CFn Custom Resource用) |
| Lambda (provisioner) | n8nからの呼び出しを受けてCFn APIを操作 |
| S3 | CloudFormationテンプレートの配置先 |
| SSM Parameter Store | DB接続情報の格納 |
| CloudWatch Logs | ECSタスクのログ出力先 |
| VPC Endpoints | Lambda(VPC内)からSSMへのアクセス用 |
| ACM | Cloudflare Origin CA証明書のインポート |
terraform apply 1回で、これらがまとめて構築されます。この土台の上に、参加者ごとの環境がCloudFormationスタックとして積み上がっていくイメージです。
参加者ごとに動的に作るもの(CloudFormation)
CloudFormationスタック nocobase-workshop-{envId} を作成すると、以下のリソースが自動的に生まれます。
| リソース | 説明 |
|---|---|
| Custom Resource (DB) | Lambdaを呼び出してCREATE DATABASE noco_{envId}を実行 |
| Custom Resource (APP_KEY) | Lambdaでランダムなキーを生成し、SSM SecureStringとして保存(後述) |
| EFS Access Point | /{envId} パスで参加者専用のストレージ領域を確保 |
| ECS Task Definition | コンテナの定義(イメージ、環境変数、シークレット等) |
| Target Group | ALBからのトラフィックを受けるターゲット |
| ALB Listener Rule | {envId}.noco-workshop.example.comへのリクエストをルーティング |
| ECS Service | Fargateタスクを起動・維持するサービス |
APP_KEYはNocoBaseが内部で使う暗号化キーです。セッションやトークンの署名などに使われるため、環境ごとにユニークな値が必要になります。ここではLambdaでsecrets.token_hex(32)によりランダムな値を生成し、SSM Parameter StoreにSecureStringとして保存しています。ECSタスクは起動時にSSMからこの値を取得します。なお、SecureStringの読み書きにはKMSの暗号化・復号権限も必要です。今回はデフォルトのSSM用KMSキー(alias/aws/ssm)を使用しているため、IAMロール側でそのキーへのアクセスを許可しています。
ポイントはCustom Resourceです。CloudFormationには「既存のRDS上に論理データベースを作る」とか「ランダムなキーを生成してSSMに保存する」という機能はありません。そこで、Lambda関数をCustom Resourceとして呼び出すことで、CloudFormationのライフサイクル(Create / Delete)に組み込んでいます。
スタック作成時にはLambdaが既存のRDSに接続してCREATE DATABASEを実行し、スタック削除時には同じLambdaがDROP DATABASEを実行します。RDSインスタンス自体は共有基盤としてTerraformで管理しているので、ここで作成・削除するのはあくまで参加者ごとの論理データベースです。スタックを消すだけで論理データベースまで消えるというわけです。
プロビジョニングのフロー全体はこうなります。
n8n (トリガー)
│
└─ Provisioner Lambda
│
└─ cloudformation:CreateStack
│
▼
CloudFormation Stack: nocobase-workshop-{envId}
│
├─ Custom Resource (Lambda) ─── CREATE DATABASE noco_{envId}
│
├─ Custom Resource (Lambda) ─── APP_KEY生成 → SSM SecureString
│
├─ EFS Access Point ─────────── /{envId}
│
├─ ECS Task Definition
│
├─ Target Group + ALB Listener Rule
│
└─ ECS Service ─────────────── Fargateタスク起動
n8nで環境の作成と削除を自動化
ここまでの仕組みをAPI(Lambda)として整えたので、あとは「誰がいつ呼ぶか」です。
ここでn8nの登場です。n8nはオープンソースのワークフロー自動化ツールで、GUIでノードをつなげてワークフローを組めます。プログラミング不要で自動化フローが作れるので、運営スタッフでも操作できます。
n8nとProvisionerの関係
n8nからProvisioner Lambda(AWS Lambdaノード)を呼び出す構成です。
n8nにはCloudFormation専用のノードがないので、CloudFormation APIを呼ぶにはHTTPリクエストノードでAWS署名付きリクエストを自前で組み立てる必要があります。しかもCreateStackにはテンプレートURL、パラメータ十数個、実行ロールARNなどを正しく渡す必要があり、ワークフロー上でやるとノードの設定が複雑になります。パラメータの変更があったときにn8n側を修正しなければならないのも面倒です。
一方、n8nにはAWS Lambdaノードが組み込みであります。そこで、間にProvisioner Lambdaを挟みました。n8nからは {"action": "create", "env_id": "k7x9m2"} とだけ渡せばよく、パラメータの組み立てやバリデーションはLambda側に閉じ込めています。
n8n ──→ Provisioner Lambda ──→ CloudFormation API
│
│ action: "create" / "delete" / "describe"
│ env_id: "k7x9m2"
▼
CreateStack / DeleteStack / DescribeStacks
Provisioner LambdaはenvIdのバリデーション(英小文字+数字6文字)を行い、環境変数に持っている各種ARNやIDを使ってCloudFormationのパラメータを組み立てます。n8n側はシンプルなまま、インフラの詳細はLambdaに任せるという分担です。
環境作成フロー
- n8nのCodeノードでランダムなenvId(6文字)を生成
- Provisioner Lambdaに
{"action": "create", "env_id": "k7x9m2"}を送信 - LambdaがCloudFormation
CreateStackを呼び出し - n8nでポーリング: Lambdaに
{"action": "describe", "env_id": "k7x9m2"}を定期送信 - ステータスが
CREATE_COMPLETEになったら完了 - 結果をスプレッドシートに記録+Slack通知

環境削除フロー
- Provisioner Lambdaに
{"action": "delete", "env_id": "k7x9m2"}を送信 - n8nのループで
describeを繰り返し、DELETE_IN_PROGRESSを確認 - スタックが消えたら(
NOT_FOUND)削除完了 - 完了/失敗をSlackに通知

環境一覧の管理
作成した環境はn8nのData Table機能で一覧管理しています。envId、URL、作成日時、ステータスなどを記録しておくことで、運営スタッフが「今どの環境が動いているか」をすぐに把握できます。
n8nのワークフローからData Tableを直接読み書きできるので、環境の作成・削除時に自動でテーブルを更新する仕組みも簡単に作れます。

やってみてどうだったか
良かった点
一番の成果は、環境の作成が完全に手作業ゼロになったことです。本運用ではGoogleフォームを用意し、回答がスプレッドシートに記録される仕組みにしました。n8n側からスプレッドシートをポーリングして新しい回答を検知し、自動で環境を作成する流れです。Googleフォームの標準UIにはwebhook設定がないため、GAS(Google Apps Script)を使えば実現できますが、今回は追加のスクリプト管理を避けてシンプルにポーリング方式を採用しました。
後片付けも確実です。DeleteStackを呼ぶだけで、ターゲットグループやECSサービスはもちろん、論理データベースまで含めてきれいに消えます。消し忘れによるコスト増を心配しなくてよいのは精神的に楽でした。
運営面では、スプレッドシートで環境一覧を管理しているので、非エンジニアのスタッフでも「今どの環境が動いているか」をすぐに把握できます。
課題・注意点
CloudFormationスタックの作成には3〜5分程度かかります。ワークショップ開始直前に慌てて作るのではなく、事前に作成しておくのが安全です。
コスト面では、VPC内のLambdaからSSMにアクセスするために追加したVPCエンドポイントに注意が必要です。Interface型のエンドポイントはENIを作るので、使っていなくても費用が発生します。ワークショップ期間外は削除を検討してもよいでしょう。
スケーラビリティについても2点ほど。まず、Lambda同時実行数を5に制限しているため、大量の環境を一気に作ろうとするとスロットリングされます。参加者が多い場合は順次作成するフローにしておくと安心です。また、1参加者につき1つのALBリスナールールを作成する設計なので、AWSのデフォルトクォータ(1リスナーあたり100ルール)にも注意が必要です。100名を超える規模の場合は、事前にAWSサポートへ上限緩和申請をしておきましょう。
もうひとつ、Custom Resourceの削除失敗リスクがあります。スタック削除時にLambda内でエラーが発生し、CloudFormationにSUCCESSシグナルを返せなかった場合、スタックがDELETE_FAILED状態で止まります。削除時はエラーが起きても必ずSUCCESSを返すフェイルセーフなエラーハンドリングにしておくと安心です。
ワークショップ用途にはちょうどいい粒度
今回のシステムは、本番環境のマルチテナントアーキテクチャとは設計思想が異なります。「数日間のワークショップで使い、終わったら全部消す」という前提があるからこそ、1スタック=1環境というシンプルな構成が成り立ちます。
逆に言えば、ワークショップやハンズオンのように使い捨て前提の独立環境が必要なケースには、このアプローチはかなりフィットします。
まとめ
- 1スタック = 1環境のアプローチは、作成・削除がアトミックで後片付けも確実。使い捨て環境に向いている
- Terraform + CloudFormationの二層構成で、共有基盤と参加者ごとのリソースをきれいに分離できた
- n8nで作成・削除フローを自動化することで、非エンジニアの運営スタッフでも環境管理が可能になった
- CloudFormationのCustom Resourceを活用すれば、DB作成やシークレット管理もスタックのライフサイクルに組み込める
ワークショップ環境の動的プロビジョニングを検討している方の参考になれば幸いです。なお、「1スタック = 1環境」のアプローチ自体はワークショップに限らず、営業デモ環境やQA・検証環境のオンデマンド払い出しなど、使い捨て前提の独立環境が必要なケースに応用できます。









