Настройте свою среду


Часто вам потребуется дополнительная настройка для ваших функций, например ключи стороннего API или настраиваемые параметры. Firebase SDK для Cloud Functions предлагает встроенную конфигурацию среды, упрощающую хранение и извлечение данных такого типа для вашего проекта.

Вы можете выбрать между этими вариантами:

  • Параметризованная конфигурация (рекомендуется для большинства сценариев). Это обеспечивает строго типизированную конфигурацию среды с параметрами, которые проверяются во время развертывания, что предотвращает ошибки и упрощает отладку.
  • Конфигурация переменных среды на основе файлов. При таком подходе вы вручную создаете файл dotenv для загрузки переменных среды.

В большинстве случаев использования рекомендуется параметризованная конфигурация. Этот подход делает значения конфигурации доступными как во время выполнения, так и во время развертывания, а развертывание блокируется, если все параметры не имеют допустимого значения. И наоборот, конфигурация с переменными среды недоступна во время развертывания.

Параметризованная конфигурация

Cloud Functions for Firebase предоставляют интерфейс для декларативного определения параметров конфигурации внутри вашей кодовой базы. Значение этих параметров доступно как во время развертывания функции, при настройке параметров развертывания и времени выполнения, так и во время выполнения. Это означает, что CLI заблокирует развертывание, если все параметры не имеют допустимого значения.

Node.js

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.`);
  }
);

Питон

from firebase_functions import https_fn
from firebase_functions.params import IntParam, StringParam

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")
WELCOME_MESSAGE = StringParam("WELCOME_MESSAGE")

# To use configured parameters inside the config for a function, provide them
# directly. To use them at runtime, call .value() on them.
@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    return https_fn.Response(f'{WELCOME_MESSAGE.value()}! I am a function!')

При развертывании функции с параметризованными переменными конфигурации интерфейс командной строки Firebase сначала пытается загрузить их значения из локальных файлов .env. Если они отсутствуют в этих файлах и не задано default , CLI запросит значения во время развертывания, а затем автоматически сохранит их в файле .env с именем .env.<project_ID> в вашем каталоге functions/ :

$ 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> в систему контроля версий.

Использование параметров в глобальной области видимости

Во время развертывания код вашей функции загружается и проверяется до того, как ваши параметры получат фактические значения. Это означает, что получение значений параметров в глобальной области приводит к сбою развертывания. В случаях, когда вы хотите использовать параметр для инициализации глобального значения, используйте обратный вызов инициализации onInit() . Этот обратный вызов выполняется до запуска каких-либо функций в рабочей среде, но не вызывается во время развертывания, поэтому это безопасное место для доступа к значению параметра.

Node.js

const { GoogleGenerativeAI } = require('@google/generative-ai');
const { defineSecret } = require('firebase-functions/params');
const { onInit } = require('firebase-functions/v2/core');

const apiKey = defineSecret('GOOGLE_API_KEY');

let genAI;
onInit(() => {
  genAI = new GoogleGenerativeAI(apiKey.value());
})

Питон

from firebase_functions.core import init
from firebase_functions.params import StringParam, PROJECT_ID
import firebase_admin
import vertexai

location = StringParam("LOCATION")

x = "hello"

@init
def initialize():
  # Note: to write back to a global, you'll need to use the "global" keyword
  # to avoid creating a new local with the same name.
  global x
  x = "world"
  firebase_admin.initialize_app()
  vertexai.init(PROJECT_ID.value, location.value)

Если вы используете параметры типа Secret , учтите, что они доступны только в процессе функций, привязавших секрет. Если секрет привязан только к некоторым функциям, проверьте, является ли secret.value() ложным, прежде чем использовать его.

Настройка поведения CLI

Параметры можно настроить с помощью объекта Options , который управляет тем, как CLI будет запрашивать значения. В следующем примере задаются параметры для проверки формата номера телефона, предоставления простого варианта выбора и автоматического заполнения параметра выбора из проекта Firebase:

Node.js

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: params.select(["odd", "even"])});

const memory = defineInt("MEMORY", {
  description: "How much memory do you need?",
  input: params.select({ "micro": 256, "chonky": 2048 }),
});

const extensions = defineList("EXTENSIONS", {
  description: "Which file types should be processed?",
  input: params.multiSelect(["jpg", "tiff", "png", "webp"]),
});

const storageBucket = defineString('BUCKET', {
  description: "This will automatically
populate the selector field with the deploying Cloud Projects
storage buckets",
  input: params.PICK_STORAGE_BUCKET,
});

Питон

from firebase_functions.params import (
    StringParam,
    ListParam,
    TextInput,
    SelectInput,
    SelectOptions,
    ResourceInput,
    ResourceType,
)

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")

WELCOME_MESSAGE = StringParam(
    "WELCOME_MESSAGE",
    default="Hello World",
    description="The greeting that is returned to the caller of this function",
)

ONLY_PHONE_NUMBERS = StringParam(
    "PHONE_NUMBER",
    input=TextInput(
        validation_regex="\d{3}-\d{3}-\d{4}",
        validation_error_message="Please enter a phone number in the format XXX-YYY-XXX",
    ),
)

SELECT_OPTION = StringParam(
    "PARITY",
    input=SelectInput([SelectOptions(value="odd"), SelectOptions(value="even")]),
)

STORAGE_BUCKET = StringParam(
    "BUCKET",
    input=ResourceInput(type=ResourceType.STORAGE_BUCKET),
    description="This will automatically populate the selector field with the deploying Cloud Project's storage buckets",
)

Типы параметров

Параметризованная конфигурация обеспечивает строгую типизацию значений параметров, а также поддерживает секреты из Cloud Secret Manager. Поддерживаемые типы:

  • Секрет
  • Нить
  • логическое значение
  • Целое число
  • Плавать
  • Список (Node.js)

Значения параметров и выражения

Firebase оценивает ваши параметры как во время развертывания, так и во время выполнения вашей функции. Из-за этих двойных сред необходимо проявлять особую осторожность при сравнении значений параметров и при их использовании для установки параметров времени выполнения для ваших функций.

Чтобы передать параметр в функцию в качестве параметра времени выполнения, передайте его напрямую:

Node.js

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) => {
    //…

Питон

from firebase_functions import https_fn
from firebase_functions.params import IntParam

MIN_INSTANCES = IntParam("HELLO_WORLD_MIN_INSTANCES")

@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    ...

Кроме того, если вам нужно сравнить параметр, чтобы узнать, какой вариант выбрать, вам нужно будет использовать встроенные компараторы вместо проверки значения:

Node.js

const { onRequest } = require('firebase-functions/v2/https');
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) => {
    //…

Питон

from firebase_functions import https_fn
from firebase_functions.params import IntParam, StringParam

ENVIRONMENT = StringParam("ENVIRONMENT", default="dev")
MIN_INSTANCES = ENVIRONMENT.equals("PRODUCTION").then(10, 0)

@https_fn.on_request(min_instances=MIN_INSTANCES)
def hello_world(req):
    ...

Доступ к параметрам и выражениям параметров, которые используются только во время выполнения, можно получить с помощью их функции value :

Node.js

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.`);
  }
);

