Marginalia

Firebase HostingのDeploy Targetsを使ってproduction/staging環境を分ける

これまで Firebase Hosting を使ったプロジェクトで production/staging の環境を分けたいときには Firebase プロジェクトごと分離していた。プロジェクトごとクローンしてしまえばいいので考えることはシンプルではあるけども、管理する Firebase プロジェクトが増えるのはうれしくないし、課金設定の把握も数が増えると一苦労になる。

Firestore のインスタンスを分けなくていいという条件付きなら Firebase プロジェクトで分けなくても Firebase Hosting の Multiple Sites 機能を使えばいいんじゃないかと思い至り、やってみたら案外簡単だったのでメモ。

動いているもの

趣味で作っている Web アプリ

pokepartymatch | ポケモン構築支援ツール

これの staging 環境を作った。

https://pokepartymatch-dev.web.app/

リポジトリはこちら

https://github.com/lacolaco/pokepartymatch

GitHub Actions を使い、master ブランチへの push で staging 環境で自動デプロイし、 バージョンタグを打った commit を push したときに production 環境へ自動デプロイするようにした。

手順

前提として、まず単一の Firebase Hosting でデプロイできる状態になっていることとする。

1. Firebase コンソールで staging 用の Site を追加する。

Web コンソール上でポチっと押せばすぐできる。リンクの場所ははじめて Site 追加するときば場所が違うと思うが、探せば見つかる。

サブドメインの入力を求められるがこれがそのまま Site の ID?のような識別子になる。今回はたまたま -dev が空いていたので使ったが何でも良い。

2. プロジェクトの .firebaserc にターゲットを追加する

Firebase プロジェクト上に作った Site を、Firebase CLI が備える Deploy Targets という設定と紐付ける。

Deploy targets | Firebase

直接編集してもよいが、CLI でやれば実際にその ID の Site が存在するかをサーバーに問い合わせてくれるので Typo に気づけて安心。 TARGET_NAME は任意の名前で、 RESOURCE_NAME はさきほど作成した Site のサブドメイン部分。

> firebase target:apply hosting staging pokepartymatch-dev

完成形はこちら。production の設定も追加している。

https://github.com/lacolaco/pokepartymatch/blob/master/.firebaserc

3. firebase.json で Hosting の Site と Deploy Targets を紐付ける

firebase.json ファイルの hosting 設定で、各ターゲットごとの Hosting の設定をする。設定を分ける必要がなかったとしても target フィールドで探されるのでそれぞれ対応する設定が存在しないとエラーになる。

https://github.com/lacolaco/pokepartymatch/blob/master/firebase.json#L2

4. デプロイ時にターゲットを指定する

あとは firebase deploy コマンドを実行するときに production か staging かを指定するだけ。production 時のみ Firestore などほかのコンポーネントも同時デプロイするようにしている。

"deploy:staging": "firebase deploy --only hosting:staging",
"deploy:production": "firebase deploy --only hosting:production,firestore",

振り返り

  • ある程度割り切れば Multiple Sites での production/staging は運用が楽そう
  • Storage は Bucket を分ければいいし、Functions や Firestore は完全には分離できないが URL 設計や Collection 設計次第でどうにかできそう。特に Firestore は情報がセンシティブならやめたほうがいいけどルートレベルで production/staging の分岐することはできそう
  • そもそも真に Staging なのであれば production と同じデータを使用しているべきではという話もある。(Functions で production→staging の自動コピーしてあげてもよさそうだが)