將 Firebase Extensions 遷移至自行建立的函式套件

選取遷移路徑: 遷移至 npm 上的函式套件 遷移至自行建立的函式套件

如果發布者尚未建立在 npm 上發布的官方替代套件,本指南將逐步說明如何分支擴充功能,並將其設為本機函式套件。

查看已知的遷移限制

開始遷移擴充功能執行個體前,請先檢查設定是否使用下列任何功能,這些功能需要變通方法,或函式套件尚未支援:

  • 自訂 Docker 存放區和 KMS 金鑰需要手動解決方法 Cloud Functions for Firebase 不支援用於設定自訂 Docker 存放區或客戶自行管理的加密金鑰 (KMS 金鑰) 的替代系統參數。如果擴充功能設定了其中任一參數,請參閱常見問題因應措施。

事前準備

您需要設定 Firebase CLI 並初始化 Firebase 專案。使用 CLI 時,請務必使用 firebase-tools >= 15.32.0 版,其中包含新的遷移和函式套件指令。

必要的帳戶權限和角色

視 Firebase CLI 在遷移期間需要建立及設定的項目而定,您用於向 Firebase 和 Google Cloud 驗證身分的帳戶必須具備下列角色:

  • roles/firebaseextensions.editor
  • roles/cloudbuild.builds.editor
  • roles/artifactregistry.writer
  • roles/run.developer
  • roles/iam.serviceAccountUser
  • roles/iam.serviceAccountCreator
  • roles/cloudfunctions.admin (如需對公開端點執行 setIamPermissions)
  • roles/secretmanager.admin (如果使用密鑰)
  • roles/serviceusage.serviceUsageAdmin (如需啟用新的 API)

建議使用先前安裝過擴充功能和部署過函式的帳戶,因為這類帳戶大多已具備這些權限。如果遷移帳戶需要更多角色,請按照 Google CloudIAM 操作說明新增。

將擴充功能執行個體升級至最新版本

您必須將擴充功能更新至最新版本,盡可能縮小擴充功能執行個體與替換套件之間的差異。如果擴充功能未升級,擴充功能執行個體和套件替代項目之間可能會有重大破壞性變更。由於不同版本的參數有所變更,匯出的設定可能與套件預期的設定不符。

請根據擴充功能的安裝位置,使用下列其中一種方式更新擴充功能:

  • 透過 Firebase 控制台
  • 使用 Firebase CLI,並執行下列指令:
    • firebase ext:update <extension-instance-id> --project <project-id> firebase deploy --only extensions --project <project-id>

如果略過這個步驟,且擴充功能並非最新版本,CLI 會在匯出設定時提示您升級。

將擴充功能分支至本機函式套件

開始將擴充功能轉換為本機函式套件之前,請確認擴充功能原始碼位於 Firebase 專案中。為此,請從 GitHub 複製擴充功能存放區,在 Firebase 專案根目錄中建立目錄,然後將擴充功能的 functions/ 資料夾和 extension.yaml 複製到該目錄:

