2018/02/15追記
この記事以降、Alpine Linuxイメージだとしばしばfirebase-tools
のインストール中にgrpcのバイナリファイルが存在されずにインストールが失敗することがありました。
現時点(v3.17.4
)ではこの問題は起きていませんが、安定を考えてnode-slim
イメージを使った方がいいかもしれません。
前置き
この記事は「Firebase Advent Calendar 2017」7日目の記事となります。
Firebaseを使ったプロダクトの知見はちらほら見るようになりましたが、デプロイ周りの知見はまだほとんど見ません。ですので私が実際にやっていることをまとめてみました。参考になれば幸いです。
前提とする必須条件
- 開発、ステージング、本番などにデプロイ先を切り替えられる
- 秘匿情報はリポジトリに組み込まない
- CIでデプロイ可能にする
まずはこれが達成できていることを前提にします。
Firebase CLI
まず本記事はFirebase CLIの利用を前提としています。そしてFirebase CLIに対する解説はほぼしないため、こちらについては公式のドキュメントを参照してください。
Firebase CLI リファレンス | Firebase
デプロイ先を切替可能とする
たとえばHostingやDatabase関連は、ひとつのプロジェクトにつき、ひとつしか用意されていません。そのため本番環境とステージング環境など複数の環境を用意したい場合は、それぞれのFirebaseプロジェクトを新規作成する必要があります。
これはGoogle公式のドキュメントでも、そんな風に指示しています。
プロジェクトへの追加は firebase use --add
を使う方法もありますが、.firebaserc
を直接編集した方が手っ取り早いと思います。
{
"projects": {
"release": "project-id",
"develop": "develop-project-id"
}
}
firebase init
でデフォルト連打してプロジェクトを作成すると default 枠が作られているので、これを意図的に削除しておきます。デプロイ先を明示的に指定することで、デプロイ先間違えという事故を防ぐためです。
# develop環境へdeployを実施する
$ firebase use develop
$ firebase deploy
CI用に認証トークンを用意する
通常、Firebase CLIを使用する場合、firebase login
で必要なGoogleアカウントへログインする必要があります。しかしそれだとデプロイするたびにログインが必要となりますし、なにより一度ブラウザに遷移するためCIでは利用できません。
という問題を回避するために、 login:ci
というコマンドがあります。実行すると login
同様にブラウザで認証処理が呼び出されます。この認証が完了するとトークンを返却します。
$ firebase login:ci
Visit this URL on any device to log in:
https://accounts.google.com/o/oauth2/auth?client_id=......
Waiting for authentication...
✔ Success! Use this token to login on a CI server:
1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Example: firebase deploy --token "$FIREBASE_TOKEN
# 使用例
$ firebase use develop --token 1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
$ firebase deploy --token 1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 認証トークンを無効化する
$ firebase logout --token 1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
✔ Logged out token "1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ここで取得した認証トークンを使うことで、ログインしていない環境からでもFirebase CLIを使うことができます。
# tokenを利用してdevelop環境でdeployを実施
$ firebase use develop --token 1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
$ firebase deploy --token 1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
認証トークンは「Firebaseのプロジェクトに紐づくトークン」ではない
気を付てほしいことがあります。ここで発行した認証トークンは、Googleアカウントに紐付いたトークンです。認証したGoogleアカウントが他のFirebaseプロジェクトにもアクセス可能な場合、そのプロジェクトにも利用可能です。
たとえば開発環境(ステージング)の認証トークンでは本番環境にデプロイさせたくない、というケースがあるとします。その場合、開発環境のFirebaseプロジェクトだけアクセスできるGoogleアカウントからトークンを発行しないといけません。また、該当するGoogleアカウントが削除された場合、当然発行された認証トークンは使えなくなります。
これまで生成した認証トークンって確認できる?
わかりません…。少なくとも、Firebase周りの管理画面では確認できる場所がありませんでした。私も知りたい。
Dockerでデプロイ環境を作る
ということで、CLIだけでデプロイできる準備が整ったのでここからが本番です。私の個人的な趣味と、Circle CI 2.0を利用してデプロイできるようにしたかったので、Dockerでデプロイ環境を構築します。
また、なるべくimageを軽くしたかったので、alpineイメージを利用しています。
FROM node:6.11-alpine
RUN apk update
RUN apk add git
RUN npm install -g firebase-tools
RUN mkdir app
WORKDIR app
ARG FIREBASE_PROJECT="develop"
ARG FIREBASE_TOKEN="x/xxxxxxxxxxxx"
CMD cd functions && npm install && cd ../ \
&& firebase use ${FIREBASE_PROJECT} --token ${FIREBASE_TOKEN} \
&& firebase deploy --token ${FIREBASE_TOKEN}
version: '2'
services:
deploy:
environment:
- FIREBASE_PROJECT=develop
- FIREBASE_TOKEN
build:
context: ./
dockerfile: deploy.dockerfile
volumes:
- ./:/app
これで、 docker-compose -f ./deploy-develop-compose.yml up --build
とすれば、Docker上でデプロイされるようになりました。
ステージング環境やリリース環境へデプロイする場合は、そのぶんだけ FIREBASE_PROJECT
を変更したcompose.ymlを用意します。
Dockerfileの中身少し解説
ARGで初期値を適当にいれているのは、明示的に指定させないと必ず失敗させるようにしたいためです。
また、compose.ymlでは値を指定しないenvironmentがあると、同名のシステム環境変数から値を取得します。今回のプロジェクトで秘匿したい情報は認証トークンだけですので、これでリポジトリにトークンを書かずに済みます。
DockerfileのCMDで最初にやっていることは、Cloud Functionsのデプロイに必要な設定です。Cloud Functionsを触っていないのであれば、飛ばしても問題ありません。
Circle CI2.0で実行する
Circle CI2.0では、そのままconfig.ymlでcomposeを実行するだけです。
FIREBASE_TOKENはCircle CIの環境変数へ登録してください。ただしVM上での実行を前提とするため、 machine:true
を忘れずに設定してください。
version: 2
jobs:
build:
machine: true
steps:
- checkout
- run:
name: deploy
command: |
docker-compose -f ./deploy-develop-compose.yml up --build
deployの動作について
firebase deploy
を実行すると、プロジェクト内でdeploy対象となる各機能(Hosting、Database Rules(+Firestore Rules)、Storage Rules、Cloud Functions)のデプロイが実行されます。この中でひとつでもdeployに失敗すると、全てのdeployは中断されます。また、既にdeployに成功している機能もロールバックされます。
これはDatabaseやStorageのRulesの更新と同時にHostingやCloud Functionsをデプロイしたい場合には有効ですが、こうした仕様であることは留意する必要があります。
個別で確実にdeployを終わらせたい場合は、 firebase deploy --only [hosting|functions|database|storage]
で機能ごとにdeployすればいいですが、普通使うことはないと思います。
ただCircle CI2.0だとたまにtimeoutエラーでコケることがあったので、自分は--only
で別個でアップさせる設定を利用することもあります。
全般的に言えることですが、firebase deploy
はdeployに失敗してもsuccessが返ってくるため、deployの終了通知がきてもCircleCIのログを見て確認が必要です…。不便。
#(省略)
CMD cd functions && npm install && cd ../ \
&& firebase use ${FIREBASE_PROJECT} --token ${FIREBASE_TOKEN} \
&& firebase deploy --only database --token ${FIREBASE_TOKEN} \
&& firebase deploy --only firestore --token ${FIREBASE_TOKEN} \
&& firebase deploy --only storage --token ${FIREBASE_TOKEN} \
&& firebase deploy --only hosting --token ${FIREBASE_TOKEN} \
&& firebase deploy --only functions --token ${FIREBASE_TOKEN}
機能ごとにリポジトリを分けるべきか
今回紹介したデプロイでは、ひとつのリポジトリにHosting、DatabaseやStorageのRule、Cloud Functionsがすべて入ったリポジトリの場合、全部まるごとデプロイします。当然ながらその場合、不要な部分のデプロイも走ってしまい、無駄な時間ができてしまいます。
もし変更点をチェックして、必要な部分デプロイの判断をするやり方を知っているのであれば、その対応をするのが一番かもしれません。ちなみに私はやり方を知りません…。
そうでない場合、いきなり複数のリポジトリに分けても管理が大変だと思います。一番デプロイに時間がかかるのはCloud Functionsなので、やるにしてもまずはCloud Functionsとその他、ぐらいで分けるぐらいでも良いかもしれません。
以上です。