Skip to content

Dashboarding an Idol Agency with Metabase and Cloudflare Tunnel

タレント事業のスタッフはDiscord以外をあまり見ない。タレントのマネジメントに時間を取られていて、わざわざ別のツールを開く余裕がない。

でもデータは4箇所に散らばっている。ライブ物販の売上はSquare。イベントのスケジュールはTimeTree。スタッフの稼働記録と現場の報告はDiscord。タスク管理はAsana。「先月のライブ収益と集客の関係」を見ようとすると、スプレッドシートに手作業で転記する羽目になる。

売上や経営指標はスタッフに公開されるべきだと考えている。稼働や勤怠はともかく、事業の数字が見えない状態でマネジメントの判断を求めるのは無理がある。だからDiscord上で指標を可視化する——スタッフが普段いる場所に情報を届ける。より深い分析が必要なときのためにMetabaseのダッシュボードを用意し、Cloudflare Tunnelで安全に公開している。

MetabaseとBIツール

Metabaseはオープンソースのダッシュボード/分析ツール。データソースに繋いでグラフやレポートを作れる。SQLを書く人にも書かない人にも使える。こうしたツール群は一般にBI(Business Intelligence)ツールと呼ばれる。本記事の「BI側」「BIレイヤー」もこの種のツールを指している。

Cloudflare Tunnelとは

プライベートネットワーク内のサーバーを、グローバルIPもファイアウォール解放もなしで公開できるCloudflareのサービス。サーバー側から外向きに接続を張ることで成立する。後ろのセクションで詳しく扱う。

4箇所に散らばったデータ

Square——唯一の信頼できる数値

ライブ会場の物販、チェキ会、チケット。現場の決済はすべてSquare端末を通る。現金で受け取った場合も、必ずSquareに記録を残すルールにしている。これは経理のためだけではない。全売上データがSquareに集約されていれば、それがそのまま経営指標になる。

Square の強みは公式APIがしっかりしていることだ。OAuth2で認証し、Orders / Payments / Catalog の各エンドポイントからデータを取得できる。バッチで定期的に叩けば、売上データは自動的にDBに流れ込む。緊急時にスマホのSquareアプリで決済できるのも現場では大きい。端末が壊れても、スマホ1台あれば売上データの連続性は途切れない。

Squareで現金会計も記録する理由

「現金はSquareを通さなくていい」と思われがちだが、現金売上をSquareに記録しないと、売上データに穴が開く。経理の帳簿とSquareの数字が一致しなくなり、KPIとしての信頼性が崩壊する。「すべてSquareを通す」というルールが、データ基盤の精度を支えている。

TimeTree——業界標準の、APIがないカレンダー

日本のアイドル業界では、TimeTreeがスケジュール管理のデファクトスタンダードになっている。タレント側もファン側も使っている。

問題は、TimeTreeが公式APIを提供していないことだ。過去には存在したが、現在は閉じている。

そしてTimeTree自体の設計が自由度重視で、制約が少ない。イベント名の付け方、場所の書き方、時間の入れ方——すべてが自由入力だ。人間が読む分には問題ないが、プログラムで処理しようとすると正規化の壁にぶつかる。「ライブ」「ライヴ」「LIVE」が同じ意味で混在する世界だ。

APIがないので、ログイン後に内部APIを叩く形でデータを取得している。Playwrightでセッションを確立し、イベントデータをクロールする。セッション管理、ページネーション、レート制限——公式SDKがあれば不要だった苦労が全部のしかかる。

毎回ブラウザでログインするのは遅いし不安定だ。だから一度ログインしたら、cookieとCSRFトークン、それに内部APIが期待する独自ヘッダをセッションファイルに保存しておく。次回以降は保存済みセッションを復元してAPIだけ叩き、認証エラーが返ったときだけブラウザログインに自動フォールバックする。ページネーションも内部APIの作法に従う必要があって、カーソルと終端フラグを組み合わせて続きがあるかを判定する。

Playwrightとは

ブラウザ自動操作のフレームワーク。Chrome / Firefox / WebKit をスクリプトから動かせる。元はE2Eテスト用だが、公式APIのないWebサービスからデータを取得するスクレイピング用途でも広く使われている。

