ストレージ設定
ローカル / S3 のファイルストレージ設定
Crowi にアップロードされた添付ファイルは、ストレージドライバ を通じて
保存されます。Crowi 2.0 では、どのドライバを使うかを runner プロジェクトの
crowi.config.json (storage.driver) で選択し、ドライバの実体は対応する
@crowi/plugin-storage-* プラグインが提供します。
Note: Crowi v1 にあった
FILE_UPLOAD環境変数は廃止されました。 ストレージの選択はcrowi.config.jsonのstorage.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.json の plugins に S3 ストレージプラグインを追加し、
storage.driver を s3 にします。
{
"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.json の plugins に GCS ストレージプラグインを追加し、
storage.driver を gcs にします。
{
"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 として不正、type が service_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.driver が gcs
以外) の接続保存は、他のプラグイン設定と同様、通常のホット
リロード対応の保存操作です。
一方、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 方式のみです。
- GCS 接続を管理画面で保存する (
storage.driverはまだ元のドライバの まま)。 - 全 API レプリカ、ストレージを操作するあらゆる CLI/バックグラウンド ワーカーを停止する。添付ファイルのアップロード・削除、プロフィール 画像アップロード、ページの完全削除、表示用派生ファイルの 生成・再生成のいずれも進行中でないことを確認する。この間は 読み取り配信も停止する。
- 停止した状態で
crowi-admin rebuild storage copy --from <old> --to gcsを実行し、failed: 0を確認する。さらに読み取り検証として、 使い捨ての一時的な移行先へ逆方向コピー (--from gcs --to <scratch>) を行い、全件が読み出せることを 確認するのが最も手軽な網羅的チェックです。<scratch>には 移行元データを指している既存ドライバをそのまま流用しないこと (ロールバック用のコピーを上書きしてしまいます) —— 空の一時 ディレクトリを指す新しいlocalドライバなど、使い捨て専用の 移行先を用意してください。 crowi.config.jsonのstorage.driverをgcsに変更し、全 レプリカを再起動する。- サンプルのアップロード・削除・プロフィール画像・添付ファイル配信・ 厳密ダウンロードを確認してからバックグラウンドワーカーを再開する。 ロールバックが必要な場合は、同じ full-stop 手順を逆順で行う (旧 ストレージはロールバック用に一定期間保持しておく)。
crowi-admin rebuild attachment-display-derivatives --forceを実行し、表示用派生ファイルを GCS 上のオリジナルから再生成する (下の「crowi-admin rebuild attachment-display-derivatives」の Note を参照)。
ストレージの現在の状態を確認する
管理画面のストレージ画面 (/admin/storage) を開くと、登録されている
ドライバが一覧できます。ドライバごとに、ドライバ名 (local / s3 など)、
それを提供しているプラグイン、crowi.config.json で選択中のドライバかどうかが
表示されます。
ドライバの切り替えとファイル移行
ドライバを切り替える (例: local → s3) と、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.jsonのstorage.driverが指す「アクティブなドライバ」とは無関係なので、s3をアクティブに する前にlocalからs3へデータを先にコピーしておけます。 --dry-runを付けると、コピー対象のキーを列挙するだけで実際の書き込みは しません。件数の事前確認に使えます。- 添付ファイルの行は MongoDB のカーソルで逐次処理されるため、ファイル数が 非常に多くてもメモリ使用量は一定です。
- 1 件単位のエラーはイテレーションを止めず、
failedカウントに加算 されます。再実行は安全です (local / S3 ドライバはキー単位で上書きします)。
終了コードは crowi-admin にまとめてあります。
推奨される移行手順
local から s3 へ移行する場合の安全な手順:
@crowi/plugin-storage-aws-s3をcrowi.config.json:pluginsに追加し、 管理画面で S3 バケット / クレデンシャルを設定する (storage.driverは まだlocalのまま)。crowi-admin rebuild storage copy --from local --to s3 --dry-runで対象件数を 確認する。crowi-admin rebuild storage copy --from local --to s3で実際にコピーする。failedが出た場合は再実行する。crowi.config.jsonのstorage.driverをs3に変更し、API を 再起動する。- 新しいアップロードとダウンロードが 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 ファイルまで です。 - ファイル種別: 添付できる種別は固定の許可リストで決まっており、経路 (添付ボタン / 貼り付け / ドラッグ&ドロップ) によらず同じです。一覧は 添付ファイル にあります。インスタンスごとに変更する 設定はありません。
次のステップ
- 設定 —
crowi.config.jsonの全体像 - 機密設定の暗号化 — S3 クレデンシャルの暗号化
- プラグインの導入と設定 — プラグインの追加・有効化
- バックアップ — データのバックアップ