Crowi

ストレージ設定

ローカル / S3 のファイルストレージ設定

Crowi にアップロードされた添付ファイルは、ストレージドライバ を通じて 保存されます。Crowi 2.0 では、どのドライバを使うかを runner プロジェクトの crowi.config.json (storage.driver) で選択し、ドライバの実体は対応する @crowi/plugin-storage-* プラグインが提供します。

Note: Crowi v1 にあった FILE_UPLOAD 環境変数は廃止されました。 ストレージの選択は crowi.config.jsonstorage.driver と、対応する @crowi/plugin-storage-* パッケージの組み合わせで行います。

指定できるドライバ名と提供プラグインの対応は crowi.config.json にあります。@crowi/plugin-storage-local暗黙のデフォルトプラグイン として常に読み込まれるため、crowi.config.json に何も書かなければ local ドライバが使われます。

ローカルストレージ (local)

ローカルストレージは、アップロードファイルをサーバのファイルシステムに 保存します。最小構成では設定は不要で、crowi.config.json を省略するか 次のように書くだけで有効になります。

{
  "storage": {
    "driver": "local"
  }
}

保存先ディレクトリのデフォルトは data/uploads です (crowi.config.json を置いたディレクトリからの相対パス)。

Tip: コンテナで運用する場合は、保存先ディレクトリを永続ボリュームに マウントしてください。コンテナを作り直すとローカルファイルは失われます。 S3 を使えばこの心配はなくなります。

S3 ストレージ (s3)

Amazon S3 (または S3 互換ストレージ) にファイルを保存するには、 @crowi/plugin-storage-aws-s3 プラグインを使います。

1. プラグインを有効化する

crowi.config.jsonplugins に S3 ストレージプラグインを追加し、 storage.drivers3 にします。

{
  "plugins": [
    "@crowi/plugin-storage-aws-s3"
  ],
  "storage": {
    "driver": "s3"
  }
}

@crowi/plugin-storage-aws-s3 は AWS の共通クレデンシャル基盤である @crowi/plugin-aws に依存しているため、依存プラグインは自動で 読み込まれます。プラグインの解決方法の詳細は プラグインの導入と設定 を参照してください。

2. バケットとクレデンシャルを設定する

S3 のバケット名と AWS のクレデンシャルは、管理画面から設定します。

  • バケット名@crowi/plugin-storage-aws-s3 自身の設定。管理画面の プラグイン設定 (/admin/plugins) から「AWS S3」の設定を開いて入力します。
  • リージョン / アクセスキー / シークレットキー@crowi/plugin-aws の設定。同じくプラグイン設定から入力します。

アクセスキー ID とシークレットアクセスキーは機密扱いとして登録されて おり、CROWI_ENCRYPTION_KEY が設定されていれば自動で暗号化されて 保存されます。詳細は 機密設定の暗号化 を参照してください。

Tip: EC2 / ECS のインスタンスロールや IRSA を使う場合は、アクセス キーを空のままにしておけば、AWS SDK のデフォルトのクレデンシャル解決 (環境変数やインスタンスメタデータ) が使われます。

3. 設定の即時反映

プラグインの設定を管理画面から保存すると、S3 ドライバは reconfigure により サーバ再起動なしで 新しいバケット / クレデンシャルを反映 します。

GCS ストレージ (gcs)

Google Cloud Storage にファイルを保存するには、@crowi/plugin-storage-gcs プラグインを使います。

Tip: バケット・サービスアカウント・IAM 権限を Google Cloud 側で用意する手順は GCS ストレージのセットアップ を参照してください。

1. プラグインを有効化する

crowi.config.jsonplugins に GCS ストレージプラグインを追加し、 storage.drivergcs にします。

{
  "plugins": [
    "@crowi/plugin-storage-gcs"
  ],
  "storage": {
    "driver": "gcs"
  }
}

2. 接続情報を設定する

管理画面 (/admin/plugins) の「Google Cloud Storage」から、次の 4 フィールドを設定します。

フィールド必須説明
GCS バケット必須既存の非公開バケット名。Crowi はバケットを作成しません。
オブジェクトプレフィックス任意すべての Crowi オブジェクトキーの先頭に付けるプレフィックス。先頭・末尾の / は自動で除去されます。
Google Cloud プロジェクト ID任意明示的なプロジェクト ID。空欄なら ADC 自身のプロジェクト、または inline key の project_id が使われます。
サービスアカウントキー JSON任意暗号化されて保存されます。空欄なら ADC (Application Default Credentials) を使います。