TimeTree APIの経緯

TimeTreeはかつてOAuth2ベースの公式APIを提供していた。カレンダーの読み書き、イベントのCRUDが可能だったが、現在は新規のアプリケーション登録ができず、事実上閉鎖されている。ログイン後の内部APIは残っているが、公開されたものではなく、仕様変更で突然壊れるリスクがある。

Discord——自由テキストの構造化地獄

スタッフの稼働管理と、ライブイベントの記録にDiscordを使っている。スタッフが普段いる場所だから自然とそうなった。

問題は、そこに蓄積されるデータの形だ。1つのライブにつき1つのスレッドが立ち、スタッフが情報を書き込んでいく。日時、会場、出演者、セットリスト、チケット代、集客数——すべてが自由テキストだ。

日付の書き方だけでも「4/15」「4月15日」「2026.4.15」が混在する。会場名の表記揺れ、金額のフォーマット、セットリストの番号の振り方。人間が読めば同じ情報だが、プログラムで構造化しようとすると正規表現では追いつかない。書き方のバリエーションが多すぎて、ルールベースで追いかけるときりがない。

最終的にClaude APIのtool useで構造化抽出に切り替えた。スレッドのテキストを投げて、日時・会場・出演者・セットリスト・集客数をJSON構造で返してもらう。

たとえば、スタッフがこう書いたとする。

4/15 新宿BLAZE
出演: ユニットA / ユニットB
セトリ: 1. 星空ダイブ 2. カタルシス 3. MC 4. サマーレイン 5. 未来地図
チケ代 3500円
来場 82人
物販 チェキ34枚 / Tシャツ8枚

tool useのスキーマを定義しておけば、このテキストから以下のJSONが返る。

json
{
  "date": "2026-04-15",
  "venue": "新宿BLAZE",
  "artists": ["ユニットA", "ユニットB"],
  "setlist": ["星空ダイブ", "カタルシス", "MC", "サマーレイン", "未来地図"],
  "ticket_price": 3500,
  "attendance": 82,
  "merch": { "cheki": 34, "tshirt": 8 }
}

「4/15」が「2026-04-15」になり、「チケ代 3500円」がticket_price: 3500になる。表記揺れも文脈から判断してくれるし、新しいパターンが出てきてもプロンプトの調整で対応できる。正規表現で追いかけていた頃とは安定感が違う。

LLMで構造化抽出をやるときの心配は「JSONじゃない応答が返ってきたら」という点に尽きる。tool_choiceで特定のツール呼び出しを強制すれば、出力は必ず宣言したスキーマに沿う。逸脱を考えなくていい。モデルもHaiku相当で足りる——これは推論というより、自由テキストを定義済みのフィールドに振り分ける変換タスクだから、最小サイズのモデルで安定して動く。

tool useによる構造化抽出

Claude APIの tool use(function calling)を使うと、LLMの出力をJSONスキーマに強制できる。自由テキストを「理解」して構造化するのはLLMの得意分野だが、出力が自由テキストでは後続の処理が困難になる。tool useで出力形式を固定することで、LLMを正規化エンジンとして安定的に組み込める。

Asana——統合していないタスク管理

4つ目はAsanaだ。プロジェクトとタスク、サブタスク、コメント——マネジメント側の進行管理はここに集まる。OAuth2の公式APIがあり、Tasks / Projects / Comments のCRUDが揃っているので、技術的には他のソースと同じくMySQLに流し込める。

それでも今は優先度を下げている。ライセンスコストが効くからだ。Starterで$10.99/月、Advancedで$24.99/月(いずれも人単位、年契約)。円換算でおおよそ1,650〜3,750円/人/月のレンジになる。スタッフ全員に配ると、小規模チームでも月額数万円のオーダーで効いてくる。

売上や集客のように経営判断に直結するデータと違って、タスク管理のデータをBI側に流す価値が、このコストに見合うかはまだ判断がついていない。だから当面は他と切り離したままにしている。

クローラーとBotが同居するリポジトリ

1つのリポジトリに、2つの役割が同居している。

クローラーはバッチ処理だ。Square APIを叩いて売上データを取得し、Playwrightでログインした TimeTreeからスケジュールをクロールし、Discordのスレッドを読み取って構造化する。すべてMySQLに書き込む。

