料金表Loading...
利用ガイドに戻る

Web サービス (SaaS アプリ)

ToukaCloud のコンテナホスティング — イメージをデプロイして Web アプリを公開する方法

1. 概要

Web サービス (SaaS アプリ) は、ToukaCloud に push したコンテナイメージを、 Kubernetes 上で動かして Web アプリを公開するホスティングサービスです。以下の機能を提供します。

  • イメージ指定: レジストリから任意のイメージを選択 (例: registry.max3584.net/...)
  • 自動ドメイン割り当て: {appName}.{tenantId}.tenant.max3584.net を自動生成
  • HTTPS 対応: Let's Encrypt による SSL 証明書の自動発行・更新
  • スケーリング: レプリカ数の動的調整で複数インスタンスを実行
  • 環境変数: アプリに必要な設定値やシークレットを注入
  • 永続ストレージ: PersistentVolumeClaim による永続データ保存

2. 前提条件

  • 有効なライセンス (S 以上のいずれか) があること — ライセンスページで確認
  • コンテナイメージを ToukaCloud レジストリ (registry.max3584.net) に push 済みであること
  • アプリが環境変数で PORT を受け取るか、フォーム入力のポート番号でリッスンしていること

3. アプリを作成

新しい SaaS アプリを作成するには、/saas/create ページでフォームを記入して送信します。以下が主要なフィールドです。

3.1 アプリ名

  • 使える文字: 小文字英数字 (a-z 0-9) とハイフン (-)
  • 英小文字で始まり、末尾は英数字で終わる (2 〜 63 文字)
  • ハイフンを連続させない (my--app 不可)
  • 例: my-app, web-server
  • 一度作成すると変更不可(削除して再作成してください)
  • ドメイン名として使用: my-app.{tenantId}.tenant.max3584.net

3.2 Docker イメージ

フルパスでイメージを指定します。レジストリ画面から直接コピーできます。

registry.max3584.net/<tenantId>/<userId>/<projectName>/myapp:latest

または外部イメージ (例: nginx:latest, node:20-alpine) も指定可能ですが、内部レジストリ経由のことが多いです。

3.3 ポート番号

  • アプリが内部でリッスンするポート (例: 3000, 8080)
  • 有効範囲: 1 〜 65535
  • 複数ポートの場合は、最初のメインポートを指定(複数ポート機能は将来対応予定)

3.4 レプリカ数

並行して実行するアプリインスタンス数です。プラン別の上限は以下のとおりで、 単一の SoT (components/document/PlanLimitsTable.tsx) を ライセンスガイド と共有しています。

プラン最大アプリ数1 アプリあたりのレプリカ上限
SAAS_FREE11
SAAS_BASIC31
SAAS_PRO101
SAAS_ENTERPRISE1
S11
M32
L53
XL105
XXL208
MAX10

レプリカ数を 0 にするとアプリは停止状態になります。 後で変更・再開できます。

3.5 環境変数 (オプション)

アプリに必要な環境変数をカスタマイズで設定できます。

NODE_ENV = production
API_KEY = sk-xxxxxxxx
DEBUG = false

データベース接続情報を手動で入力するか、データベース連携機能を使用します。

3.6 永続ストレージ (オプション)

アプリがファイルを保存する場合、永続ストレージを有効化できます。 容量はプランに含まれており (S 1GB / M 2GB / L 5GB / XL 10GB / XXL 20GB / MAX 50GB)、超過する場合はアプリストレージアドオンで 追加できます。

  • サイズ: 1 〜 10 GB(ライセンス容量内)
  • マウントパス: コンテナ内のパス (例: /data, /var/lib/app)
  • データはアプリ再起動後も保持されます

3.7 プライベートレジストリの認証 (任意)

