Недавно я написал статью об AWS Lambda. Моя статья послужила кратким введением в сервис и его функциональные возможности. В этой статье мы рассмотрим еще один ценный актив в вашем бессерверном арсенале AWS: пошаговые функции и, в частности, конечные автоматы.

В июле 2019 года AWS решила предоставить нам доступ к своему API CloudWatch Events с ребрендингом событий CloudWatch на EventBridge, который является их бессерверным решением для шины событий. В сочетании с Step Functions это довольно трансформирует.

Пошаговые функции позволяют организовать и отделить лямбда-функции от конечных автоматов. Поток последовательных или параллельных рабочих нагрузок может быть определен в состояниях для создания несвязанных архитектур, управляемых событиями, для ваших процессов. Подходящими рабочими нагрузками для конечных автоматов являются микросервисы, автоматизация ИТ/DevOps, ETL, бизнес-процессы, потоковая передача данных и многое другое.

В State Machines каждый шаг вашего рабочего процесса представлен состоянием. Каждое состояние имеет вход и выход. Вход состояния является выходом предыдущего состояния. Вы можете указать пользовательский ввод для первого состояния при запуске нового выполнения. Входные и выходные данные должны быть полезными данными JSON.

Большим преимуществом конечного автомата является наблюдаемость. С Workflow Studio вы можете визуально проектировать свои процессы, перетаскивая блоки «Действия» для своих состояний и блоков «Поток».

Пример блоков «Действия»:

  • Вызвать лямбда-функцию
  • Общедоступно для темы SNS
  • Запустить задачу в ECS
  • Выполнить задание в Glue
  • Интеграция со многими другими сервисами AWS

Пример блоков «Поток»:

  • Блок выбора для логики if-then-else
  • Параллельный блок для параллелизма
  • Блок Map для логики for-each и динамического параллелизма
  • Блок успеха для обработки успеха
  • Блок Fail для обработки ошибок

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

В качестве простого примера давайте создадим конечный автомат, который находит все работающие инстансы EC2 в первых трех регионах AWS (в соответствии с ответом API description_regions).

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

  • Первая функция Lambda извлечет и вернет первые три региона AWS. Регионы AWS будут первой выходной полезной нагрузкой штата.
  • Затем мы получим все запущенные экземпляры для каждого региона, вызвав лямбда-функцию для каждого из первых трех регионов AWS. Выполнение будет распараллелено, как только завершится выполнение первой лямбда-функции, извлекающей первые три региона. Для каждого вызова Lambda мы будем соответствующим образом фильтровать выходные данные предыдущего состояния по регионам.

Все мои функции Lambda будут на Python. Обратите внимание, что в этом случае мы могли бы легко использовать блок Map flow для динамического перебора регионов, но для визуализации происходящего мы будем использовать три параллельные ветви, экземпляры которых создадим вручную.

Давайте создадим первую лямбда-функцию: retrieve_regions.

import json
import boto3
def describe_regions():
    regions = []
ec2_client = boto3.client(
        "ec2",
        "us-east-1"
        )
    
    response = ec2_client.describe_regions()
for r in response['Regions'][:3]:
        regions.append(r['RegionName'])
        
    return regions
def lambda_handler(event, context):
    regions = describe_regions()
    
    response = {}
    
    i = 1
    
    for r in regions:
        index = "Region" + str(i)
        response[index] = {}
        response[index]['Region'] = r
        i+=1
        
    return response

Эта функция вернет вложенный словарь с первыми тремя регионами AWS:

{
  "Region1": {
    "Region": "eu-north-1"
  },
  "Region2": {
    "Region": "ap-south-1"
  },
  "Region3": {
    "Region": "eu-west-3"
  }
}

Затем давайте создадим наш конечный автомат и сопоставим первое состояние с вызовом нашей лямбда-функции retrieve_regions в Workflow Studio:

Когда мы запускаем State Machine, мы можем визуализировать ввод и вывод первого состояния. Как упоминалось ранее, выходные данные состояния будут входными данными следующего состояния:

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

Теперь давайте создадим вторую региональную лямбда-функцию get_running_instances:

import json
import boto3
def get_running_instances(region):
    ec2_client = boto3.client(
        "ec2",
        region
        )
        
    instances = ec2_client.describe_instances()
    
    ids = []
    
    for instance in instances['Reservations']:
        for i in instance['Instances']:
            ids.append(i['InstanceId'])
        
    return ids
def lambda_handler(event, context):
    region = event["Region"]
    instances = get_running_instances(region)
    return instances

Изменение нашего конечного автомата:

Изменение входной полезной нагрузки для региона 1 путем установки для InputPath значения $.Region1 для фильтрации первого региона:

Сделайте то же самое для региона 2 и региона 3. Задайте для параметра InputPath значение $.Region2 для ПОЛУЧЕНИЯ РАБОТАЮЩЕГО ЭКЗЕМПЛЯРА РЕГИОН 2, а для параметра InputPath — значение $.Region3 для ПОЛУЧЕНИЯ РАБОТАЮЩЕГО ЭКЗЕМПЛЯРА РЕГИОН 3.

При добавлении вызова Lambda в вашу машину состояний вы также должны изменить роль выполнения и добавить новую функцию Lambda в эту роль.

Для целей этого упражнения я развернул экземпляр EC2 в каждом из первых трех регионов. Тестирование выполнения нашего конечного автомата:

Я надеюсь, что этот пример послужил хорошим введением в построение несвязанных рабочих процессов в конечных автоматах и ​​что вы сможете применить то, что было продемонстрировано, для разработки собственных критически важных бизнес-процессов или других приложений!