Botは常駐プロセスだ。DiscordにWebSocket接続を維持し、MySQLからデータを引いてチャンネルに返す。スタッフがDiscordを開くだけで、直近の売上やイベント実績が目に入る。

データの出口は2つある。日常の指標はBotがDiscordに流す。曜日別・会場別の傾向分析や月次推移など、より深い分析をしたいときはMetabaseのダッシュボードを開く。BotとMetabaseの間にETLパイプラインはない。MySQLが唯一のインターフェースだ。

Cloudflare Tunnel + Access でMetabaseを公開する

Metabaseが動いているのはオンプレのVMで、グローバルIPを持っていない。プライベートネットワークの中にいる。これを社外のスタッフがブラウザからアクセスできるようにしたい。

VPNを張る方法もある。でもスタッフにVPNクライアントをインストールさせ、接続手順を教え、トラブルシューティングをする未来が見える。タレント事業のスタッフはエンジニアではない。「ブラウザでURLを開いてGoogleでログインする」以上の手順は求められない。

Cloudflare Tunnel——ポートを開けない公開

Cloudflare Tunnelは、サーバー側からCloudflareのエッジに向けてアウトバウンド接続を張る。サーバーにグローバルIPもいらないし、ファイアウォールにポートを開ける必要もない。

cloudflared というデーモンをVM上で起動するだけだ。このデーモンがQUICプロトコル(UDPベースの暗号化された通信プロトコル)でCloudflareのエッジに常時接続し、外部からのリクエストをローカルのMetabase(localhost:3000)に中継する。

Cloudflare Tunnelの接続方向

従来のリバースプロキシはサーバーがインバウンド接続を受け付ける。Cloudflare Tunnelは逆で、cloudflaredがCloudflareのエッジに対してアウトバウンド接続を張る。つまり、サーバー側のファイアウォールはインバウンドを全拒否していても動作する。NATの内側、VLANの奥、グローバルIPのないVM——どこにいても外部公開できる。

設定はシンプルだ。

yaml
tunnel: <tunnel-id>
ingress:
  - hostname: metabase.example.com
    service: http://localhost:3000
  - service: http_status:404

グローバルIPの取得、ルーターのポートフォワーディング、Let's EncryptでのTLS証明書管理——これらすべてが不要になる。TLS終端はCloudflareのエッジが担う。

Cloudflare Access——ゼロトラストの認証レイヤー

ゼロトラストとは

「社内ネットワークなら安全」という前提を捨て、すべてのアクセスを認証・認可で判定するセキュリティモデル。VPNで「中に入れば信用する」古典的モデルの対極にある。場所ではなくIDで境界を引く。

Tunnelだけでは、URLを知っている人は誰でもアクセスできてしまう。Cloudflare Accessは、アプリケーションの手前に認証レイヤーを挟む。

Access のポリシーで「このメールドメインのGoogleアカウントだけ通す」と設定する。これでURLが漏れても、許可されたGoogleアカウントを持っていない限りMetabaseには到達できない。

この構成の前提:Google Workspace

Cloudflare Accessでメールドメインベースのポリシーをかけるためには、スタッフに独自ドメインのGoogleアカウントを発行している必要がある。Google Workspaceでスタッフ全員に @example.com のメールアドレスを配っていれば、そのドメインだけ許可すればアクセス制御が成立する。個人のGmailアカウントを使っている場合は、メールアドレスを1つずつ登録することになる。

Metabase Free版の二重ログインを解消する

ここが一番の工夫だ。

Metabase のFree版はSAML/OIDCによるSSOをサポートしていない。Pro版(有料)の機能だ。つまり、Cloudflare Accessで認証を通過しても、Metabase側でもう一度ログインが必要になる。二重ログインだ。

これを解決するために、Cloudflare AccessとMetabaseの両方に同じGoogle OAuth Client IDを設定した。

流れはこうなる。

  1. スタッフがURLにアクセスする
  2. Cloudflare AccessがGoogle認証にリダイレクトする
  3. Googleでログインする(ここでブラウザにGoogleセッションが残る)
  4. Metabaseのログイン画面が表示される
  5. 「Sign in with Google」をクリックする
  6. Googleセッションが残っているので、再入力なしで即座にログインされる

