| 选择迁移路径: | 迁移到 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.editorroles/cloudbuild.builds.editorroles/artifactregistry.writerroles/run.developerroles/iam.serviceAccountUserroles/iam.serviceAccountCreatorroles/cloudfunctions.admin(如果您需要为公共端点执行setIamPermissions)roles/secretmanager.admin(如果使用 Secret)roles/serviceusage.serviceUsageAdmin(如果您需要启用新的 API)
我们建议使用之前安装过扩展程序和部署过函数的账号,因为这些账号的大部分权限都已获得。如果迁移账号需要更多角色,请按照 Google Cloud IAM 说明添加这些角色。
将扩展程序实例升级到最新版本
您必须将扩展程序更新到最新版本,以尽可能缩小扩展程序实例与其替换套件之间的差异。如果您的扩展程序未升级,则扩展程序实例与其套件替换项之间可能存在重大且破坏性的更改。由于各版本之间的参数变化,导出的配置可能与套件的预期不符。
根据扩展程序的安装位置,使用以下任一选项更新扩展程序:
- 在 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 的端到端同步:
- 在 Firebase 控制台的 Cloud Firestore 页面中,创建您设置为
COLLECTION_PATH(users) 的集合(如果该集合尚不存在)。 - 创建一个名为
bigquery-mirror-test的文档,其中包含任意字段和任意值。 在 Google Cloud 控制台的 BigQuery 页面中,查询原始更改日志表。它应包含记录文档创建的单行:
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`查询最新视图,该视图应返回唯一存在的文档 (
bigquery-mirror-test) 的最新更改事件:SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`删除 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-testSDK 对代码进行单元测试,如 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>
如果您在验证期间的任何时候决定要停止或撤消此迁移,都可以按照卸载扩展程序中的说明卸载该套件。
4. 卸载扩展程序
验证已部署的函数套件后,您可以卸载扩展程序,这样就不会出现以下情况:套件和扩展程序都执行一次扩展程序的行为,从而导致行为重复。无论您是通过何种方式安装的扩展程序,都可以通过 Firebase CLI 卸载所有扩展程序,只需传递 --immediate 标志即可:
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 环境(每个环境都有一个导出到 BigQuery 的 documents Cloud Firestore 实例),则可能安装了两个 firestore-bigquery-export 扩展程序实例:
export-documents-testingexport-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 配置的实例,还是安装第二个实例。