Google Cloud BuildでSPAのビルドとFirebase Hostingへのデプロイを自動化する

この記事はSpeee Advent Calendar 201819日目の記事です。
前日はyuta_kobayashiによる雑談の効用についてのお話でした。

tech.speee.jp

今日はSPAの継続的デリバリーを手軽に実現できる、
Google Cloud Buildのお話です。

やること

最近、ちょっとしたSPAのデプロイ先としてFirebase Hostingを使うことが多くなってきたが、ローカルでFirebaseプロジェクトの設定をしたり、ビルドやデプロイを手動で実行するのが面倒なので最初にCloud Buildを設定してビルドとデプロイを自動化するようにしている。

  • GitHubリポジトリのmasterブランチにコードをpushしたら
  • Cloud BuildでSPAをビルドして
  • Firebase Hostingにデプロイする

以上を自動化する。

参考ドキュメント

ビルドトリガーを使用したビルドの自動化  |  Cloud Build  |  Google Cloud

準備

  • Firebaseで新規プロジェクトを作成
  • アプリケーションはcreate-react-appで適当に作っておく

Firebaseのセットアップ

firebase-cli を使ってセットアップする

// Firebase CLIでログイン
$ firebase login

// SPAのプロジェクトディレクトリでfirebaseを初期化
$ firebase init

// hostingを選択
? **Which Firebase CLI features do you want to setup for this folder? Press Space**
❯◉ Hosting: Configure and deploy Firebase Hosting sites

// buildディレクトリの内容をdeployする
? **What do you want to use as your public directory?** (public) build

// SPAの設定
? **Configure as a single-page app (rewrite all urls to /index.html)?** (y/N) y

デプロイの確認

念のため、Firebase Hostingにローカルからデプロイできることを確認しておく

$ yarn build
$ firebase deploy

Cloud Buildの設定

APIの有効化

コンソールにアクセスしてCloud Build APIを有効にする

FirebaseはGCPと統合するとBlazeプラン(従量)に上がってしまうため、Firebaseのプロジェクトと別にプロジェクトを作ってからCloud Build APIの有効化を行うとよい。

設定

ビルドトリガーの作成

「トリガーの作成」からビルドトリガーを作成する。

f:id:nishaya:20181010141637p:plain

cloudbuild.yamlの作成

cloudbuild.yamlを追加する。

ひとまずビルドの動作確認のため、yarn installyarn build の設定のみ書いてcommitし、masterにpushする

steps:
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['install']
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['build']

pushして確認

cloudbuild.yamlをcommit、pushしてトリガーの確認を行う。

Cloud Buildのビルド履歴から、ビルドの結果が閲覧できる。 各ビルドステップがグリーンになっていればトリガーが発動し、ビルドが成功している。

f:id:nishaya:20181010141707p:plain

Firebaseデプロイのビルドステップを追加する

ここを参考にした

github.com

Firebaseのイメージを作成してgcr.ioにsubmitしておく

$ git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
$ cd cloud-builders-community/firebase/
$ gcloud builds submit --config cloudbuild.yaml .

Firebaseトークンの取得

firebase-cliを使ってCI用のトークンを取得する

$ firebase login:ci
...
✔  Success! Use this token to login on a CI server:

[GENERATED_TOKEN] // 生成されたトークンが表示される

// 後で使うのでexportしておく
$ export FIREBASE_TOKEN=[GENERATED_TOKEN]

KMSでトークンを暗号化

KMSの有効化

https://console.cloud.google.com/security/kms の「セットアップ」でAPIを有効にする

Cloud BuildのサービスアカウントにKMS復号の権限を追加

f:id:nishaya:20181010143112p:plain

トークンの暗号化

$ gcloud auth login
$ gcloud config set project [PROJECT_ID]
$ gcloud kms keyrings create cloudbuilder --location global
$ gcloud kms keys create firebase-token --location global --keyring cloudbuilder --purpose encryption
$ echo -n $FIREBASE_TOKEN | gcloud kms encrypt \
  --plaintext-file=- \
  --ciphertext-file=- \
  --location=global \
  --keyring=cloudbuilder \
  --key=firebase-token | base64
[ENCRYPTED_TOKEN]

表示されたトークンをコピーしておく

cloudbuild.yamlにステップを追加

steps:
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['install']
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['build']
  - name: 'gcr.io/$PROJECT_ID/firebase'
    args: ['deploy', '--project=$PROJECT_ID']
    secretEnv: ['FIREBASE_TOKEN']
secrets:
# kmsKeyNameの中では$PROJECT_IDが使えないので直接書く必要あり
- kmsKeyName: 'projects/[PROJECT_ID]/locations/global/keyRings/cloudbuilder/cryptoKeys/firebase-token'
  secretEnv:
    FIREBASE_TOKEN: '[ENCRYPTED_TOKEN]' # ここにさきほどコピーしたトークンをペースト

余談: create-react-appの環境変数

ビルドトリガーにはsubstitution(代入変数)という形で変数を定義できるが、そのまま環境変数としてロードされるわけではないので env を使ってsubstitution -> 環境変数への変換を行う必要がある

f:id:nishaya:20181010143250p:plain

ビルドトリガーで上記のようにsubstitutionを設定し、cloudbuild.yamlで下記のようにして利用する

steps:
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['install']
  - name: 'gcr.io/cloud-builders/yarn'
    args: ['build']
    env: # substitutionをenvに渡す
      - 'REACT_APP_GOOGLE_MAPS_API_KEY=$_GOOGLE_MAPS_API_KEY'
...
substitutions:
  _GOOGLE_MAPS_API_KEY: your-api-key

確認

以上の設定を行い、cloudbuild.yamlをcommitしてGitHubにpushすればビルドが開始される

f:id:nishaya:20181010143230p:plain

まとめ

現在実運用しているプロジェクトでは、複数のビルドトリガーを用意し、
devブランチへのpushを開発環境に即反映、releaseブランチにpushしたら本番にデプロイといった運用をしている。

ビルドトリガーによって別の cloudbuild.yaml を設定することもできるため、環境に応じて異なるビルドステップを設定することも可能だ。

最近はGitHub Actionsが登場し、同じようなことが実現できるようになったので、そちらも近いうちに試してみたい。


明日はiida-hayatoによるハッカソンの話
お楽しみに!