🔥

Firebase へのデプロイを GitHub Actions & Workload Identity によって自動化する

2024/07/22に公開

firebase

こんにちは、クラウドエース フロントエンド・UI/UX 部の 小堀内 です。

今回は、私が個人開発においてよく実施する、Firebase デプロイ自動化の方法についてご紹介します。

はじめに

Firebase には複数のサービスが存在しています。
例えば、Cloud Firestore, Cloud Storage for Firebase, Cloud Functions for Firebase などがありますよね。

Firebase は mBaaS のプロダクトであり、基本的には Firebase コンソール内でほとんどの操作が可能ですが、実際のプロジェクト開発においては、コードベースでの管理も重要になってきます。

具体的な管理対象としては、下記のものが挙げられるかなと思います。

  • Cloud Firestore
    • Indexes
    • Security Rules
  • Cloud Storage for Firebase
    • Security Rules
  • Cloud Functions for Firebase
    • 関数の実体
  • Firebase Hosting
    • ホスティング対象のコンテンツ
  • など

今回は、これらを GitHub で管理した際の、自動デプロイの方法についてご紹介します。

やること

  • GitHub Actions と Workload Identity を活用した Firebase サービスの自動デプロイ

やらないこと

  • Cloud Build を活用した Firebase サービスの自動デプロイ
  • Google Cloud / Firebase の IaC 化
  • CI 領域の説明

前提事項

本記事での環境構成は、下記として以降の説明を行なっていきます。

環境数

1 プロジェクトのみ (今回は開発、検証、本番などの環境分けは実施しない)

ディレクトリ構成

Firebase CLI で firebase init を実行した際に生成されるディレクトリ構成
.
├── functions/              # Cloud Functions for Firebase の関数の実体を管理
├── .firebaserc             # Firebase プロジェクトの設定や環境の定義
├── firebase.json           # Firebase サービスの設定ファイル
├── firestore.indexes.json  # Firestore のインデックス定義
├── firestore.rules         # Firestore のセキュリティルール
└── storage.rules           # Cloud Storage for Firebase のセキュリティルール

Firebase デプロイ自動化アーキテクチャ

architecture
GitHub Actions & Workload Identity の活用による Firebase デプロイの自動化

  1. 開発者が Firebase 関連のコード (Firestore Indexes, Security Rules, Cloud Functions for Firebase の関数, Firebase Hosting 配信対象のコンテンツ など) を GitHub リポジトリに push する
  2. プッシュされたコードが GitHub Actions Workflow をトリガーする
  3. GitHub OIDC Provider が Workload Identity Pool と連携し、認証情報を提供する
  4. GitHub Actions Workflow が Google Cloud の Security Token Service (Cloud IAM) と通信し、認証を行う
  5. 認証が成功すると、指定された Service Account (Cloud IAM) の権限が付与される
  6. 取得した権限を使用して、GitHub Actions から Firebase へのデプロイが実行される
  7. デプロイ対象の Firebase サービス(Cloud Firestore, Cloud Storage for Firebase, Cloud Functions for Firebase, Firebase Hosting など)に変更が適用される

構築手順

1. Firebase プロジェクト立ち上げ

start-firebase

2. Workload Identity 連携

Firebase (Google Cloud) 側で、GitHub Actions からのデプロイリクエストを安全に認証するための Workload Identity 連携を設定します。

これにより、特定の GitHub リポジトリからの操作のみを許可し、セキュアなデプロイプロセスを実現します。

2-1. GitHub Actions が Firebase にアクセスするためのサービスアカウントを作成

create-sa

2-2. サービスアカウントに対し、必要なロールを付与

add-roles

Firebase デプロイに必要なロールは下記の 3 つです。

  • Cloud RuntimeConfig 管理者
  • Firebase 管理者
  • Workload Identity ユーザー
  • サービス アカウント ユーザー

2-3. ID プールの作成

create-pool

2-4. プールにプロバイダを追加

create-provider

2-5. プロバイダの属性を構成

プロバイダの属性を構成することで、どの GitHub リポジトリからのリクエストを許可するかを細かく制御できます。
今回は、以下のように設定します。

add-element

設定項目 説明
google.subject assertion.repository GitHub Actions から送られてくる JWT トークンの repository クレームと一致するリクエストのみを許可します。
特定の GitHub リポジトリからのデプロイリクエストのみを受け付けることができます。
属性条件 assertion.repository_owner == "nozomi-koborinai" リポジトリのオーナーが "nozomi-koborinai" である場合のみ、リクエストを許可します。
特定の GitHub アカウントまたは組織が所有するリポジトリからのデプロイのみを許可することができます。

2-6. サービスアカウントに対してアクセス権を付与

これにより、GitHub Actions から特定のサービスアカウントを使用して Firebase リソースにアクセスできるようになります。

sa-access

設定項目 説明
アクセス権付与方法 サービス アカウントの権限借用を使用してアクセス権を付与する Workload Identity を使用して一時的な認証を行います。
サービス アカウント deploy-to-firebase-backend 使用するサービスアカウントを選択します。
プリンシパル - 属性 subject GitHub Actions から送られる JWT トークンの subject クレームを使用します。
プリンシパル - 値 nozomi-koborinai/target-repository アクセスを許可する GitHub リポジトリを指定します。

