配置您的环境


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

您可以从以下三个选项中进行选择:

  • 参数化配置(推荐用于大多数情况)。这是一种强类型的环境配置方法,会在部署时对参数进行验证,这样可以防止一些错误的发生并简化调试过程。
  • 基于文件的环境变量配置。如果使用此方法,您可以手动创建用于加载环境变量的 dotenv 文件。
  • 使用 Firebase CLI 和 functions.config(仅限 Cloud Functions[第 1 代])的运行时环境配置

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

参数化配置

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

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

const functions = require('firebase-functions');
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 = functions.runWith({ minInstances: minInstancesConfig}).https.onRequest(
  (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 functions = require('firebase-functions');
const { defineInt} = require('firebase-functions/params');
const minInstancesConfig = defineInt('HELLO\_WORLD\_MININSTANCES');

export const helloWorld = functions.runWith({ minInstances: minInstancesConfig}).https.onRequest(
  (req, res) => {
    //…

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

const functions = require('firebase-functions');
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 = functions.runWith({ minInstances: minInstancesConfig}).https.onRequest(
  (req, res) => {
    //…

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

const functions = require('firebase-functions');
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 = functions.https.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 functions = require('firebase-functions');
const { defineSecret } = require('firebase-functions/params');
const discordApiKey = defineSecret('DISCORD_API_KEY');

export const postToDiscord = functions.runWith({ secrets: [discordApiKey] }).https.onRequest(
  (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 = functions.https.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:

    exports.processPayment = functions
      // Make the secret available to this function
      .runWith({ secrets: ["SECRET_NAME"] })
      .onCall((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 = functions.https.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 值时。

从环境配置迁移

如果您一直将环境配置与 functions.config 搭配使用,则可以将现有配置作为环境变量(采用 dotenv 格式)迁移。Firebase CLI 提供了一条导出命令,用于输出您目录下 .firebaserc 文件中列出的每个别名或项目(下面示例中的 localdevprod)的配置作为 .env 文件。

如需迁移,请使用 firebase functions:config:export 命令导出现有的环境配置:

firebase functions:config:export
i  Importing configs from projects: [project-0, project-1]
⚠  The following configs keys could not be exported as environment variables:

⚠  project-0 (dev):
    1foo.a => 1FOO\_A (Key 1FOO\_A must start with an uppercase ASCII letter or underscore, and then consist of uppercase ASCII letters, digits, and underscores.)

Enter a PREFIX to rename invalid environment variable keys: CONFIG\_
✔  Wrote functions/.env.prod
✔  Wrote functions/.env.dev
✔  Wrote functions/.env.local
✔  Wrote functions/.env

请注意,在某些情况下,系统会提示您输入前缀以重命名导出的环境变量键。这是因为并非所有配置都可以自动转换,原因可能是它们无效或者是预留的环境变量键

我们建议您在部署函数或将 .env 文件签入源代码控制系统之前仔细检查生成的 .env 文件的内容。如果有任何值属于敏感信息且不应泄露,请将其从 .env 文件中移除,并改为将其安全地存储在 Secret Manager 中。

您还需要更新函数代码。任何使用 functions.config 的函数现在都需要改用 process.env,如升级到第 2 代部分所示。

环境配置

firebase-functions v3.18.0 中开始支持环境变量之前,我们推荐使用 functions.config() 来配置环境。此方法仍受支持,但我们建议所有新项目改用环境变量,因为它们更易于使用并且可以提高代码的可移植性。

使用 CLI 设置环境配置

如需存储环境数据,您可以使用 Firebase CLI 中的 firebase functions:config:set 命令。 每个键都可以使用句点设置命名空间,以便将相关配置组合在一起。请注意,键只能使用小写字符;请勿使用大写字符。

例如,要存储“某些服务”的客户端 ID 和 API 密钥,您可以运行:

firebase functions:config:set someservice.key="THE API KEY" someservice.id="THE CLIENT ID"

检索当前环境配置

要检查当前存储在项目的环境配置中的内容,您可以使用 firebase functions:config:get。它会输出如下 JSON:

{
  "someservice": {
    "key":"THE API KEY",
    "id":"THE CLIENT ID"
  }
}

此功能基于 Google Cloud Runtime Configuration API

在函数中使用 functions.config 访问环境配置

系统在预留的 firebase 命名空间下自动提供了部分配置。通过 functions.config() 可在运行的函数内使用环境配置。 要使用上述配置,代码可能如下所示:

const functions = require('firebase-functions');
const request = require('request-promise');

exports.userCreated = functions.database.ref('/users/{id}').onWrite(event => {
  let email = event.data.child('email').val();

  return request({
    url: 'https://someservice.com/api/some/call',
    headers: {
      'X-Client-ID': functions.config().someservice.id,
      'Authorization': `Bearer ${functions.config().someservice.key}`
    },
    body: {email: email}
  });
});

使用环境配置来初始化模块

某些 Node 模块无需任何配置即可使用。其他模块需要额外的配置才能正确初始化。我们建议您将此配置存储在环境配置变量中,而非将其硬编码。这有助于提高代码的可移植性,从而使您可以开放应用的源代码,或在生产版本和预演版本之间轻松切换。

例如,如需使用 Slack Node SDK 模块,您可以这样编写代码:

const functions = require('firebase-functions');
const IncomingWebhook = require('@slack/client').IncomingWebhook;
const webhook = new IncomingWebhook(functions.config().slack.url);

在部署之前,请设置 slack.url 环境配置变量:

firebase functions:config:set slack.url=https://hooks.slack.com/services/XXX

其他环境命令

  • firebase functions:config:unset key1 key2 从配置中移除指定的键
  • firebase functions:config:clone --from <fromProject> 将其他项目的环境克隆到当前活动项目中。

自动填充的环境变量

有一些环境变量会自动填充到函数运行时和本地模拟的函数中,其中包括 Google Cloud 填充的环境变量以及特定于 Firebase 的环境变量:

process.env.FIREBASE_CONFIG:提供以下 Firebase 项目配置信息:

{
  databaseURL: 'https://databaseName.firebaseio.com',
  storageBucket: 'projectId.appspot.com',
  projectId: 'projectId'
}

当您不带任何参数初始化 Firebase Admin SDK 时,系统会自动应用此配置。如果您使用 JavaScript 编写函数,请按如下所示进行初始化:

const admin = require('firebase-admin');
admin.initializeApp();

如果您使用 TypeScript 编写函数,请按如下所示进行初始化:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import 'firebase-functions';
admin.initializeApp();

如果您需要使用服务帐号凭据以默认项目配置初始化 Admin SDK,则可以从文件加载凭据并将它们添加到 FIREBASE_CONFIG 中,如下所示:

serviceAccount = require('./serviceAccount.json');

const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG);
adminConfig.credential = admin.credential.cert(serviceAccount);
admin.initializeApp(adminConfig);