この 4 フィールドは 1 つの暗号化されたドキュメントとして アトミックに保存 されます。個別の行として保存されるわけではないため、 一部だけが保存されて残りが古いままになることはありません。すでに サービスアカウントキーが保存されている状態で他のフィールドだけを 更新して保存しても、キー欄を空欄のまま送信すれば既存のキーは 上書きされません(管理画面は変更していないシークレットを再送しません)。

サービスアカウントキーはシークレットフィールドです。管理画面が生の値を 読み戻すことはなく、キーが保存済みかどうかだけが表示されます。その内容が アプリケーションログに出力されることもありません。

サービスアカウントキー JSON を空欄のままにする と、 Application Default Credentials (ADC) が使われます —— GCE/GKE/Cloud Run にアタッチされたサービスアカウント、 Workload Identity Federation のクレデンシャル、またはローカルの gcloud auth application-default login です。Google Cloud 上で運用する 場合はこれが推奨構成です。プラットフォームが払い出す短命な クレデンシャルに任せ、長期有効なキーを Mongo に保存しないようにします。

不正なキー JSON (JSON として不正、typeservice_account でない、 project_id/client_email/private_key の欠落、PEM 形式でない private_key など) は保存前に HTTP 422 で拒否され、どのフィールドが 不正かが issues に列挙されます。

3. 必要な IAM 権限

ドライバが呼び出すのはオブジェクトの作成・取得・削除の 3 つだけで、バケット 操作もオブジェクトの一覧取得も行いません。付与する具体的なロールと、V4 署名付き URL (signBlob) が別権限になる点は GCS ストレージのセットアップ を参照してください。

4. プレフィックスは IAM 境界ではない

プレフィックスを設定すると、バケット内の同名の他データと Crowi の オブジェクトを命名規則として分離できますが、これは アクセス制御の 境界ではありません。バケットレベルのアクセス権を持つ主体は、 プレフィックス外のオブジェクトにも引き続きアクセスできます。

5. 設定変更の反映と full-stop の注意

GCS ドライバが アクティブでない間 (storage.drivergcs 以外) の接続保存は、他のプラグイン設定と同様、通常のホット リロード対応の保存操作です。

一方、GCS が すでにアクティブなドライバ になっている マルチインスタンス構成でバケット / プレフィックス / クレデンシャルを 変更する場合は話が別です。この機能はレプリカ間の調整機構 (Redis pub/sub、リース、CAS など) を一切追加していないため、各 API レプリカは自分自身のプロセス内状態を独立に反映します。オンラインで 変更すると、一部のレプリカが古い接続のまま読み書きを続ける区間が 生まれてしまいます。サポートされる手順は、全 API レプリカを停止 してから設定を変更し、再起動する full-stop 方式のみです。

6. bucket 削除・権限剥奪時の表示について (受容する制約)

設定後にバケットが削除された、権限が剥奪された、プレフィックスが 実在しない、といった状態になった場合、GCS が返す 404 は「オブジェクトが 存在しない」場合と区別できません。そのため、表示は placeholder (サムネイル表示)、厳密ダウンロードは FILE_MISSING、派生画像は original へのフォールバックという、通常の「オブジェクトが存在しない」 場合と同じ挙動になります —— 実際には設定ミスであっても、です。これは local/s3 ドライバも共通して持つ粗さであり、GCS だけを特別扱い しません。設定ミスの早期検出は、保存時にバケットへの往復確認を行う 別機能が担当します。

7. ファイルの移行 (local/S3 → GCS)

GCS への移行は、他ドライバ間の移行と同じ crowi-admin rebuild storage copy を使いますが、無停止のライブ移行は提供しません。 コピー中は書き込みも読み取り配信も止める、full-stop 方式のみです。

  1. GCS 接続を管理画面で保存する (storage.driver はまだ元のドライバの まま)。
  2. 全 API レプリカ、ストレージを操作するあらゆる CLI/バックグラウンド ワーカーを停止する。添付ファイルのアップロード・削除、プロフィール 画像アップロード、ページの完全削除、表示用派生ファイルの 生成・再生成のいずれも進行中でないことを確認する。この間は 読み取り配信も停止する。
  3. 停止した状態で crowi-admin rebuild storage copy --from <old> --to gcs を実行し、failed: 0 を確認する。さらに読み取り検証として、 使い捨ての一時的な移行先へ逆方向コピー (--from gcs --to <scratch>) を行い、全件が読み出せることを 確認するのが最も手軽な網羅的チェックです。<scratch> には 移行元データを指している既存ドライバをそのまま流用しないこと (ロールバック用のコピーを上書きしてしまいます) —— 空の一時 ディレクトリを指す新しい local ドライバなど、使い捨て専用の 移行先を用意してください。
  4. crowi.config.jsonstorage.drivergcs に変更し、全 レプリカを再起動する。
  5. サンプルのアップロード・削除・プロフィール画像・添付ファイル配信・ 厳密ダウンロードを確認してからバックグラウンドワーカーを再開する。 ロールバックが必要な場合は、同じ full-stop 手順を逆順で行う (旧 ストレージはロールバック用に一定期間保持しておく)。
  6. crowi-admin rebuild attachment-display-derivatives --force を実行し、表示用派生ファイルを GCS 上のオリジナルから再生成する (下の「crowi-admin rebuild attachment-display-derivatives」の Note を参照)。

