/* terashim.com */

システム開発・データエンジニアリング・データ分析についての個人的なノート

Wiki.js を Cloud Run + Neon のサーバレス構成でデプロイする

概要

Wiki.js とは

Wiki として使えるツールとしては NotionConfluence が有名です。 どちらも便利なツールなのですが、ユーザー数ごとに課金される料金体系になっているので多人数で使おうと思うと費用がかさんできます。

Wiki.js は OSS の Wiki であり、好きな環境にデプロイして自分で管理することができます。必要なのはインフラ料金のみなので、ユーザー数が多くてもインフラ負荷が低ければ費用を安く済ませることができます。

本記事はシステム環境構築の紹介を主眼とし、Wiki.js の機能や活用方法については詳しく扱いません。 Wiki.js についてさらに詳しくは

などのページが参考になります。

システム構成

Wiki.js はビルド済みの公式 Docker イメージが公開されているので、コンテナ実行環境とデータベースを用意すればデプロイは簡単です。今回はとにかく安く動かすことを目指して、コンテナ実行環境には GCP の Cloud Run を、データベースには Neon を利用してデプロイしてみました。

デプロイしたサイトは Cloud Run のドメイン マッピング 機能を利用し、独自ドメイン wiki.terashim.com で公開しました。

Wiki.jsの画面

Wiki.jsの画面

構成図

構成図

Cloud Run とは

Cloud Run は コンテナ化されたアプリケーションをウェブサービスとしてデプロイできる実行基盤です。

HTTPSエンドポイントの自動発行や、リクエストに応じたオートスケーリングなどの機能があります。

中でも ゼロスケール (Scale to zero) が可能という特徴があり、サービスへのリクエストが途絶えたときにはコンテナを完全に停止し、実行料金が掛からないようにすることができます。これは個人利用や検証用などの小規模なユースケースでは特に嬉しい機能です。

Neon とは

Neon はフルマネージドなサーバレスの Postgres データベースサービスです。

インスタンスの管理が不要で、利用した計算資源(CPUとメモリ)とストレージの量に応じた従量課金の料金体系となっています。

Neon もまたゼロスケールが可能です。 自動サスペンド機能 によって、しばらくDB呼び出しがない場合(デフォルトでは5分間)には自動的に計算資源を停止してアイドル状態にすることができます。アイドル状態の間はコンピューティング料金が発生しません。

Neon の料金体系について正確には Billing metrics - Neon Docs のドキュメントをご参照ください。

構築手順

Neon でデータベースを作成する

Neon にユーザー登録し、適当なプロジェクト名(例: wikijs)で新規プロジェクトを、適当なデータベース名(例: wikijs)で新規データベースを作成します。 計算資源のサイズ (Compute size) は最小の 0.25 CU (0.25 vCPU, 1GB メモリ)とし、5分間アクセスがなければアイドル状態になるよう設定します。

データベースにテーブルは作らず、空のままにしておきます。

データベースを作成すると、自動的にホスト名が割り当てられます。 ホスト名、ユーザー名、パスワードなどの接続情報はダッシュボードの "Connection String" で確認することができます。

図: Neonダッシュボード画面

図: Neonダッシュボード画面

Neon の設定について詳しくは公式ドキュメントのページをご確認ください:

GCPプロジェクトを作成する

Cloud Run を利用するため、まず GCPプロジェクトを作成 します。 ここでは仮にプロジェクトIDを your-wikijs-project とします。

Artifact Registry に Docker イメージを保存する

Wiki.js のコンテナを Cloud Run で動かすため、Docker イメージを Artifact Registry に置く必要があります。

そのため、まずは Artifact Registry に Docker イメージを置くための リポジトリを新規作成します

リポジトリの設定は以下のようにします:

  • リポジトリ名: docker
  • 形式: Docker
  • モード: 標準
  • ロケーションタイプ: リージョン
  • リージョン: asia-northeast1 (東京)
Artifact Registry のコンソール画面

Artifact Registry のコンソール画面

gcloud CLI を使ってリポジトリを作成したい場合は、次のようなコマンドを実行します:

PROJECT_ID=your-wikijs-project
ARTIFACT_REPOSITORY_NAME=docker
ARTIFACT_REPOSITORY_LOCATION=asia-northeast1