ToukaCloud のレジストリ (registry.max3584.net/…) を使うときは アクセストークンの選択ボックスが出ます。一方、Docker Hub の private repo、 ghcr.io の private、独自レジストリなど 外部のプライベートレジストリ を使う場合は、フォームの「プライベートレジストリの認証を使う」チェックを 入れて UsernamePassword / Token を入力します。

  • 入力された認証情報は kubernetes.io/dockerconfigjson Secret として作成され、Pod の imagePullSecrets に自動付与されます
  • anonymous で pull できる public image ではチェック不要
  • 既存アプリの認証情報は設定ページで差し替え可能。password 欄を空のまま保存すれば 現在の認証情報をそのまま使い続けます (「認証情報を削除」ボタンで完全クリアも可能)

3.8 作成完了

「アプリを作成」をクリックすると、Kubernetes にリソースがデプロイされます。 数秒〜数分で以下が進行します。

  • Namespace とネットワークポリシーの作成
  • Deployment 作成 — イメージ pull と Pod 起動
  • Service 作成 — クラスタ内通信用
  • IngressRoute 作成 — Traefik による HTTP/HTTPS ルーティング
  • DNS レコード作成 — 公開ドメイン設定
  • SSL 証明書リクエスト — Let's Encrypt から自動発行

4. 公開ドメイン

各アプリには自動的に公開ドメインが割り当てられます。アクセス方法は以下の通りです。

4.1 ドメイン形式

https://{appName}.{tenantId}.tenant.max3584.net

例:
https://my-app.a1b2c3d4.tenant.max3584.net
https://web-server.f9e8d7c6.tenant.max3584.net
  • appName: 作成時に指定したアプリ名
  • tenantId: あなたのテナント ID (自動生成、8 桁の hex)
  • ベースドメイン: tenant.max3584.net

4.2 HTTPS による自動保護

すべてのアプリは自動的に HTTPS で保護されます。Let's Encrypt により無料で SSL 証明書が発行されます。

  • HTTP からの自動リダイレクト — http://... にアクセスしても自動的に https://... に転送
  • 証明書の自動更新 — 有効期限の 30 日前に自動更新
  • SAN 対応 — 複数ドメイン対応 (将来のカスタムドメイン対応に備え)

4.3 Let's Encrypt 証明書のタイミング

初回デプロイ時、Traefik から Let's Encrypt への ACME チャレンジが発生します。

  • DNS 設定後、約 5 〜 10 分で ACME チャレンジが完了 しブラウザで安全に表示されます
  • その間、自己署証明書または警告ページが表示される可能性があります(正常です)
  • 完了後は「保護された接続」として表示され、セキュリティ警告は出ません

5. 環境変数 / シークレット

アプリに設定値やシークレットを注入する方法は複数あります。

5.1 作成時に設定

/saas/create ページで「カスタム環境変数」セクションから、KEY=value の形式で入力できます。

NODE_ENV = production
DATABASE_URL = postgresql://user:pass@db.internal:5432/mydb
API_SECRET = sk-abc123xyz
LOG_LEVEL = info

5.2 データベース連携

ToukaCloud のデータベースサービスを連携すると、接続情報がアプリの 環境変数として自動注入されます。クレデンシャルは作成・編集の UI には一切表示されず、デプロイ直前にサーバ側で取得され Pod 環境変数に流し込まれます。 ローテーション時もアプリの再デプロイで自動的に最新値が反映されます。

5.2.1 ロール (権限分離)

1 つのデータベースに対して用途別の MariaDB ユーザーを発行できます。 アプリには必要最小限のロールだけ渡すのが推奨です。

ロール権限用途
ownerDDL + DML(フル権限)マイグレーション、Adminer などスキーマ操作
appDML のみ(SELECT/INSERT/UPDATE/DELETE)通常のアプリ接続。SQL インジェクション対策として推奨
readonlySELECT のみダッシュボード、レプリカ的な参照系

複数ロールを同時に渡すと env 名にロール名がプレフィックスされます (例: APP_DATABASE_URLREADONLY_DATABASE_URL)。 1 つだけ選んだ場合はプレフィックスなし(DATABASE_URL)。

