1. yamacraft

    Posted

    yamacraft
Changes in title
+Firebaseプロジェクトのデプロイについて
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,167 @@
+この記事は「[Firebase Advent Calendar 2017](https://qiita.com/advent-calendar/2017/firebase)」7日目の記事となります。
+
+Firebaseを使ったプロダクトの知見はちらほら見るようになりましたが、デプロイ周りの知見はまだほとんど見ません。ですので私が実際にやっていることをまとめてみました。参考になれば幸いです。
+
+## 前提とする必須条件
+
+ * 開発、ステージング、本番などにデプロイ先を切り替えられる
+ * 秘匿情報はリポジトリに組み込まない
+ * CIでデプロイ可能にする
+
+まずはこれが達成できていることを前提にします。
+
+## Firebase CLI
+
+まず本記事はFirebase CLIの利用を前提としています。そしてFirebase CLIに対する解説はほぼしないため、こちらについては公式のドキュメントを参照してください。
+
+[Firebase CLI リファレンス  |  Firebase](https://firebase.google.com/docs/cli/?hl=ja)
+
+## デプロイ先を切替可能とする
+
+たとえばHostingやDatabase関連は、ひとつのプロジェクトにつき、ひとつしか用意されていません。そのため本番環境とステージング環境など複数の環境を用意したい場合は、それぞれのFirebaseプロジェクトを新規作成する必要があります。
+[これはGoogle公式のドキュメントでも、そんな風に指示しています。](https://firebase.google.com/docs/cli/?hl=ja)
+
+
+プロジェクトへの追加は `firebase use --add` を使う方法もありますが、`.firebaserc` を直接編集した方が手っ取り早いと思います。
+
+```json:.firebaserc
+{
+ "projects": {
+ "release": "project-id",
+ "develop": "develop-project-id"
+ }
+}
+```
+
+`firebase init`でデフォルト連打してプロジェクトを作成すると default 枠が作られているので、これを意図的に削除しておきます。デプロイ先を明示的に指定することで、デプロイ先間違えという事故を防ぐためです。
+
+```shell-session
+$ firebase use develop
+$ firebase deploy
+```
+
+## CI用に認証トークンを用意する
+
+通常、Firebase CLIを使用する場合、`firebase login`で必要なGoogleアカウントへログインする必要があります。しかしそれだとデプロイするたびにログインが必要となりますし、なにより一度ブラウザに遷移するためCIでは利用できません。
+
+という問題を回避するために、 `login:ci` というコマンドがあります。実行すると `login` 同様にブラウザで認証処理が呼び出されます。この認証が完了するとトークンを返却します。
+
+```shell-session
+$ 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を使うことができます。
+
+```shell-session
+$ 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イメージを利用しています。
+
+```Dockerfile:deploy.dockerfile
+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}
+```
+
+```yaml:deploy-develop-compose.yml
+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` を忘れずに設定してください。
+
+```yaml:.circleci/config.yml
+version: 2
+jobs:
+ build:
+ machine: true
+ steps:
+ - checkout
+ - run:
+ name: deploy
+ command: |
+ docker-compose -f ./deploy-develop-compose.yml up --build
+```
+
+## 機能ごとにリポジトリを分けるべきか
+
+今回紹介したデプロイでは、ひとつのリポジトリにHosting、DatabaseやStorageのRule、Cloud Functionsがすべて入ったリポジトリの場合、全部まるごとデプロイします。当然ながらその場合、不要な部分のデプロイも走ってしまい、無駄な時間ができてしまいます。
+
+いちおう、`firebase deploy --only [hosting|functions|database|storage]`とすれば部分的なデプロイも可能です。変更点をチェックして、必要な部分デプロイの判断をするやり方を知っているのであれば、その対応をするのが一番かもしれません。
+ちなみに私はやり方を知りません…。
+
+そうでない場合、いきなり複数のリポジトリに分けると管理がたいへんな気がするので、いったんはCloud Functionsとその他、で分けるぐらいでも良いかもしれません。
+
+以上です。