gcloud artifacts repositories create \
  $ARTIFACT_REPOSITORY_NAME \
  --location=$ARTIFACT_REPOSITORY_LOCATION \
  --repository-format=docker \
  --mode=standard-repository \
  --project=$PROJECT_ID

次に、作成したリポジトリへ Wiki.js の Docker イメージをプッシュします。 これには以下のようなコマンドを実行します:

IMAGE_TAG=$ARTIFACT_REPOSITORY_LOCATION-docker.pkg.dev/$PROJECT_ID/$ARTIFACT_REPOSITORY_NAME/wiki:latest

docker pull ghcr.io/requarks/wiki:2
docker tag ghcr.io/requarks/wiki:2 $IMAGE_TAG
docker push $IMAGE_TAG

このコマンドでは

  • Wiki.js 公式の Docker イメージ ghcr.io/requarks/wiki:2 をローカルに pull する
  • pull したイメージに asia-northeast1-docker.pkg.dev/プロジェクトID/docker/wiki:latest のタグを付ける
  • タグ付けした URL へとイメージを push する

という手順で Artifact Registry にイメージを保存しています。

なおここで 認証ヘルパー等の設定 は既に済んでいるものとしています。

Secret Manager でパスワードを管理する

データベース接続用のパスワードは Secret Manager で管理します。

そのため 新規シークレットを作成 し、パスワードを保存します。 ここではシークレット名を db-pass とします。

Secret Manager のコンソール画面

Secret Manager のコンソール画面

シークレットの作成を gcloud で行う場合は、次のようなコマンドを実行します:

DB_PASS_SECRET_NAME="db-pass"

printf "DBパスワードの値" | \
  gcloud secrets create $DB_PASS_SECRET_NAME \
  --data-file=- \
  --project=$PROJECT_ID

次に、 Cloud Run の実行用サービスアカウントにシークレットへのアクセスを許可します。

Cloud Run のデフォルト設定では、サービスは Compute Cloud のデフォルトサービスアカウント プロジェクト番号[email protected] で実行されます。 このサービスアカウントにシークレットへの "Secret Manager のシークレット アクセサー" ロールを割り当てます

シークレットの権限設定

シークレットの権限設定

ロールの割り当てを gcloud で行うには、以下のような形のコマンドになります:

SERVICE_ACCOUNT_EMAIL=プロジェクト番号[email protected]

gcloud secrets add-iam-policy-binding $DB_PASS_SECRET_NAME \
  --project=$PROJECT_ID \
  --member=serviceAccount:$SERVICE_ACCOUNT_EMAIL \
  --role=roles/secretmanager.secretAccessor

Cloud Run でサービスをデプロイする

次に、以下のような設定で Cloud Run サービスをデプロイします:

  • サービス名: wikijs
  • リージョン: asia-northeast1
  • コンテナイメージ: 上で Artifact Registry にプッシュしたイメージ
  • コンテナポート: 3000
  • 環境変数:
    • DB_TYPE: postgres
    • DB_HOST: DBホスト名
    • DB_PORT: 5432
    • DB_USER: DBユーザー名
    • DB_NAME: データベース名
    • DB_SSL: true
  • "環境変数として公開されるシークレット":
    • 変数名: DB_PASS:
    • シークレット名: db-pass
    • バージョン: latest
  • メモリ: 512 MiB
  • CPU: 1
  • 実行環境: 第2世代
  • CPU の割り当てと料金: リクエストの処理中にのみ CPU を割り当てる
  • インスタンスの最小数: 0
  • インスタンスの最大数: 1
  • Ingressの制御: すべて
コンテナの設定

コンテナの設定

未認証の呼び出しを許可する設定

未認証の呼び出しを許可する設定

インターネットからのすべてのトラフィックを許可する設定

インターネットからのすべてのトラフィックを許可する設定

サービスのデプロイを gcloud で行いたい場合は、次のようなコマンドを実行します:

# サービス名とリージョン
SERVICE_NAME=wikijs
SERVICE_REGION=asia-northeast1

# データベース接続情報
DB_HOST=xx-xxxx-xxxx-12345678.ap-southeast-1.aws.neon.tech
DB_PORT=5432
DB_USER=cloudrun
DB_NAME=wikijs
DB_PASS_SECRET_NAME=db-pass