5.2.2 接続フォーマット

アプリが期待する設定の形に合わせて 3 つから選びます。

フォーマット注入される env向いてるアプリ
separateHOST + PORT + DATABASE + USER + PASSWORD(5 個)Ghost、Rails、Django など個別 env を読むタイプ
combinedHOST(host:port) + DATABASE + USER + PASSWORD(4 個)HOST に host:port を期待する古めの Java / PHP 系
urlDSN URL 1 本(mysql://user:pass@host:port/dbNode.js (sequelize, prisma) など DATABASE_URL 1 本で済むやつ。creds が URL に埋め込まれるためロール 1 つ限定

5.2.3 env 名プリセット

env 名はプリセットから選ぶか手で書きます。 切替時に既存の env 名が一括で書き換わるので、ベースとして近いものを 選んで足りない所だけ手で直すのが速いです。

プリセットenv 名の例(app ロール / separate
標準 DATABASE_*DATABASE_HOST / DATABASE_PORT / DATABASE_NAME / DATABASE_USER / DATABASE_PASSWORD
MySQL MYSQL_*MYSQL_HOST / MYSQL_PORT / MYSQL_DATABASE / MYSQL_USER / MYSQL_PASSWORD
短縮 DB_*DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASSWORD
カスタム任意の名前を手で指定(Ghost の database__connection__host のような特殊形式に使用)

5.2.4 デプロイ後の変更

アプリ詳細ページの「連携 DB」セクションで、リンク先データベース・ ロール構成・フォーマット・env 名をいつでも変更できます。保存すると 再デプロイされ、新しい接続情報が反映されます。連携を解除したい 場合は DB ドロップダウンで「連携しない」を選びます。

5.3 予約プレースホルダ ${...}

環境変数の 側に ${TOKEN} 形式で 書くと、デプロイ直前にアプリ固有の値へ自動置換されます。アプリのホスト名や URL を手で組み立てる必要がなくなり、たとえば Ghost の url や OAuth コールバックの設定が一行で済みます。

トークン解決値(例: appName=blog, tenantId=abc12345, port=2368
${APP_NAME}blog
${TENANT_ID}abc12345
${APP_HOSTNAME}blog.abc12345.tenant.max3584.net
${APP_URL}https://blog.abc12345.tenant.max3584.net/(末尾 / あり)
${NAMESPACE}saas-abc12345
${APP_PORT}2368

使用例(Ghost のデプロイ):

url             = ${APP_URL}
NODE_ENV        = production
database__client = mysql
  • 置換対象: 環境変数の値 / イメージ名 / タグ / 永続ストレージのマウントパス
  • ${...} 内は UPPER_SNAKE_CASE のみ 認識。 bare $VAR${lowercase} はそのまま渡るので、 bcrypt ハッシュ ($2a$10$...) などは安全。
  • 未知のトークン(タイポ含む)はエラーにならず、リテラルとして残ります。

5.4 Kubernetes 内での参照

環境変数は Deployment の spec.template.spec.containers[].env に設定され、アプリプロセスから標準的に参照できます。

# Node.js の例
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_SECRET;

# Python の例
import os
db_url = os.getenv('DATABASE_URL')
api_key = os.getenv('API_SECRET')

# Bash の例
echo "API_SECRET=$API_SECRET"

5.5 シークレット管理のベストプラクティス

  • パスワード・トークンは環境変数で注入(ソースコードに埋め込まない)
  • 本番環境では強力なパスワードを使用
  • 定期的に credentials をローテーション

6. レプリカ / リソース設定

アプリの負荷分散やリソース制限の方法について説明します。

6.1 レプリカ数の役割

レプリカ数は、Kubernetes Deployment が維持する同一 Pod インスタンス数です。増やすことで負荷分散と可用性が向上します。

# 例: レプリカ3
Pod-1 (10.0.0.1:3000)
Pod-2 (10.0.0.2:3000)
Pod-3 (10.0.0.3:3000)
        ↓
   Service (cluster-ip:3000)
        ↓ (ラウンドロビン)
   Traefik → どれかに振り分け

6.2 CPU / メモリの制限

各アプリのリソースは requests (保証) limits (上限) の 2 軸で設定します。アプリ作成直後はデフォルトが 入っていて、/saas の アプリ詳細 → 設定タブのスライダーで上限内で動かせます。

  • 初期値 (requests): 100m CPU / 128Mi メモリ
  • 初期値 (limits): 500m CPU / 512Mi メモリ
  • 絶対上限 (MAX プラン): 8000m CPU / 16384Mi メモリ (8 cores / 16 GiB)

1 アプリあたりの CPU / メモリ上限はプラン別に決まり、サーバー側で保存時に 再クランプされます。スライダーは 50m 刻み (CPU) / 64Mi 刻み (Memory) で動き、requests ≤ limits を自動で維持します。

上限が足りない場合は、上位プランへのアップグレードや アドオンをご検討ください。

6.3 スケーリング戦略

  • 垂直スケーリング: 設定ページのスライダーで CPU / メモリ の requests / limits を変更し、再デプロイ
  • 水平スケーリング: レプリカ数を増やしてラウンドロビンで分散
  • 自動スケーリング: Metrics-based HPA (計画中、CPU/メモリ使用率に応じた自動増減)

7. アプリの管理

作成したアプリを管理・制御する方法です。

7.1 アプリ一覧表示

/saas ページでは、現在のテナント配下のすべてのアプリが表示されます。 各カードに以下の情報が含まれます。

  • アプリ名
  • ステータスバッジ:「準備中」「作成中」「稼働中」「更新中」「エラー」「削除中」
  • 公開ドメイン — クリックで新タブにアプリを開きます
  • イメージ名:タグ
  • ポート、レプリカ数
  • 連携 DB と使用ロール(連携している場合)
  • 作成日時

ドメイン以外の領域をクリックするとアプリ詳細ページに遷移します。

7.2 アプリ詳細 / 編集

カード本体をクリックすると /saas/apps/<appName> に遷移し、デプロイ済みアプリの設定を変更できます。

  • 設定: イメージ / タグ / ポート / レプリカ / 環境変数 / リソース上限を編集して「更新(再デプロイ)」
  • 連携 DB: リンク先データベース、ロール構成、 接続フォーマット、env 名プリセットを変更(詳細は 5.2 データベース連携 を参照)
  • 接続情報: hostname / namespace / 連携 DB / 作成・更新時刻の参照
  • 危険ゾーン: アプリと関連 K8s リソース(Deployment / Service / IngressRoute / PVC / DNS)の一括削除

デプロイが失敗した状態(ステータス「エラー」)の場合は赤いパネルの 「再デプロイ」ボタンで現在の設定のままもう一度試行できます。原因 (pull 失敗、Quota、TLS 等)を直してから押す形です。

7.3 起動 / 停止

レプリカ数を 0 に設定するとアプリが停止します。 またいつでも起動(レプリカ数を 1 以上に)できます。

  • 停止中のアプリにアクセスするとエラー (503 Service Unavailable など)
  • 停止中もドメイン・設定は保持されます
  • 再起動時に環境変数・イメージは再適用されます

7.4 再起動

アプリを再起動するには、詳細ページから「再起動」を選択するか、 または一度停止 (replicas=0) して再度起動 (replicas=1+) します。

  • 再起動中は数秒程度のダウンタイムが発生
  • 設定変更 (環境変数、ポート、イメージなど) を反映させたい場合は詳細ページから「更新」を選択

7.5 削除

アプリが不要になった場合、詳細ページから「削除」を選択します。 以下が自動的に削除されます。

  • Kubernetes Deployment, Pod, Service
  • IngressRoute
  • DNS レコード
  • SSL 証明書
  • PersistentVolumeClaim (永続ストレージが有効な場合)

8. ログとメトリクス

アプリのログとメトリクスを確認する方法です。

8.1 アプリケーションログ

詳細ページの「ログ」タブで、アプリの stdout/stderr を閲覧できます(計画中、現在は以下のコマンドで確認)。

kubectl logs -n saas-{tenantId} -l app={appName} --tail=100 -f

8.2 メトリクス

詳細ページの「メトリクス」タブで、CPU/メモリ使用率や リクエスト数などを時系列グラフで表示します(計画中)。

8.3 Kubernetes からの確認

詳しいトラブルシューティングには kubectl コマンドラインを使用できます。

# Pod 一覧
kubectl get pods -n saas-{tenantId}

# Pod の詳細
kubectl describe pod {podName} -n saas-{tenantId}

# Pod のログ(stdout/stderr)
kubectl logs {podName} -n saas-{tenantId}

# 前のインスタンスログ(クラッシュした場合)
kubectl logs {podName} -n saas-{tenantId} --previous

9. トラブルシューティング

ステータスが「作成中」で止まっている

イメージ pull、Pod 起動、DNS/SSL 設定に数分かかることがあります。以下を確認してください。

  • 数分待ってページをリロード
  • kubectl describe pod -n saas-{tenantId} でイベントを確認
  • 以下の「ImagePullBackOff」や「Crashloop」を参照
ImagePullBackOff エラー

イメージ pull に失敗しています。以下の原因を確認してください。

  • イメージパスが正しいか — 空白やタイポなし?
  • イメージが registry に存在するか — レジストリ画面で確認
  • イメージを registry に push したか — docker push で再確認
  • タグが指定されているか — image:tag 形式で指定
  • 内部レジストリ使用時、トークンが有効か — レジストリで新しいトークンを発行
CrashLoopBackOff — アプリがすぐに終了する

Pod が起動しても数秒で停止を繰り返しています。アプリのエラーログを確認してください。

  • kubectl logs {podName} -n saas-{tenantId} でエラーを確認
  • 環境変数が不足していないか — NODE_ENV, DATABASE_URL など
  • ポート番号は正しいか — フォームで指定したポートでリッスン中?
  • コンテナのリソース上限に達していないか —「リソース設定」を参照
ポート不一致エラー

ブラウザで接続できない、タイムアウトする場合、ポート設定を確認してください。

  • フォームで指定したポート(例: 3000)が、 コンテナのリッスンポートと一致しているか?
  • 例: Node.js app が process.env.PORT || 3000 で起動している
  • Dockerfile の EXPOSE 3000 は参考情報のため、 実際のリッスンポートを フォームで指定してください
HTTPS 証明書エラー / 「接続がプライベートではない」

初回デプロイ時や DNS 設定直後、Let's Encrypt のチャレンジが完了していない可能性があります。

  • 約 10 分待機後、再度アクセスしてください
  • ブラウザのキャッシュをクリアして F5 リロード
  • kubectl -n traefik-system logs -f でセキュリティ情報を確認
ドメインにアクセスできない / 404 Not Found

ドメインは正解していますが、アプリが応答していない可能性があります。

  • アプリのステータスが「稼働中」か確認 — /saas ページで確認
  • kubectl get pods -n saas-{tenantId} で Pod が Running 状態か
  • kubectl logs {podName} -n saas-{tenantId}でエラーログを確認
  • 404 が返される場合は、アプリ側のルーティング設定を確認
503 Service Unavailable

アプリが起動していないか、すべての Pod が停止している可能性があります。

  • レプリカ数を確認 — 0 になっていないか?
  • 再起動してみる — 詳細ページから「再起動」を選択
  • Pod のイベントを確認 — kubectl describe pod ... でエラーを見る
アプリ上限に達した

ライセンスで決められたアプリ数の上限に達しています。