この設定により、指定した GitHub リポジトリ (nozomi-koborinai/target-repository) からの GitHub Actions ワークフローが、選択したサービスアカウント (deploy-to-firebase-backend) の権限を借用して Firebase リソースにアクセスできるようになります。

3. GitHub Actions ワークフローの定義

GitHub Actions を用いて、対象リポジトリから Firebase へデプロイを行うためのワークフローを定義します。

今回は下記のデプロイワークフローを定義していきます。

この図を、GitHub Actions ワークフロー定義の yaml に記述してみると下記になります。

.github/workflows/cd.yaml
name: CD

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  changes:
    name: Changes
    runs-on: ubuntu-latest
    timeout-minutes: 15
    outputs:
      storage: ${{ steps.filter.outputs.storage }}
      firestore: ${{ steps.filter.outputs.firestore }}
      functions: ${{ steps.filter.outputs.functions }}
    steps:
      # ソースコードをチェックアウト
      - name: Check out repository
        uses: actions/checkout@v2

      # Firebase のどのサービスに変更が加えられるかをチェックする
      - name: Detect changes
        uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            storage:
              - 'storage.*'
            firestore:
              - 'firestore.*'
            functions:
              - 'functions/**'

  # Cloud Storage for Firebase にデプロイ
  deploy_storage:
    name: Deploy Storage
    needs:
      - changes
    if: ${{ needs.changes.outputs.storage == 'true' }}
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    timeout-minutes: 15
    steps:
      # ソースコードをチェックアウト
      - name: Check out repository
        uses: actions/checkout@v2

      # 認証
      - name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.SERVICE_ACCOUNT }}
          create_credentials_file: true
          export_environment_variables: true

      # Cloud Storage for Firebase にデプロイ
      - name: Deploy to Storage
        run: |
          npm install -g firebase-tools
          firebase deploy --only storage --project=bar-project

  # Cloud Firestore にデプロイ
  deploy_firestore:
    name: Deploy Firestore
    needs:
      - changes
    if: ${{ needs.changes.outputs.firestore == 'true' }}
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    timeout-minutes: 15
    steps:
      # ソースコードをチェックアウト
      - name: Check out repository
        uses: actions/checkout@v2

      # 認証
      - name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.SERVICE_ACCOUNT }}
          create_credentials_file: true
          export_environment_variables: true

      # Cloud Firestore にデプロイ
      - name: Deploy to Firestore
        run: |
          npm install -g firebase-tools
          firebase deploy --only firestore --project=bar-project

  # Cloud Functions for Firebase にデプロイ
  deploy_functions:
    name: Deploy Functions
    needs:
      - changes
    if: ${{ needs.changes.outputs.functions == 'true' }}
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    timeout-minutes: 15
    steps:
      # ソースコードをチェックアウト
      - name: Check out repository
        uses: actions/checkout@v2

      # 認証
      - name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v1"
        with:
          workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.SERVICE_ACCOUNT }}
          create_credentials_file: true
          export_environment_variables: true

      # node のセットアップ
      - name: Setup node
        uses: actions/setup-node@v1
        with:
          node-version: 18

      # ライブラリのインストール
      - name: Install Dependencies
        run: npm install
        working-directory: ./functions

      # Cloud Functions for Firebase にデプロイ
      - name: Deploy to Functions
        run: |
          npm install -g firebase-tools
          firebase deploy --only functions --force --project=bar-project

4. GitHub リポジトリシークレットの設定

GitHub Actions ワークフローで Workload Identity を使用するために、いくつかの秘密情報を GitHub リポジトリのシークレットとして設定していきます。

NAME VALUE
WORKLOAD_IDENTITY_PROVIDER projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<Workload Identity プール ID>/providers/<Workload Identity プロバイダ ID>
SERVICE_ACCOUNT deploy-to-firebase-backend@<プロジェクトID>.iam.gserviceaccount.com

これで、Firebase デプロイ自動化のための設定は完了です。

動作確認

最後に、どのようにデプロイが実施されるかを見ていきましょう。
今回は、動作確認程度なので、Cloud Firestore の Security Rules のみを変更して、その挙動を確認していきたいと思います。

1. 現状確認

まず Firebase コンソールから現状の Cloud Firestore の Security Rules を確認します。

before

2. コード変更

次に、対象 Firebase プロジェクトのコードを管理しているリポジトリで、一部コードを修正します。

change-code

3. Pull Request 作成 & main ブランチへの merge

コード変更の Pull Request を作成します。

create-pr

その後、コード修正に問題がなければ merge を実施します。

merge-pr

この時点で、先ほど定義した GitHub Actions ワークフローが実行されます。

4. GitHub Actions による Firebase 自動デプロイの実行

run-actions

5. 反映確認

実際に Firebase コンソール上でも値を確認してみます。

after

何の問題もなくデプロイが成功しました。

さいごに

今回は、Firebase のデプロイを GitHub Actions と Workload Identity を使用して自動化する方法をご紹介しました。

この自動化プロセスを導入することで、手動デプロイによる属人性の解消や、ヒューマンエラーの削減、Workload Identity 活用によるセキュアな認証プロセスの確立が期待できます。

さいごまで読んでいただきありがとうございました。

参考

Discussion