Питон

from firebase_functions import https_fn
from firebase_functions.params import StringParam

WELCOME_MESSAGE = StringParam("WELCOME_MESSAGE")

@https_fn.on_request()
def hello_world(req):
    return https_fn.Response(f'{WELCOME_MESSAGE.value()}! I am a function!')

Встроенные параметры

SDK Cloud Functions предлагает три предопределенных параметра, доступных в подпакете firebase-functions/params :

Node.js

  • projectID — облачный проект, в котором выполняется функция.
  • databaseURL — URL-адрес экземпляра базы данных реального времени, связанного с функцией (если включено в проекте Firebase).
  • storageBucket — сегмент облачного хранилища, связанный с функцией (если он включен в проекте Firebase).

Питон

  • PROJECT_ID — облачный проект, в котором выполняется функция.
  • DATABASE_URL — URL-адрес экземпляра базы данных реального времени, связанного с функцией (если включен в проекте Firebase).
  • STORAGE_BUCKET — сегмент облачного хранилища, связанный с функцией (если он включен в проекте Firebase).

Они функционируют как определяемые пользователем строковые параметры во всех отношениях, за исключением того, что, поскольку их значения всегда известны интерфейсу командной строки Firebase, их значения никогда не будут запрашиваться при развертывании и не сохраняться в файлах .env .

Секретные параметры

Параметры типа Secret , определенные с помощью defineSecret() , представляют собой строковые параметры, значения которых хранятся в Cloud Secret Manager. Вместо проверки локального файла .env и записи в него нового значения, если оно отсутствует, секретные параметры проверяются на наличие в Cloud Secret Manager и в интерактивном режиме запрашивают значение нового секрета во время развертывания.

