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


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

Вы можете выбрать один из следующих вариантов:

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

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

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

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

Чтобы определить параметры в вашем коде, следуйте этой модели:

const functions = require('firebase-functions/v1');
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 запросит значения во время развертывания, а затем автоматически сохранит их значения в файле .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() . Этот обратный вызов выполняется до запуска любых функций в производстве, но не вызывается во время развертывания, поэтому это безопасное место для доступа к значению параметра.

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

  const apiKey = defineSecret('GOOGLE_API_KEY');

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

Настроить поведение 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. Поддерживаемые типы:

  • Секрет
  • Нить
  • Булев
  • Целое число
  • Плавать

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

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

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

const functions = require('firebase-functions/v1');
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/v1');
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/v1');
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 — облачный проект, в котором запущена функция.
  • databaseURL — URL-адрес экземпляра базы данных реального времени, связанного с функцией (если включено в проекте Firebase).
  • storageBucket — контейнер облачного хранилища, связанный с функцией (если включен в проекте Firebase).

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

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

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

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

const functions = require('firebase-functions/v1');
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();
    //…

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

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

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

Чтобы настроить среду таким образом, создайте файл .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.
    # ...
    

После развертывания ваших пользовательских переменных среды ваш код функции может получить к ним доступ с помощью синтаксиса 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 (например, staging vs production), создайте файл .env. <project or alias > и запишите в него переменные среды, специфичные для вашего проекта. Переменные среды из .env и специфичные для проекта файлы .env (если они существуют) будут включены во все развернутые функции.

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

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

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

АУДИТОРИЯ=Разработчики АУДИТОРИЯ=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
  • ТОЧКА_ВХОДА
  • GCP_ПРОЕКТ
  • GCLOUD_ПРОЕКТ
  • GOOGLE_CLOUD_PROJECT
  • ТИП_ТРИГГЕРА_ФУНКЦИИ
  • ИМЯ_ФУНКЦИИ
  • FUNCTION_MEMORY_MB
  • FUNCTION_TIMEOUT_SEC
  • ФУНКЦИЯ_ИДЕНТИЧНОСТЬ
  • ФУНКЦИОНАЛЬНЫЙ_РЕГИОН
  • ФУНКЦИЯ_ЦЕЛЬ
  • ТИП_ПОДПИСИ_ФУНКЦИИ
  • K_СЕРВИС
  • K_REVISION
  • ПОРТ
  • K_КОНФИГУРАЦИЯ

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

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

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

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

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

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

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

    firebase functions:secrets:set SECRET_NAME

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

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

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

    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 попытается получить доступ к секрету, она получит неопределенное значение:

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

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

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

Используйте Firebase CLI для управления секретами. При таком управлении секретами помните, что некоторые изменения 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 CLI пытается автоматически уничтожить неиспользуемые версии секрета, когда это уместно, например, когда вы развертываете функции с новой версией секрета. Кроме того, вы можете активно очищать неиспользуемые секреты с помощью 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 CLI предоставляет команду экспорта, которая выводит конфигурацию каждого псевдонима или проекта, перечисленного в файле .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 , как показано в Upgrade to 2nd gen .

Конфигурация среды

Настройка конфигурации среды с помощью CLI

Для хранения данных среды можно использовать команду firebase functions:config:set в Firebase CLI . Каждый ключ может быть разделен на пространство имен с помощью точек для группировки связанных конфигураций. Помните, что в ключах допускаются только строчные символы ; заглавные символы не допускаются.

Например, чтобы сохранить идентификатор клиента и ключ API для «Some Service», вы можете выполнить:

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"
  }
}

Эта функциональность основана на API конфигурации Google Cloud Runtime .

Используйте functions.config для доступа к конфигурации среды в функции

Некоторые конфигурации автоматически предоставляются в зарезервированном пространстве имен firebase . Конфигурация среды становится доступной внутри вашей работающей функции через functions.config() . Чтобы использовать конфигурацию выше, ваш код может выглядеть следующим образом:

const functions = require('firebase-functions/v1');
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/v1');
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://DATABASE_NAME.firebaseio.com',
  storageBucket: 'PROJECT_ID.firebasestorage.app',
  projectId: 'PROJECT_ID'
}

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

Эта конфигурация применяется автоматически при инициализации Firebase Admin SDK без аргументов. Если вы пишете функции на JavaScript, инициализируйте так:

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

Если вы пишете функции на TypeScript, инициализируйте их следующим образом:

import * as functions from 'firebase-functions/v1';
import * as admin from 'firebase-admin';
import 'firebase-functions/v1';
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);