配置您的环境

您往往需要对您的函数进行额外配置,例如第三方 API 密钥或可调整的设置。Firebase SDK for Cloud Functions 提供了两种环境配置方法,让您可以轻松地为项目存储和检索此类数据:

  • 参数化配置。这是一种强类型的环境配置方法,会在部署时对参数进行验证,这样可以防止一些错误发生并简化调试过程。
  • 环境变量。如果使用此方法,您可以手动创建用于加载环境变量的 dotenv 文件。

对于大多数使用场景,建议使用参数化配置。此方法使得配置值在运行时和配置时都可使用;并且除非所有参数都具有有效值,否则系统将阻止部署。与之相反,使用环境变量方法进行的配置在部署时则无法使用。

参数化配置

Cloud Functions for Firebase 提供了一个用于在代码库中以声明方式定义配置参数的接口。这些参数的值可在函数部署期间(设置部署和运行时选项时)以及在执行过程中使用。这意味着,除非所有参数都具有有效值,否则 CLI 将阻止部署。

如需在代码中定义参数,请参考以下代码:

const { onRequest } = require('firebase-functions/v2/https');
const { defineInt, defineString } = require('firebase-functions/params');

// Define some parameters
const minInstancesConfig = defineInt('HELLO_WORLD_MININSTANCES');
const welcomeMessage = defineString('WELCOME_MESSAGE');

// To use configured parameters inside the config for a function, provide them
// directly. To use them at runtime, call .value() on them.
export const helloworld = onRequest(
  { minInstances: minInstancesConfig },
 (req, res) => {
    res.send(`${welcomeMessage.value()}! I am a function.`);
  }
);

使用参数化配置变量部署函数时,Firebase CLI 会先尝试从本地 .env 文件加载其值。如果这些文件中不存在所需值,并且未设置 default,CLI 将在部署过程中提示您输入值,然后自动将其值保存到您的 functions/ 目录中名为 .env.<project_ID>.env 文件中:

$ firebase deploy
i  functions: preparing codebase default for deployment
? Enter a string value for ENVIRONMENT: prod
i  functions: Writing new parameter values to disk: .env.projectId
…
$ firebase deploy
i  functions: Loaded environment variables from .env.projectId

将生成的 .env.<project_ID> 文件添加到版本控制可能会很有用,具体取决于您的开发工作流。

配置 CLI 行为

您可以使用 Options 对象配置参数来控制 CLI 提示您输入值的方式。以下示例会设置选项来验证电话号码的格式、提供简单的选择选项,并会自动从 Firebase 项目填充选择选项:

const { defineString } = require('firebase-functions/params');

const welcomeMessage = defineString('WELCOME_MESSAGE', {default: 'Hello World',
description: 'The greeting that is returned to the caller of this function'});