Определенные таким образом секретные параметры должны быть привязаны к отдельным функциям, которые должны иметь к ним доступ:

Node.js

const { onRequest } = require('firebase-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();
    //…

Питон

from firebase_functions import https_fn
from firebase_functions.params import SecretParam

DISCORD_API_KEY = SecretParam('DISCORD_API_KEY')

@https_fn.on_request(secrets=[DISCORD_API_KEY])
def post_to_discord(req):
    api_key = DISCORD_API_KEY.value

Поскольку значения секретов скрыты до выполнения функции, вы не можете использовать их при настройке функции.

Переменные среды

Cloud Functions for Firebase поддерживают формат файла dotenv для загрузки переменных среды, указанных в файле .env , в среду выполнения вашего приложения. После развертывания переменные среды можно прочитать через process.env (в проектах на основе Node.js) или os.environ (в проектах на основе Python).

Чтобы настроить среду таким образом, создайте файл .env в своем проекте, добавьте нужные переменные и разверните:

  1. Создайте файл .env в каталоге functions/ :

    # 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.
    # ...
    

После развертывания ваших пользовательских переменных среды ваш функциональный код сможет получить к ним доступ:

Node.js

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

Питон

import os

@https_fn.on_request()
def hello(req):
    return https_fn.Response(
        f"Hello {os.environ.get('PLANET')} and {os.environ.get('AUDIENCE')}"
    )

Развертывание нескольких наборов переменных среды

Если вам нужен альтернативный набор переменных среды для ваших проектов Firebase (например, промежуточный или производственный), создайте файл .env. <project or alias > и запишите туда переменные среды, специфичные для проекта. Переменные среды из .env и файлов .env , специфичных для проекта (если они существуют), будут включены во все развернутые функции.

Например, проект может включать в себя эти три файла, содержащие немного разные значения для разработки и производства:

.env .env.dev .env.prod
ПЛАНЕТА=Земля

АУДИТОРИЯ=Люди

АУДИТОРИЯ = Люди-разработчики АУДИТОРИЯ=Продюсеры

Учитывая значения в этих отдельных файлах, набор переменных среды, развернутых с вашими функциями, будет варьироваться в зависимости от вашего целевого проекта:

$ 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_MEMORY_МБ
  • FUNCTION_TIMEOUT_SEC
  • FUNCTION_IDENTITY
  • FUNCTION_REGION
  • FUNCTION_TARGET
  • FUNCTION_SIGNATURE_TYPE
  • К_СЕРВИС
  • К_РЕВИЗИЯ
  • ПОРТ
  • K_КОНФИГУРАЦИЯ

Храните и получайте доступ к конфиденциальной информации о конфигурации.

Переменные среды, хранящиеся в файлах .env можно использовать для конфигурации функций, но не следует рассматривать их как безопасный способ хранения конфиденциальной информации, такой как учетные данные базы данных или ключи API. Это особенно важно, если вы проверяете свои файлы .env в системе контроля версий.

Чтобы помочь вам хранить конфиденциальную информацию о конфигурации, Cloud Functions for Firebase интегрируется с Google Cloud Secret Manager . Эта зашифрованная служба надежно хранит значения конфигурации, но при этом обеспечивает легкий доступ к вашим функциям, когда это необходимо.

Создайте и используйте секрет

Чтобы создать секрет, используйте интерфейс командной строки Firebase .

Чтобы создать и использовать секрет:

  1. Из корня локального каталога проекта выполните следующую команду:

    firebase functions:secrets:set SECRET_NAME

  2. Введите значение SECRET_NAME .

    Интерфейс командной строки отображает сообщение об успехе и предупреждает, что вам необходимо развернуть функции, чтобы изменения вступили в силу.

  3. Перед развертыванием убедитесь, что код вашей функции позволяет функции получить доступ к секрету с помощью параметра runWith :

    Node.js

    const { onRequest } = require('firebase-functions/v2/https');
    
    exports.processPayment = onRequest(
      { secrets: ["SECRET_NAME"] },
      (req, res) => {
        const myBillingService = initializeBillingService(
          // reference the secret value
          process.env.SECRET_NAME
        );
        // Process the payment
      }
    );

    Питон

    import os
    from firebase_functions import https_fn
    
    @https_fn.on_request(secrets=["SECRET_NAME"])
    def process_payment(req):
        myBillingService = initialize_billing(key=os.environ.get('SECRET_NAME'))
        # Process the payment
        ...
    
  4. Развертывание Cloud Functions :

    firebase deploy --only functions

    Теперь вы сможете получить к ней доступ, как к любой другой переменной среды. И наоборот, если другая функция, не указывающая секрет в runWith попытается получить доступ к секрету, она получит неопределенное значение:

    Node.js

    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
    });
    

    Питон

    @https_fn.on_request()
    def another_endpoint(req):
        return https_fn.Response(f"The secret API key is {os.environ.get("SECRET_NAME")}")
        # Responds with "The secret API key is None" because the `secrets` parameter is missing.
    

