Crowi

Redis の運用

対応バージョン・接続数の見積もり・ACL・エンドポイントの入れ替え

Crowi は Redis をレプリカ間の連携に使います。単一レプリカでは任意、複数レプリカでは必須です。どの機能がどのキーで Redis を使うかは Redis のキーと ACL、無いときにどう縮退するかは 環境変数 にまとめてあります。

Crowi は Redis サーバを同梱しません。運用者が用意したインスタンスにネットワーク越しで接続します。マネージドサービス (ElastiCache・MemoryDB・Upstash など) を選ぶ場合、その選定と利用条件の遵守は運用組織の責任です。

対応バージョン

  • 動作確認しているバージョン範囲は >=8.0 <9 です。これは方針であって、ランタイムで強制されるわけではありません (Crowi は Redis 7 系への接続を拒否しません)。
  • バージョンを固定 (pin) してください。 redis:8redis:latest のような moving tag は、意図しないタイミングでバージョンをまたぎます。Crowi の compose と CI は 1 つのパッチタグを digest つきで固定しています。
  • Valkey は正式サポートではありません。 プロトコル互換なので動く可能性は高いものの、Crowi の互換性テストは CI で Valkey に対して実行していないため best-effort 扱いです。同じテスト一式が Valkey でも継続的に通るようになった時点で正式サポートへ切り替えます。
  • RDB のクロスエンジン可搬性は保証しません。 Redis が書き出したスナップショットを Valkey が読める (またはその逆) という前提を置かないでください。

接続数の見積もり

api インスタンスあたりの接続数は、そのインスタンスの状態で変わります。

  • 起動直後 (共同編集セッションがまだ 1 つも始まっていない): 5 本 — 共有クライアント 1 本 (編集者上限カウンタ・最近見たページ・レート制限・プレゼンスの書き込み・通知の publish が相乗り) + プレゼンス subscriber 1 本 + 通知 subscriber 1 本 + 設定同期の publisher/subscriber 2 本。
  • 最初の共同編集セッションが始まったあと: 7 本 — 上記に加えて共同編集専用の pub / sub 接続が 2 本。最初のドキュメントを開くまでは接続されません。

例として api 10 レプリカなら、起動直後で 50 接続、全レプリカで共同編集が始まったあとで 70 接続です。web からの Redis 接続はありません (すべて api 経由)。既定の maxclients=10000 には十分収まりますが、小さいマネージドインスタンス (cache.t3.micro など) では余裕を確認してください。

エンドポイントを入れ替える

レプリカごとに違う Redis を向いたまま同時に動かさないでください (split-brain)。 Redis の pub/sub は分散排他制御を提供しないため、この構成は派手に失敗せず、レプリカ間の伝搬 (共同編集・プレゼンス・通知・設定同期) が 2 つの断絶したグループへ静かに分裂するだけです。

標準手順は、全レプリカを揃えて止め、REDIS_URL を新しいエンドポイントに切り替えてから揃えて起動する、というものです (手順の全体は バージョンアップ)。ベンダーのオンライン昇格機能 (マネージドなレプリカ昇格や blue-green 切替) を使うのは、そのディストリビューションが実際にその方法でのトポロジ変更をサポートすると確認できた場合だけにしてください。Crowi 側では検証していません。

切り替え前に、古いインスタンスに残ったコーディネーション state をクリアしてください。 編集者上限カウンタのキーやプレゼンスのハッシュが残ったまま全レプリカを再接続すると、古いソケット ID が新しい接続に混ざり、編集者上限が実際より高く数えられて編集者が意図せず読み取り専用になったり、プレゼンスに幽霊の閲覧者が出たりします。

  1. 全レプリカが旧エンドポイントから drain 済み (既存の WebSocket 接続が閉じ、新規接続が旧エンドポイントを向いていない) であることを確認する。
  2. 切り替え先を、新規に立てた空のインスタンスにするか、crowi:<インスタンス slug>: prefix のキーを消しておく。
  3. 切り替え後、編集者数とプレゼンスの表示が実際の接続数と一致することを確認してから運用トラフィックを戻す。

ページ本文は Redis に置かれないため、Redis のコールドスタートでページが失われることはありません。失われるのは最近見たページの一覧と、確認待ちのアカウント連携コードだけです (どちらも利用者がやり直せます)。

セキュリティ

  • 通信と認証情報: TLS (rediss://) と証明書検証の既定を維持してください。検証の無効化は、明示的に制御された開発環境や private CA の場合に限ります。認証情報は環境変数かシークレットマネージャに置き、設定画面やログには書かないでください。
  • ネットワーク: Redis は Crowi と内部インフラからのみ到達できるようにし、認証なしで公開ネットワークへ晒さないでください。
  • キーの分離: すべてのキーとチャンネルは、インスタンス単位の名前空間 crowi:<slug>:… の下に作られます (既定は CLIENT_URL のホスト名、REDIS_KEY_PREFIX で上書き可能 — 環境変数)。同じインスタンスの全レプリカは同じ値に解決される必要があり、同じ Redis を共有する別インスタンスは異なる値にしてください。URL のデータベース番号 (/1 など) は代わりになりません — pub/sub は DB にスコープされないため、DB 番号だけが違う 2 インスタンスは全チャンネルで cross-talk します。
  • 障害時の挙動: 編集者上限とレート制限は認可の境界ではなく、Redis に到達できないときは fail-open (通す側) に倒れます。JWT や WebSocket トークンの認証は Redis に依存しないため、Redis 障害が認証をバイパスすることはありません。

ACL を絞る場合に必要な権限

制限付きの ACL ユーザーを作る場合、Crowi が使うコマンドをすべて許可しないと、「未対応機能」としてきれいに失敗するのではなく、共同編集の保存などが permission エラーで失敗し始めます。キーとチャンネルの一覧と必要なコマンドは Redis のキーと ACL にあります。

  • 権限は実際に解決された <slug> を対象に付与します。
  • 保存ロックはスクリプト内部のコマンドまで許可が必要です。 Redis の ACL は Lua スクリプトから呼ばれるコマンドにも権限を要求するため、EVAL だけを許可した ACL は共同編集の保存を毎回失敗させます。
  • ACL は ユーザー にスコープされ、listener には紐づきません。「TLS ポートだけ制限する」という設計は成立しないため、全 listener で default ユーザーを無効化またはパスワード保護し、Crowi には常にスコープ済みの認証情報を使わせてください。

一覧はソースを精査して書き起こしたもので、制限付き ACL ユーザーに対する自動テストで検証されたものではありません。許可漏れは上記の permission エラーとして現れます。

関連ページ

On this page