const onlyPhoneNumbers = defineString('PHONE_NUMBER', {input: {text:
{validationRegex: /\d{3}-\d{3}-\d{4}/, validationErrorMessage: "Please enter
a phone number in the format XXX-YYY-ZZZZ"}}});

const selectedOption = defineString('PARITY', {input: {select: {options:
[{value: "odd"}, {value: "even"}]}}})

const storageBucket = defineString('BUCKET', {input: {resource: {type:
"storage.googleapis.com/Bucket"}}, description: "This will automatically
populate the selector field with the deploying Cloud Project’s
storage buckets"})

参数类型

参数化配置可提供强类型的参数值,并且还支持来自 Cloud Secret Manager 的 Secret。支持的类型包括:

  • Secret
  • 字符串
  • 布尔值
  • 整数
  • 浮点数

参数值和表达式

Firebase 会在部署时和在函数执行期间评估参数。出于这种双重环境考虑,在比较参数值和使用它们来为函数设置运行时选项时,必须格外小心。

如需将参数作为运行时选项传递给函数,直接传递即可:

const { onRequest } = require('firebase-functions/v2/https');
const { defineInt } = require('firebase-functions/params');
const minInstancesConfig = defineInt('HELLO\_WORLD\_MININSTANCES');

export const helloworld = onRequest(
  { minInstances: minInstancesConfig },
  (req, res) => {
    //…

但是,若您需要针对某个参数进行比较,以了解应该选择哪个选项,则需要使用内置比较器,而不仅仅是检查值:

const { onRequest } = require('firebase-functions/v2/https');
const { defineBool } = require('firebase-functions/params');
const environment = params.defineString(ENVIRONMENT, {default: dev});

// use built-in comparators
const minInstancesConfig = environment.equals('PRODUCTION').thenElse(10, 1);
export const helloworld = onRequest(
  { minInstances: minInstancesConfig },
  (req, res) => {
    //…

只在运行时使用的参数和参数表达式可以通过其 value 函数访问:

const { onRequest } = require('firebase-functions/v2/https');
const { defineString } = require('firebase-functions/params');
const welcomeMessage = defineString('WELCOME_MESSAGE');

// To use configured parameters inside the config for a function, provide them
// directly. To use them at runtime, call .value() on them.
export const helloworld = onRequest(
 (req, res) => {
    res.send(`${welcomeMessage.value()}! I am a function.`);
  }
);

内置参数

Cloud Functions SDK 提供三个预定义参数,可通过 firebase-functions/params 子软件包获取:

  • projectId - 运行函数的 Cloud 项目。
  • databaseUrl - 与函数关联的 Realtime Database 实例的网址(如果已在 Firebase 项目中启用)。
  • storageBucket - 与函数关联的 Cloud Storage 存储桶(如果已在 Firebase 项目中启用)。

这些函数在各个方面都类似于用户定义的字符串参数,只是由于 Firebase CLI 始终知道它们的值,因此系统永远不会在部署时提示您输入其值,也不会将其值保存到 .env 文件中。

Secret 参数

使用 defineSecret() 定义的 Secret 类型的参数表示具有存储在 Cloud Secret Manager 中的值的字符串参数。Secret 参数会检查在 Cloud Secret Manager 中是否存在所需值,并在部署时以交互方式提示您输入新的 Secret 值,而不会针对本地 .env 文件进行检查并在值缺失时将新值写入文件。

以这种方式定义的 Secret 参数必须绑定到应该有权访问它们的各个函数:

const { onRequest } = require("functions/v2/https");
const { defineSecret } = require('firebase-functions/params');
const discordApiKey = defineSecret('DISCORD_API_KEY');

export const posttodiscord = onRequest(
  { secrets: [discordApiKey] },
  (req, res) => {
    const apiKey = discordApiKey.value();
    //…

由于 Secret 的值在函数执行之前会处于隐藏状态,因此您无法在配置函数时使用它们。

环境变量

Cloud Functions for Firebase 支持使用 dotenv 文件格式将 .env 文件中指定的环境变量加载到应用运行时。部署后,环境变量可通过 process.env 接口读取。

如需以这种方式配置环境,请在项目中创建 .env 文件,添加所需的变量,然后进行部署:

  1. functions/ 目录中创建一个 .env 文件:

    # Directory layout:
    #   my-project/
    #     firebase.json
    #     functions/
    #       .env
    #       package.json
    #       index.js
    
  2. 打开 .env 文件进行修改,然后添加所需的键。例如:

    PLANET=Earth
    AUDIENCE=Humans
    
  3. 部署函数并验证是否已加载环境变量:

    firebase deploy --only functions
    # ...
    # i functions: Loaded environment variables from .env.
    # ...
    

部署自定义环境变量后,您的函数代码即可使用 process.env 语法访问这些变量:

// Responds with "Hello Earth and Humans"
exports.hello = onRequest((request, response) => {
  response.send(`Hello ${process.env.PLANET} and ${process.env.AUDIENCE}`);
});

部署多组环境变量

如果您的 Firebase 项目需要一组替代的环境变量(例如预演环境变量与生产环境变量),请创建 .env.<project or alias> 文件并在其中写入项目特定的环境变量。.env 文件和项目专属 .env 文件(如果存在)中的环境变量将包含在所有已部署的函数中。

例如,一个项目可以包含以下三个文件,这些文件中包含的用于开发环境和生产环境的值略有不同:

.env .env.dev .env.prod
PLANET=Earth

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Prod Humans

根据这些不同文件中的值,随函数部署的环境变量集会因目标项目而异:

$ firebase use dev
$ firebase deploy --only functions
i functions: Loaded environment variables from .env, .env.dev.
# Deploys functions with following user-defined environment variables:
#   PLANET=Earth
#   AUDIENCE=Dev Humans

$ firebase use prod
$ firebase deploy --only functions
i functions: Loaded environment variables from .env, .env.prod.
# Deploys functions with following user-defined environment variables:
#   PLANET=Earth
#   AUDIENCE=Prod Humans

预留的环境变量

某些环境变量键已预留给内部使用。请勿在 .env 文件中使用以下任何键:

  • 以 X_GOOGLE_ 开头的所有键
  • 以 EXT_ 开头的所有键
  • 以 FIREBASE_ 开头的所有键
  • 以下列表中的任何键:
  • CLOUD_RUNTIME_CONFIG
  • ENTRY_POINT
  • GCP_PROJECT
  • GCLOUD_PROJECT
  • GOOGLE_CLOUD_PROJECT
  • FUNCTION_TRIGGER_TYPE
  • FUNCTION_NAME
  • FUNCTION_MEMORY_MB
  • FUNCTION_TIMEOUT_SEC
  • FUNCTION_IDENTITY
  • FUNCTION_REGION
  • FUNCTION_TARGET
  • FUNCTION_SIGNATURE_TYPE
  • K_SERVICE
  • K_REVISION
  • PORT
  • K_CONFIGURATION

存储和访问敏感的配置信息

存储在 .env 文件中的环境变量可用于函数配置,但您不应认为它们是存储数据库凭据或 API 密钥等敏感信息的安全方式。如果您将 .env 文件签入源代码控制系统,尤其需要注意这一点。

为了帮助您存储敏感的配置信息,Cloud Functions for Firebase 已与 Google Cloud Secret Manager 集成。此加密服务可以安全地存储配置值,同时仍可让您在需要时轻松从您的函数访问这些数据。

创建和使用 Secret

如需创建 Secret,请使用 Firebase CLI。

如需创建和使用 Secret,请执行以下操作

  1. 从本地项目的根目录中,运行以下命令:

    firebase functions:secrets:set SECRET_NAME

  2. 输入 SECRET_NAME 的值。

    CLI 会回显成功消息并警告您必须部署函数,以使更改生效。

  3. 在部署之前,请确保您的函数代码允许函数使用 runWith 参数访问 Secret:

    const { onRequest } = require("functions/v2/https");
    
      exports.processpayment = onRequest(
        { secrets: ["SECRET_NAME"] },
        (data, context) => {
          const myBillingService = initializeBillingService(
            // reference the secret value
            process.env.SECRET_NAME
          );
          // Process the payment
        }
      );
  4. 部署 Cloud Functions 函数:

    firebase deploy --only functions

现在,您可以像访问任何其他环境变量一样对其进行访问。 相对地,如果另一个没有在 runWith 中指定 Secret 的函数试图访问该 Secret,则会收到一个未定义的值:

exports.anotherendpoint = onRequest((request, response) => {
  response.send(`The secret API key is ${process.env.SECRET_NAME}`);
  // responds with "The secret API key is undefined" because the `runWith` parameter is missing
});

函数部署后便可访问 Secret 值。只有在其 runWith 参数中明确包含 Secret 的函数才能以环境变量的形式访问该 Secret。这有助于确保系统仅在需要时才会提供 Secret 值,从而降低意外泄露 Secret 的风险。

管理 Secret

使用 Firebase CLI 管理您的 Secret。请注意,以这种方式管理 Secret 时,某些 CLI 更改需要您修改和/或重新部署关联的函数。具体而言:

  • 每次为 Secret 设置新值时,您都必须重新部署所有引用该 Secret 的函数,使它们获取最新的值。
  • 如果您删除 Secret,请确保已部署的所有函数均未引用该 Secret。使用已删除的 Secret 值的函数将会失败且无提示。

下面总结了用于 Secret 管理的 Firebase CLI 命令:

# Change the value of an existing secret
firebase functions:secrets:set SECRET_NAME

# View the value of a secret
functions:secrets:access SECRET_NAME

# Destroy a secret
functions:secrets:destroy SECRET_NAME

# View all secret versions and their state
functions:secrets:get SECRET_NAME

# Automatically clean up all secrets that aren't referenced by any of your functions
functions:secrets:prune

对于 accessdestroy 命令,您可以提供可选的版本参数来管理特定版本。例如:

functions:secrets:access SECRET_NAME[@VERSION]

如需详细了解这些操作,请随命令传递 -h 以查看 CLI 帮助。

Secret 的结算方式

Secret Manager 允许您免费拥有 6 个有效的 Secret 版本。这意味着一个 Firebase 项目每月可以免费拥有 6 个 Secret。

默认情况下,Firebase CLI 会在适当的时候(例如,在使用新版本的 Secret 部署函数时)尝试自动销毁未使用的 Secret 版本。此外,您还可以使用 functions:secrets:destroyfunctions:secrets:prune 主动清理未使用的 Secret。

Secret Manager 允许每月对一个 Secret 执行 10,000 次不计费的访问操作。函数实例每次冷启动时仅会读取在其 runWith 参数中指定的 Secret。如果您有许多函数实例都会读取大量 Secret,则您的项目可能会超出此限额,此时系统会按照每 10,000 次访问操作 $0.03 的费率向您收取费用。

如需了解详情,请参阅 Secret Manager 价格

模拟器支持

使用 dotenv 的环境配置可与本地 Cloud Functions 模拟器进行互操作。

使用本地 Cloud Functions 模拟器时,您可以通过设置 .env.local 文件来替换项目的环境变量。.env.local 的内容优先于 .env 和项目特定的 .env 文件。

例如,一个项目可以包含以下三个文件,这些文件中包含的用于开发和本地测试的值会略有不同:

.env .env.dev .env.local
PLANET=Earth

AUDIENCE=Humans

AUDIENCE=Dev Humans AUDIENCE=Local Humans

在本地环境中启动时,模拟器会加载环境变量,如下所示:

  $ firebase emulators:start
  i  emulators: Starting emulators: functions
  # Starts emulator with following environment variables:
  #  PLANET=Earth
  #  AUDIENCE=Local Humans

Cloud Functions 模拟器中的 Secret 和凭据

Cloud Functions 模拟器支持使用 Secret 存储和访问敏感配置信息。默认情况下,模拟器将尝试使用应用默认凭据访问生产 Secret。在某些情况(例如 CI 环境)下,模拟器可能会由于权限限制而无法访问 Secret 值。

与 Cloud Functions 模拟器对环境变量的支持类似,您可以通过设置 .secret.local 文件来替换 Secret 值。这样,您就可以轻松地在本地测试函数,尤其是当您无法访问 Secret 值时。