После развертывания вашей функции она получит доступ к секретному значению. Только функции, которые специально включают секрет в свой параметр runWith будут иметь доступ к этому секрету как к переменной среды. Это поможет вам убедиться, что секретные значения доступны только там, где они необходимы, что снижает риск случайной утечки секрета.

Управление секретами

Используйте интерфейс командной строки Firebase для управления своими секретами. Управляя секретами таким образом, имейте в виду, что некоторые изменения CLI требуют изменения и/или повторного развертывания связанных функций. Конкретно:

  • Всякий раз, когда вы устанавливаете новое значение для секрета, вы должны повторно развернуть все функции, ссылающиеся на этот секрет, чтобы они могли получить последнее значение.
  • Если вы удаляете секрет, убедитесь, что ни одна из ваших развернутых функций не ссылается на этот секрет. Функции, использующие удаленное секретное значение, завершатся автоматически.

Вот краткое описание команд 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

Для команд access и destroy вы можете указать необязательный параметр версии для управления конкретной версией. Например:

functions:secrets:access SECRET_NAME[@VERSION]

Для получения дополнительной информации об этих операциях передайте -h с командой для просмотра справки CLI.

Как выставляются счета за секреты

Secret Manager позволяет бесплатно использовать 6 активных секретных версий . Это означает, что вы можете бесплатно иметь 6 секретов в месяц в проекте Firebase.

По умолчанию интерфейс командной строки Firebase пытается автоматически уничтожить неиспользуемые версии секрета, когда это необходимо, например, когда вы развертываете функции с новой версией секрета. Кроме того, вы можете активно очищать неиспользуемые секреты, используя functions:secrets:destroy и functions:secrets:prune .

Secret Manager допускает 10 000 неоплачиваемых ежемесячных операций доступа к секрету. Экземпляры функций считывают только секреты, указанные в их параметре runWith , каждый раз при холодном запуске. Если у вас много экземпляров функций, читающих много секретов, ваш проект может превысить эту норму, и в этот момент с вас будет взиматься плата в размере 0,03 доллара США за 10 000 операций доступа.

Дополнительную информацию см. в разделе «Цены Secret Manager .

Поддержка эмулятора

Конфигурация среды с помощью dotenv предназначена для взаимодействия с локальным эмулятором Cloud Functions .

При использовании локального эмулятора Cloud Functions вы можете переопределить переменные среды для своего проекта, настроив файл .env.local . Содержимое .env.local имеет приоритет над .env и файлом .env специфичным для проекта.

Например, проект может включать в себя эти три файла, содержащие немного разные значения для разработки и локального тестирования:

.env .env.dev .env.local
ПЛАНЕТА=Земля

АУДИТОРИЯ=Люди

АУДИТОРИЯ = Люди-разработчики АУДИТОРИЯ=Местные жители

При запуске в локальном контексте эмулятор загружает переменные среды, как показано:

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

Секреты и учетные данные в эмуляторе Cloud Functions

Эмулятор Cloud Functions поддерживает использование секретов для хранения и доступа к конфиденциальной информации о конфигурации . По умолчанию эмулятор попытается получить доступ к вашим производственным секретам, используя учетные данные приложения по умолчанию . В определенных ситуациях, например в средах CI, эмулятор может не получить доступ к секретным значениям из-за ограничений разрешений.

Аналогично поддержке переменных среды эмулятором Cloud Functions , вы можете переопределить значения секретов, настроив файл .secret.local . Это упрощает локальное тестирование ваших функций, особенно если у вас нет доступа к секретному значению.

Миграция из конфигурации среды

Если вы использовали конфигурацию среды с помощью functions.config , вы можете перенести существующую конфигурацию как переменные среды (в формате dotenv ). Интерфейс командной строки Firebase предоставляет команду экспорта, которая выводит конфигурацию каждого псевдонима или проекта, указанного в файле .firebaserc вашего каталога (в примере ниже, local , dev и prod ) в виде файлов .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 , как показано в разделе «Переменные среды» .