mkdir -p path/to/kit
cp -r /path/to/extension-source/functions/* path/to/kit/
cp /path/to/extension-source/extension.yaml path/to/kit/.

按照發布者遷移指南的步驟 1 至 8,將擴充功能原始碼遷移至第 2 代函式。再執行下列步驟。

讓本機套件支援匯出函式區域和進階參數

在本機函式套件中,Firebase CLI 不會產生 index.ts 檔案來設定套件,並將其設為使用遷移的系統參數。如要使用為擴充功能設定的函式區域和進階參數,請設定 index.ts 檔案,將 firebase ext:export --mode functions 匯出的格式讀取至環境變數檔案。

具體來說,在匯出函式的頂層 index.ts 檔案中,定義 FUNCTION_DEFAULT_REGION 的參數,並使用 EXT_MIGRATED_SYSTEM_<GLOBAL_OPTION> 形式的環境變數呼叫 setGlobalOptions,類似於 CLI 使用的 index-kit-migration.ts 範本:

import { setGlobalOptions } from "firebase-functions";
import { MemoryOption, VpcEgressSetting, IngressSetting } from "firebase-functions/v2/options";
import { defineString } from "firebase-functions/params";

export const regionParam = defineString("FUNCTION_DEFAULT_REGION", {
  input: { text: { nonEmpty: true } },
  description: "Global default region where functions should be deployed. Can be overridden per-function.",
});

setGlobalOptions({
  region: regionParam,
  memory: (process.env.EXT_MIGRATED_SYSTEM_MEMORY as MemoryOption) ?? undefined,
  timeoutSeconds: process.env.EXT_MIGRATED_SYSTEM_TIMEOUTSECONDS
    ? Number(process.env.EXT_MIGRATED_SYSTEM_TIMEOUTSECONDS)
    : undefined,
  vpcConnectorEgressSettings:
    process.env.EXT_MIGRATED_SYSTEM_VPCCONNECTOREGRESSSETTINGS &&
    process.env.EXT_MIGRATED_SYSTEM_VPCCONNECTOREGRESSSETTINGS !== "VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED"
      ? (process.env.EXT_MIGRATED_SYSTEM_VPCCONNECTOREGRESSSETTINGS as VpcEgressSetting)
      : undefined,
  vpcConnector: process.env.EXT_MIGRATED_SYSTEM_VPCCONNECTOR ?? undefined,
  maxInstances: process.env.EXT_MIGRATED_SYSTEM_MAXINSTANCES
    ? Number(process.env.EXT_MIGRATED_SYSTEM_MAXINSTANCES)
    : undefined,
  minInstances: process.env.EXT_MIGRATED_SYSTEM_MININSTANCES
    ? Number(process.env.EXT_MIGRATED_SYSTEM_MININSTANCES)
    : undefined,
  ingressSettings: (process.env.EXT_MIGRATED_SYSTEM_INGRESSSETTINGS as IngressSetting) ?? undefined,
  // Parses a comma-separated string of key:value pairs into a key-value object
  // (for example, "key1:value1,key2:value2" -> { key1: "value1", key2: "value2" }).
  labels: process.env.EXT_MIGRATED_SYSTEM_LABELS
    ? process.env.EXT_MIGRATED_SYSTEM_LABELS.split(",").reduce<Record<string, string> | undefined>(
        (acc, curr) => {
          const [key, value] = curr.split(":");
          const trimmedKey = key?.trim();
          const trimmedValue = value?.trim();
          if (!trimmedKey || !trimmedValue) {
            return acc;
          }
          acc = acc ?? {};
          acc[trimmedKey] = trimmedValue;
          return acc;
        },
        undefined,
      )
    : undefined,
});

// Re-export all functions so the Firebase CLI can deploy them
export * from "./your-functions";

在遷移前測試套件

您現在擁有本機函式套件,部署後的行為與新安裝的擴充功能完全相同。下一步是驗證並修正過程中意外發生的任何問題,然後再將正式版擴充功能執行個體遷移至該版本。

首先,請將 fork 新增為本機套件、設定套件,並部署至測試專案。本機函式套件必須位於 Firebase 專案中,因此如果複製的擴充功能存放區位於 Firebase 專案外部,請將其移至專案目錄中。接著執行下列套件安裝指令,將其安裝為本機套件:

firebase functions:kits:install --directory <path-to-your-fork> --project <test-project-id>

這項指令會引導您選擇套件 ID、執行個體 ID,以及第一個測試執行個體的設定。接著,指令碼會修改 firebase.json 檔案,註冊指向分支目錄的本機套件,並將每個執行個體的設定儲存在 function-kits/<kit-id>/config-<instance-id> 的 .env 檔案中。

將本機套件部署至測試專案,並提供適當資源來測試其行為。如果您已在測試擴充功能時設定測試專案,請執行下列指令:

firebase deploy --only functions:<kit-instance-id> --project <test-project-id>

實際範例:將 Cloud Firestore 串流至 BigQuery (firestore-bigquery-export)

驗證 Cloud Firestore 到 BigQuery 的端對端同步處理:

  1. 在 Firebase 控制台的「Cloud Firestore」頁面中,建立您設為 COLLECTION_PATH (users) 的集合 (如果尚未建立)。
  2. 建立名為 bigquery-mirror-test 的文件,其中包含任何欄位和任何值。
  3. 在 Google Cloud 控制台的「BigQuery」頁面中,查詢原始變更記錄資料表。其中應包含記錄文件建立作業的單一資料列:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`
    
  4. 查詢最新檢視畫面,系統應會傳回唯一現有文件 (bigquery-mirror-test) 的最新變更事件:

    SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`
    
  5. 刪除 Cloud Firestore 中的 bigquery-mirror-test 文件。該資料列會從最新檢視畫面中消失,且 DELETE 事件會附加至原始變更記錄資料表。

    您可以透過下列方式,查看單一文件的完整記錄:

    SELECT *
       FROM `PROJECT_ID.analytics.users_raw_changelog`
       WHERE document_name = "bigquery-mirror-test"
       ORDER BY timestamp ASC
    

與測試擴充功能時的差異:

  • 觸發條件會部署為 kit-<kit-instance-id>-fsexportbigquery,而非 ext-<instanceId>-fsexportbigquery。在 Cloud Functions 資訊主頁和記錄中尋找該名稱。
  • 程式碼會以標準函式的形式在 Firebase Local Emulator Suite 中執行。您可以使用 .env.local 設定要在模擬器中使用的參數值。您也可以使用 firebase-functions-test SDK 對程式碼進行單元測試,如「Cloud Functions 的單元測試」一文所述。
  • 佈建作業不再由擴充功能執行階段驅動。如果部署後缺少變更記錄資料表,請手動重新執行設定工作:firebase functions:lifecycle:run afterFirstDeploy <kit-instance-id>。這項工作具有冪等性,因此重新執行時會協調資料集、資料表和檢視表。
  • 參數值來自 .env,而非安裝表單,因此 .env 完成後,重新執行 firebase deploy 時不會有互動。

(選用) 清除測試資源

如要在測試後移除這個測試執行個體,請將其解除安裝:

firebase functions:kits:uninstall --instance <kit-instance-id> --project <test-project-id>

這會刪除部署套件時建立的所有雲端資源,並移除執行個體設定。如果套件只有一個例項,這也會從 firebase.json 中移除套件項目。但不會刪除本機原始碼目錄。為正式環境遷移作業安裝套件時,可以再次選擇套件 ID。

從擴充功能遷移至本機套件

測試完本機套件後,即可遷移已部署的擴充功能執行個體。

1. 安裝替換用函式套件執行個體

安裝本機函式套件,並傳遞 --no-configure 以略過手動設定,讓下一步可以直接將現有的擴充功能設定匯出至這個套件執行個體:

firebase functions:kits:install --no-configure --directory <path-to-your-fork> --project <project-id>

2. 將函式套件執行個體設為與擴充功能相同

您必須自訂這個套件執行個體,並採用與要取代的擴充功能相同的設定。您可以將擴充功能執行個體設定匯出至 .env 檔案,該檔案會儲存所有 Cloud Functions (包括套件) 的參數、環境變數和密鑰參照設定資料。如要直接匯出至套件的設定檔,請執行:

firebase ext:export --mode functions --instance <extension-instance-id> --kit-instance <kit-instance-id> --project <project-id>

完成這個步驟後,這個執行個體的設定資訊會儲存在執行個體設定目錄中專案專屬的 .env 檔案,例如: function-kits/<kit-name>/config-<instance-id>/.env.<project-id>

3. 部署及驗證套件替換作業

套件安裝完畢並可做為一組函式使用後,您就可以部署套件替換項目。函式套件的運作方式與標準函式類似,每個套件執行個體都可做為獨立的程式碼集,用於整理函式。您可以選擇部署所有函式,或只部署特定套件執行個體。遷移單一擴充功能執行個體時,請只部署該套件執行個體。

如果套件使用任何新參數,而這些參數並未出現在您遷移的擴充功能執行個體中,Firebase CLI 會在部署程序開始時提示您輸入這些參數。在這個範例中,我們使用的是最新版 firestore-bigquery-export 擴充功能,因此不會出現這種情況,但許多套件都會提示使用者為套件使用的任何事件觸發來源提供新參數。在這次遷移中,更新後的套件會使用第 2 代函式,而擴充功能先前使用的是第 1 代函式。在第 2 代中,函式位於事件來源附近,並新增為額外參數。在日後的更新中,如果新增了參數,CLI 會在下次部署時提示您。

實際範例:

firebase deploy --only functions:firestore-bigquery-export --project my-project

輸出內容:

=== Deploying to 'my-project'...
i  deploying functions
i  functions: Loaded environment variables from function-kits/firestore-bigquery-export/config-firestore-bigquery-export/.env.my-project
i  functions: ensuring required API bigquery.googleapis.com is enabled...
i  functions: ensuring required API cloudtasks.googleapis.com is enabled...
✔  functions: required APIs are enabled
i  functions: granting declarative IAM roles to managed service account:
   - BigQuery Data Editor
   - BigQuery User
   - Cloud Datastore User
   - Eventarc Event Receiver
   - roles/run.invoker
✔  functions: successfully granted IAM roles
i  functions: creating Node.js 22 (2nd Gen) function kit-firestore-bigquery-export-fsexportbigquery(us-central1)...
i  functions: creating Node.js 22 (2nd Gen) function kit-firestore-bigquery-export-initBigQuerySync(us-central1)...
i  functions: creating Node.js 22 (2nd Gen) function kit-firestore-bigquery-export-setupBigQuerySync(us-central1)...
✔  functions[kit-firestore-bigquery-export-fsexportbigquery(us-central1)] Successful create operation.
✔  functions[kit-firestore-bigquery-export-initBigQuerySync(us-central1)] Successful create operation.
✔  functions[kit-firestore-bigquery-export-setupBigQuerySync(us-central1)] Successful create operation.
i  functions: Executing afterFirstDeploy lifecycle hook targeting: kit-firestore-bigquery-export-initBigQuerySync...
✔  functions: Successfully queued task for lifecycle hook kit-firestore-bigquery-export-initBigQuerySync in queue projects/my-project/locations/us-central1/queues/kit-firestore-bigquery-export-initBigQuerySync.
✔  Deploy complete!

如要確認套件的 firebase deploy 沒有錯誤,請查看部署記錄,確認是否觸發任何生命週期掛鉤。Stream Cloud Firestore to BigQuery 等熱門擴充功能會使用生命週期掛鉤。以下是生命週期掛鉤觸發時的範例:

i  functions: Executing afterFirstDeploy lifecycle hook targeting: kit-firestore-bigquery-export-initBigQuerySync...
✔  functions: Successfully queued task for lifecycle hook kit-firestore-bigquery-export-initBigQuerySync in queue projects/my-project/locations/europe-west1/queues/kit-firestore-bigquery-export-initBigQuerySync.
i  functions: View logs for afterFirstDeploy at: https://console.cloud.google.com/logs/query;query=resource.type%3D%22cloud_run_revision%22%0Aresource.labels.service_name%3D%22kit-firestore-bigquery-export--initbigquerysync%22%0Aresource.labels.location%3D%22europe-west1%22;project=my-project

這些記錄訊息可確認下列事項:

  • 找到並執行生命週期掛鉤。
  • 工作已排入生命週期掛鉤相關聯的工作佇列。
  • 我們提供了 Cloud Logging 的連結,方便您確認工作是否順利完成。

點選記錄連結前往 Google Cloud 控制台,確認記錄中沒有錯誤,且工作佇列事件已成功處理。如果生命週期事件未成功執行,您可以執行下列指令重新觸發:

firebase functions:lifecycle:run <hook-name> <codebase>

如果是首次部署函式套件執行個體,請執行:

firebase functions:lifecycle:run afterFirstDeploy <kit-instance-id>

在驗證期間,如果您決定停止或復原這項遷移作業,可以按照「解除安裝擴充功能」中的操作說明解除安裝套件。

確認函式套件的初始部署作業已成功完成後,您需要驗證套件的功能是否確實正常運作。由於擴充功能本身仍在執行,因此您必須小心,不要只檢查兩者之一是否有效,因為擴充功能已經且將繼續運作。

如果處理方式未變更,且您未看到擴充功能和套件的重複結果,最好的做法是在觸發套件功能後,直接檢查套件的記錄。

實際範例:將 Cloud Firestore 串流至 BigQuery

如果同一個事件傳送至套件和擴充功能,則使用 Stream Cloud Firestore to BigQuery 擴充功能寫入原始變更記錄資料表 (*_raw_changelog) 和最新檢視資料表 (*_raw_latest) 時,會簡化資料。因此,您無法使用 BigQuery 中的任何資料列或重複資料列,來驗證套件是否正常運作。

使用 firebase functions:logs 寫入 Firestore 集合後,您很快就能查看函式記錄,並尋找指出成功處理寫入作業的記錄項目。如果這個套件的預設執行個體 ID 為 firestore-bigquery-export,則會顯示如下:

2026-09-25T19:52:46.328612Z I kit-firestore-bigquery-export-fsexportbigquery: Started execution of extension
2026-09-25T19:52:46.328644Z I kit-firestore-bigquery-export-fsexportbigquery: {"message":"Firestore event received by onDocumentWritten trigger","document_name":"projects/extensions-eventarc-testing/databases/(default)/documents/exported-messages/cmGP6XLyksqGdw2OgTOq","operation":0,"event_id":"9f59a2a4-1a5e-4c41-a32d-561e3c734502"}
2026-09-25T19:52:46.616064Z I kit-firestore-bigquery-export-fsexportbigquery: Completed execution of extension
2026-09-2

如果記錄很多,可以使用下列指令篩選出特定套件函式:

firebase functions:log --only <function name>

如要找出套件執行個體的特定函式名稱,可以使用下列指令:

firebase functions:list | grep kit-<kit instance id>

在這個範例中:

firebase functions:list | grep kit-firestore-bigquery-export
│ kit-firestore-bigquery-export-fsexportbigquery  │ v2      │ google.cloud.firestore.document.v1.written                    │
│ kit-firestore-bigquery-export-initBigQuerySync  │ v2      │ taskQueue                                                     │
│ kit-firestore-bigquery-export-setupBigQuerySync │ v2      │ taskQueue                                                     │
│ kit-firestore-bigquery-export-syncBigQuery      │ v2      │ taskQueue                                                     │

4. 解除安裝擴充功能

驗證已部署的函式套件後,您可以解除安裝擴充功能,以免套件和擴充功能重複執行相同行為。無論安裝方式為何,只要傳遞 --immediate 旗標,即可從 Firebase CLI 解除安裝所有擴充功能:

firebase ext:uninstall <extension-instance-id> --project <project-id> --immediate

實際範例:

firebase ext:uninstall firestore-bigquery-export --project my-project --immediate

輸出內容:

i  extensions: uninstalling firestore-bigquery-export...
i  extensions: deleting extension instance resources in project my-project...
✔  extensions: successfully uninstalled firestore-bigquery-export

進階遷移

您可以在多個 Firebase 專案中加入擴充功能,並使用單一程式碼集管理。舉例來說,如果您將相同的基礎架構部署至 testing 環境和 production 環境,且每個環境都有一個 documents Cloud Firestore 執行個體,並匯出至 BigQuery,則您可能會安裝兩個 firestore-bigquery-export 擴充功能執行個體:

  • export-documents-testing
  • export-documents-production

如果使用 Firebase CLI,將這兩個擴充功能執行個體遷移至單一程式碼集中的兩個函式套件執行個體,並使用 firebase deploy --project testing 和 firebase deploy --project production 部署,則每次部署都會在 testing 和 production 環境中建立兩個執行個體。

請改用一個部署至多個專案的 firestore-bigquery-export 函式套件執行個體,取代兩個擴充功能執行個體,每個專案都有自己的設定。執行個體的設定目錄應如下所示:

  • config-export-documents/
    • .env.testing
    • .env.production

每次部署至 testing 和 production 時,系統都會建立一個套件執行個體,並套用對應的設定。只要在每次叫用 ext:migrate 或 functions:kits:install 時傳遞 --project 旗標,現有的 CLI 指令就會建立這項設定。

實際範例:

firebase functions:kits:install --package @firebase-function-kits/firestore-bigquery-export --project testing --no-configure --template migration
✔ What would you like to name this kit? firestore-bigquery-export
✔ What would you like to name this instance? export-documents
✔  Wrote function-kits/firestore-bigquery-export/source/package.json
✔  Wrote function-kits/firestore-bigquery-export/source/tsconfig.json
✔  Wrote function-kits/firestore-bigquery-export/source/.gitignore
✔  Wrote function-kits/firestore-bigquery-export/source/src/index.ts
i  functions: Running npm install
✔  Wrote configuration info to firebase.json
✔  functions: Function kit firestore-bigquery-export successfully installed.
# This creates the export-documents instance with an empty .env.testing file
# for the testing project. Now populate it via export:
firebase ext:export --mode functions --instance export-documents-testing \
  --kit-instance export-documents --project testing

# Repeat the export for production into the same kit instance to create
# .env.production from the export-documents-prod instance:
firebase ext:export --mode functions --instance export-documents-prod \
  --kit-instance export-documents --project production

您現在已設定單一套件執行個體,可使用各自的設定部署至 testing 和 production 專案。如果您在 testing 專案中建立執行個體,並在 production 專案中為同一個套件執行 functions:kits:install 指令,系統會提示您選擇重複使用為 testing 設定的執行個體,或安裝第二個執行個體。