ストレージの現在の状態を確認する

管理画面のストレージ画面 (/admin/storage) を開くと、登録されている ドライバが一覧できます。ドライバごとに、ドライバ名 (local / s3 など)、 それを提供しているプラグイン、crowi.config.json で選択中のドライバかどうかが 表示されます。

ドライバの切り替えとファイル移行

ドライバを切り替える (例: locals3) と、crowi.config.json を 書き換えた時点で 新規アップロードは新しいドライバに保存 されます。 しかし、既存のファイルは自動では移動しません。古いドライバに残った ファイルは、crowi-admin CLI で新しいドライバへコピーします。

crowi-admin rebuild storage copy

crowi-admin はサーバ内 (ssh / kubectl exec) での実行を想定した運用者用 CLI です。rebuild storage copy サブコマンドで、あるドライバから別のドライバへ 全オブジェクトをコピーできます。

# local から s3 へ全ファイルをコピー
crowi-admin rebuild storage copy --from local --to s3

# まず対象キーだけを確認する (コピーは行わない)
crowi-admin rebuild storage copy --from local --to s3 --dry-run

ポイント:

  • コピー元・コピー先の 両方のドライバが、いずれかのプラグインで 読み込まれている 必要があります。crowi.config.jsonstorage.driver が指す「アクティブなドライバ」とは無関係なので、s3 をアクティブに する前に local から s3 へデータを先にコピーしておけます。
  • --dry-run を付けると、コピー対象のキーを列挙するだけで実際の書き込みは しません。件数の事前確認に使えます。
  • 添付ファイルの行は MongoDB のカーソルで逐次処理されるため、ファイル数が 非常に多くてもメモリ使用量は一定です。
  • 1 件単位のエラーはイテレーションを止めず、failed カウントに加算 されます。再実行は安全です (local / S3 ドライバはキー単位で上書きします)。

終了コードは crowi-admin にまとめてあります。

推奨される移行手順

local から s3 へ移行する場合の安全な手順:

  1. @crowi/plugin-storage-aws-s3crowi.config.json:plugins に追加し、 管理画面で S3 バケット / クレデンシャルを設定する (storage.driver は まだ local のまま)。
  2. crowi-admin rebuild storage copy --from local --to s3 --dry-run で対象件数を 確認する。
  3. crowi-admin rebuild storage copy --from local --to s3 で実際にコピーする。 failed が出た場合は再実行する。
  4. crowi.config.jsonstorage.drivers3 に変更し、API を 再起動する。
  5. 新しいアップロードとダウンロードが S3 経由で動くことを確認する。

Tip: 手順 1 で先に S3 へデータをステージングしておけば、ドライバを 切り替えた瞬間からダウンタイムなしで S3 が使えます。

crowi-admin rebuild attachment-display-derivatives

添付ファイル の画像は、表示用に最適化された 派生ファイルをアップロード時にベストエフォートで生成します。 rebuild attachment-display-derivatives サブコマンドは、この派生ファイルを 既存の添付ファイルに対して事後生成・修復・回収するための運用コマンドです。

# 未生成/失敗のみを対象に、通常どおり生成する
crowi-admin rebuild attachment-display-derivatives

# まず対象件数だけを確認する (書き込みなし)
crowi-admin rebuild attachment-display-derivatives --dry-run

# 現行レシピの記述済みレコードも含め、全件を再生成する (ストレージ移行後は必須)
crowi-admin rebuild attachment-display-derivatives --force

# 「記述はあるが実体が無い」ものだけを狙って修復する
crowi-admin rebuild attachment-display-derivatives --repair-missing

