Источник: Nuances of Programming
Cloud Functions — это масштабируемая реализация от Google Cloud Platform (GCP) архитектурного шаблона function-as-a-service, FaaS («функция как услуга») с оплатой по мере использования, позволяющая запускать код без управления сервером.
Ради большего удобства восприятия напишем Cloud Function на Python, хотя это можно сделать также на Node.js, Go, Java, .NET, Ruby и PHP. Так что смело выбирайте любимый язык и вперед!
Terraform — это инструмент разработки, применяющий подход Infrastructure as Code (IaC), то есть «инфраструктура как код». Он позволяет создавать и менять инфраструктуру, а также обновлять ее версии безопасно, эффективно и с возможностью воспроизводимости. В Terraform используется декларативный способ описания инфраструктуры. Для этого облачные API (GCP, AWS, Azure…) переносятся в конфигурационные файлы.
В итоге, загрузив файл в Google Cloud Storage bucket, мы запустим Cloud Function.
Что потребуется?
- проект Google Cloud Platform, настроенный и привязанный к учетной записи для выставления счетов. Обязательно активируем API для Cloud Functions.
Затем проходим аутентификацию в GCP на локальном компьютере, введя в терминале gcloud auth application-default login.
Цели
Цель — с помощью Terraform развернуть в GCP-проекте:
- bucket для загрузки файлов;
- bucket для хранения исходного кода Cloud Function;
- Cloud Function, которая запускается каждый раз, когда файл загружается в первый bucket, и исходный код которой находится во втором bucket.
Структура проекта
Создаем новую папку cloud_function_project, а в ней — файлы:
.cloud_function_project/
│
├── terraform/
│ │
│ ├── backend.tf
│ ├── function.tf
│ ├── main.tf
│ ├── storage.tf
│ └── variables.tf
│
└── src/
│
├── main.py
└── requirements.txt
Оставим их пока пустыми и, прежде чем начинать заполнять, кратко разберем каждый из них.
В папке src находится исходный код Cloud Function. Структура src характерна для Python:
- main.py: исходный код Cloud Functions.
- requirements.txt: список библиотек Python для запуска main.py (здесь они нам не понадобятся).
В папке terraform содержатся конфигурационные файлы среды для развертывания:
- main.tf: основные объявления среды.
- variables.tf: определение переменных.
- storage.tf: объявление Google Cloud Storage bucket.
- function.tf: объявление Cloud Function.
Создаем Cloud Function
Написание и запуск Cloud Function здесь не так важны. Развернем функцию, в которой будет записываться полезная информация о файле, загруженном в bucket для отслеживания:
def hello_gcs(event, context):
"""Background Cloud Function to be triggered by Cloud Storage.
This generic function logs relevant data when a file is changed.
Args:
event (dict): The dictionary with data specific to this type of event.
The `data` field contains a description of the event in
the Cloud Storage `object` format described here:
https://cloud.google.com/storage/docs/json_api/v1/objects#resource
context (google.cloud.functions.Context): Metadata of triggering event.
Returns:
None; the output is written to Stackdriver Logging
"""
print('Event ID:' , context.event_id)
print('Event type:', context.event_type)
print('Bucket:', event['bucket'])
print('File:', event['name'])
print('Metageneration:', event['metageneration'])
print('Created:', event['timeCreated'])
print('Updated:', event['updated'])
# пустой
Примечание № 1: эта функция не имеет необходимых требований, поэтому requirements.txt остается пустым.
Примечание № 2: это Cloud Function для Python, для других языков источник кода можно поменять.
Создаем инфраструктуру в Terraform
Backend
Начнем объявлять среду, указав бэкенд Terraform. Выбираем local, чтобы все файлы состояния сохранялись в локальном каталоге. Можно также выбрать бэкенд gcs, но не будем усложнять:
terraform {
backend "local" {}
}
Variables
Объявляем переменные, используемые в файлах Terraform. Нужно поменять переменную project_id на идентификатор проекта, в котором будут развернуты ресурсы. region тоже можно поменять:
variable "project_id" {
default = "<YOUR-PROJECT-ID>"
}
variable "region" {
default = "europe-west1"
}
Main
Объявляем подключение к провайдеру Google:
provider "google" {
project = var.project_id
region = var.region
}
Google Cloud Storage
Объявляем один Google Cloud Storage bucket для хранения кода Cloud Function, а другой — для загрузки файлов:
resource "google_storage_bucket" "function_bucket" {
name = "${var.project_id}-function"
location = var.region
}
resource "google_storage_bucket" "input_bucket" {
name = "${var.project_id}-input"
location = var.region
}
Примечание № 3: уникальность названий bucket обеспечивается префиксами project_id.
Google Cloud Function
Последний шаг: объявляем Cloud Function. Для этого сжимаем исходный код в zip-файл и загружаем его в bucket для хранения. Затем получаем доступ к исходному коду при создании Cloud Function с помощью Terraform.
Примечание № 4: файл длинноват, так что для понимания логики смотрите комментарии:
# Создает архив исходного кода в виде сжатого .zip file.
data "archive_file" "source" {
type = "zip"
source_dir = "../src"
output_path = "/tmp/function.zip"
}
# Добавляем zip-файл с исходным кодом в bucket для Cloud Function
resource "google_storage_bucket_object" "zip" {
source = data.archive_file.source.output_path
content_type = "application/zip"
# Добавляем к контрольной сумме MD5 содержимого файлов,
# чтобы после изменения zip-файл обновлялся принудительно
name = "src-${data.archive_file.source.output_md5}.zip"
bucket = google_storage_bucket.function_bucket.name
# Зависимости выводятся автоматически, поэтому эти строки можно удалить
depends_on = [
google_storage_bucket.function_bucket, # объявленный в «storage.tf»
data.archive_file.source
]
}
# Создаем Cloud Function, запускаемую с помощью события «Finalize» в bucket
resource "google_cloudfunctions_function" "function" {
name = "function-trigger-on-gcs"
runtime = "python37" # можно поменять, конечно
# Получаем исходный код Cloud Function в zip-файле
source_archive_bucket = google_storage_bucket.function_bucket.name
source_archive_object = google_storage_bucket_object.zip.name
# Должно совпадать с именем функции в исходном коде Cloud Function «main.py»
entry_point = "hello_gcs"
#
event_trigger {
event_type = "google.storage.object.finalize"
resource = "${var.project_id}-input"
}
# Зависимости выводятся автоматически, поэтому эти строки можно удалить
depends_on = [
google_storage_bucket.function_bucket, # объявленный в «storage.tf»
google_storage_bucket_object.zip
]
}
Развёртываем среду
К развертыванию все готово. Переходим в корневой каталог проекта в терминале, затем в корневой каталог папки terraform:
$ cd ./terraform
Инициализируем код, чтобы загрузить указанные в нем требования:
$ terraform init
Просматриваем изменения:
$ terraform plan
Принимаем их и применяем к реальной инфраструктуре:
$ terraform apply
Тестируем Cloud Function
Проверим, что все работает нормально:
- Видим оба bucket: <YOUR-PROJECT-ID>-function и <YOUR-PROJECT-ID>-input. Нажимаем на bucket <YOUR-PROJECT-ID>-input и, загрузив в него любой файл, запускаем Cloud Function.
- Чтобы убедиться, что запуск произошел, переходим в список Cloud Functions. Там должна быть Cloud Function с именем function-trigger-on-gcs. Нажимаем на этом названии и переходим на вкладку LOGS, чтобы увидеть, что Cloud Function запущена с помощью загруженного нами файла.
Что дальше?
Процесс развертывания Cloud Function можно дополнить, например:
Читайте также:
Перевод статьи Axel Thevenot: Deploy Cloud Functions on GCP with Terraform