完全なシングルサインオンではない。Metabaseのログイン画面でワンクリックが必要だ。しかし、パスワードの入力は一度もない。Googleアカウント1つですべてが通る。

Free版でこの体験が実現できるのは、Cloudflare AccessのIdP(Identity Provider)設定とMetabaseのGoogle Sign-In機能が、同じOAuth Client IDを共有できるからだ。Pro版のSSO機能(SAML/OIDCによる自動プロビジョニングやグループマッピング)とは別物だが、小規模チームなら十分な認証体験になる。

なぜ同一OAuth Client IDで動くのか

GCPのOAuth Client IDには複数のリダイレクトURIを登録できる。Cloudflare Access用のコールバックURLとMetabase用のコールバックURLを同一のClient IDに登録すれば、ブラウザのGoogleセッションが両方に効く。Metabase側から見れば「Google Sign-Inで認証された正規のユーザー」であり、Cloudflare Accessを経由したかどうかは関係ない。

Zero Trust Free プランの制限

Cloudflare Zero Trust のFreeプランは50ユーザーまで使える。タレント事業の関係者——マネージャー、経理、プロデューサー——を合わせても50人に収まる規模なら、ランニングコストはゼロだ。

セッション有効期間は24時間に設定している。毎日最初のアクセスでGoogle認証が走るが、それ以降はシームレスにアクセスできる。

2つの可視化レイヤー

データの見せ方は、誰が・いつ・何を見たいかで変わる。

Discord BotMetabase
対象現場スタッフ全員マネージャー・経営層
頻度毎日、受動的に目に入る必要なときに能動的に開く
内容売上速報、直近のイベント実績クロス分析、トレンド、KPI
操作見るだけフィルタ・ドリルダウン

Discordに流れるのはテキストだけではない。BotはChart.jsとchartjs-node-canvasでサーバーサイドにPNG画像を生成し、Discordチャンネルに投稿する。売上推移の折れ線グラフ、メンバー別売上の棒グラフ、会場別ランキングの横棒グラフ——クエリ結果に応じて適切なグラフ形式が自動選択される。

スタッフはタレントのマネジメントで手一杯だ。わざわざダッシュボードを開く時間はない。普段いるDiscordにグラフ付きの指標が流れてくるだけで、事業の現在地が共有される。

Metabaseの出番は「なぜこの会場は集客が伸びないのか」「チェキの売上比率は半年でどう変わったか」といった問いに答えるときだ。3つのデータソースがMySQLに集約されているので、クロスソースの分析がSQLひとつで書ける。

  • イベント別収益: Squareの売上データとDiscordのイベント記録を突き合わせて、イベントごとの売上・集客・客単価を出す
  • 曜日・会場別分析: TimeTreeのスケジュールデータとSquareの売上を掛け合わせて、どの曜日・どの会場の収益性が高いかを可視化する
  • スタッフ稼働: Discordの記録から稼働時間を集計し、報酬計算の基礎データにする
  • 月次推移: 売上・集客・イベント数の推移をトラッキングする

小さく始められる構成

この仕組みの構成要素はすべて無料か、低コストだ。

コンポーネントコスト
Metabase Free版無料
Cloudflare Tunnel無料
Cloudflare Access (50ユーザーまで)無料
MySQL無料
Square API無料
Discord Bot無料
VM (オンプレ or VPS)既存インフラ

エンタープライズ向けのBIツールやVPN製品に投資する前に、この組み合わせで十分かどうか試す価値はある。Metabase Free版の制限(SSOなし、行レベルのアクセス制御なし)は、Cloudflare Accessの認証レイヤーと運用ルールで補える範囲だ。

必要なのは、データを1つのDBに集める仕組みと、それを安全に公開する経路だ。前者はBot、後者はCloudflare Tunnel。どちらも大げさなインフラは要らない。

本当のボトルネックはツールではなく、データを1箇所に集めるルールの徹底だ。「すべてSquareを通す」「ライブ記録はスレッドに書く」——技術的にはただのETLだが、現場の人間がルールを守り続ける仕組みのほうがよほど難しい。ダッシュボードは、その習慣が定着して初めて意味を持つ。