# 無参照になった派生オブジェクトを報告する (local ドライバのみ、削除はしない)
crowi-admin rebuild attachment-display-derivatives --gc

# 報告された無参照オブジェクトを実際に削除する
crowi-admin rebuild attachment-display-derivatives --gc --confirm

オプションの一覧は crowi-admin にあります。

ポイント:

  • 通常実行 (--force/--repair-missing なし) は idempotent です。 現行 recipe の resized/passthrough/unsupported はスキップされ、 failed・未生成・古い recipe のレコードだけが再処理されます。 メタデータ上は resized だが実体がストレージから失われている場合、 通常実行では検出されずスキップされ続けます —— この修復には --force または --repair-missing が必要です。
  • Attachment の行は MongoDB のカーソルで単一の producer だけが pull し、取り出したアイテムの処理だけが --concurrency で有界並列化されます (storage copy と同じくメモリ使用量は一定に保たれます)。
  • 各ワーカーは original をローカル一時ディレクトリへステージしてから 生成処理に渡し、処理後に自分がステージした一時ファイルを削除します。 同時にステージされるデータ量の目安は --concurrency × 100MB (footer 添付追加の実際の上限に基づく) です。--concurrency を既定より 上げる場合は、このディスク/メモリ使用量の見積もりを掛け直してください。 ステージ前に空き容量を確認し、不足時はそのアイテムを安全に skip・ failed として記録します。
  • SIGINT (Ctrl-C) を受け取ると、新規アイテムの受付を止め、進行中の 一時ファイルを片付けてから直近の進捗サマリを出力し、非ゼロで終了 します。
  • 項目単位の失敗があっても実行は継続し、Attachment ID と分類済みの 理由を出力します。

終了コードは crowi-admin にまとめてあります (中断したときは非ゼロで終了します)。

Note: ストレージドライバを移行した直後は --force の実行が 必須です。 storage copy は original のみをコピーし、派生ファイルは コピーしません(派生ファイルは durable data ではなく disposable な キャッシュのため)。移行後にそのまま rebuild attachment-display-derivatives を実行しても、全ての resized レコードは(新ドライバ上に実体が無くても)「完了済み」として通常実行 ではスキップされ、何も再生成されません。必ず --force(全件 再評価)、または実体が失われた記述子だけを狙いたい場合は --repair-missing を実行してください。--force 実行までの間、 canonical URL は original へ安全にフォールバックし続けます(データ 損失ではありません)。

Note: S3 での派生オブジェクトの孤児残存リスク。ローカル ドライバの atomic write 化と、削除処理の原子的スナップショット取得 + 決定的キーの sweep により、通常運用ではほぼすべての孤児発生源が閉じて います。ただし、運用規約(rebuild/--gc は同時に 1 プロセスのみ)に 反して複数のプロセスが同じ添付を並行処理した場合など、ごく稀に 無参照オブジェクトが残ることがあります。--gc は v1 では local ドライバのみに対応しており、S3 では「未対応」を報告して正常終了 します。長期的な蓄積が気になる場合は、S3 の derivatives/ プレフィックス 配下に対して十分保守的な(例えば 1 年程度の)有効期限のライフサイクル ルールを 最後の砦としてのみ 設定することを推奨します —— ライフ サイクルルールは「参照されているか」を判定できず経過時間でしか判断 できないため、短い有効期限を設定すると生きた派生ファイルまで削除して しまい、original フォールバックへ静かに劣化します。--gc の代替には なりません。

エディタからのアップロード制限

ページ編集画面では、画像の 貼り付け とファイルの ドラッグ&ドロップ でエディタへ直接アップロードできます (利用者向けの説明は 添付ファイル 参照)。運用者が知っておく制限は 3 つです。

  • レート制限: エディタからのアップロードは 1 ユーザーあたり 1 分間に 20 回 までです。上限に達するとサーバは 429 を返し、エディタには 「アップロード上限に達しました。N 秒後に再試行してください」と表示されます。
  • サイズ上限: 1 ファイルあたり既定 50 MB です。CROWI_UPLOAD_MAX_BYTES で下げられます (設定)。この値はメモリ予算でもあるため、 上げることはできません。1 回のドラッグ&ドロップで扱えるのは 5 ファイルまで です。
  • ファイル種別: 添付できる種別は固定の許可リストで決まっており、経路 (添付ボタン / 貼り付け / ドラッグ&ドロップ) によらず同じです。一覧は 添付ファイル にあります。インスタンスごとに変更する 設定はありません。

次のステップ

On this page