gcloud run deploy $SERVICE_NAME \
  --project=$PROJECT_ID \
  --region=$SERVICE_REGION \
  --set-env-vars=DB_TYPE=postgres,DB_HOST=$DB_HOST,DB_PORT=$DB_PORT,DB_USER=$DB_USER,DB_NAME=$DB_NAME,DB_SSL=true \
  --set-secrets=DB_PASS=$DB_PASS_SECRET_NAME:latest \
  --port=3000 \
  --allow-unauthenticated \
  --ingress=all \
  --execution-environment=gen2 \
  --cpu=1000m \
  --memory=512Mi \
  --timeout=120 \
  --concurrency=30 \
  --max-instances=1 \
  --min-instances=0

ドメインマッピングの設定

最後に、Cloud Run のドメイン マッピングを使用してカスタムドメインを設定します。

参考: カスタム ドメインのマッピング | Cloud Run のドキュメント | Google Cloud

本記事の公開時点では、Cloud Run のドメイン マッピングはまだプレビュー版となっています。 レイテンシー(応答時間)が非常に大きくなる場合があるため、本番環境での利用は非推奨とされています。

所有しているドメイン(仮に yourdomain.com とします)のサブドメイン(仮に wiki.yourdomain.com とします)で Wiki を公開したいとします。

そのためには、まず所有ドメイン yourdomain.com に対して ドメイン所有権の確認 を行う必要があります。 ドメイン所有権の確認方法については Search Console のドキュメント

サイトの所有権を確認する - Search Console ヘルプ

をご覧ください。

ドメイン所有権の確認が済んだら、 サブドメイン (wiki.yourdomain.com) からサービス wikijs への Cloud Run のドメイン マッピング を作成します。

ドメインマッピングの画面

ドメインマッピングの画面

Cloud Run のドメイン マッピングの作成を gcloud で行いたい場合は、次のようなコマンドを実行します:

DOMAIN=wiki.yourdomain.com
gcloud beta run domain-mappings create --domain=$DOMAIN --project=$PROJECT_ID --region=$SERVICE_REGION

ドメインマッピングの作成が完了すると、必要なDNSレコードの設定内容が表示されます。 例えば

NAME  RECORD TYPE  CONTENTS
wiki  CNAME        ghs.googlehosted.com.

のような内容が表示されたら、サブドメイン wiki.yourdomain.comCNAME レコードを ghs.googlehosted.com という値で作成します。

DNSレコードを設定してからしばらく時間が経つと自動でSSL/TLS証明書が作成され、ドメインマッピングがアクティブ状態になります。

Wiki.jsの初期設定

以上でサービスのデプロイは完了です。

ブラウザでサイト (https://wiki.yourdomain.com) へアクセスすると Wiki.js の初期設定画面が表示されます。 ここで管理者ユーザーのログインIDとパスワードを設定します。また "Site URL" の項目には https://wiki.yourdomain.com を入力します。

Wiki.js の初期設定画面

Wiki.js の初期設定画面

必要事項を入力して "INSTALL" ボタンをクリックすると、初期化処理が行われます。このときデータベースにテーブルが作成されます。

注意

コールドスタート問題

上述の通り、このシステム構成では料金を抑制するためゼロスケールの性質を利用しています。 そのため、サイトへのアクセスがしばらく途絶えると Cloud Run の起動インスタンス数はゼロに、Neon のデータベースはアイドル状態になります。

この状態でサイトに接続すると コールドスタート 状態となり、応答に時間が掛かります。 そのまましばらく待つと下のようなエラー画面が表示されます。このような場合、1分間ほど待ってページをリロードすると正常に表示されるようです。

Wiki.js のエラー画面

Wiki.js のエラー画面

このようなコールドスタート問題を避けるには Cloud Run の最小インスタンス数を1以上にしたり、Neon の自動サスペンド機能を無効にしたりする対策が考えられます。

しかし今回はとにかく料金を安くするためこの問題は無視することにしました。

まとめと感想

Cloud Run と Neon を使って Wiki.js をデプロイする方法について説明しました。 Cloud Run も Neon も従量課金でゼロスケールの機能を持ち、料金の節約に役立ちます。 コールドスタート問題のため不安定な動作にはなりますが、制約を理解した上で利用すれば一つの選択肢になるのではないかと思います。