Статьи

Уведомления о событиях CloudWatch с использованием AWS Lambda

Основным вариантом использования CloudWatchEvents является отслеживание изменений в инфраструктуре AWS. В настоящее время он поддерживает события, генерируемые группами Auto Scaling, EC2, EBS и другими. Чтобы сделать что-то значимое с этими событиями, нам нужен способ их использовать. AWS использует термин targets для обозначения всего, что хочет использовать события, и поддерживает AWS Lambda и некоторые другие.

В этой статье мы увидим, как мы можем настроить функцию AWS Lambda для приема событий из CloudWatch. К концу этой статьи у нас будет функция AWS Lambda, которая будет публиковать уведомления на канале Slack . Однако, поскольку механизм будет универсальным, вы сможете настроить его в соответствии со своими вариантами использования. Давайте начнем!

Настройка и общая информация

Хранилище кода для этой статьи можно найти здесь . Имеет две подкаталоги:

  • functions , который имеет исходный код для лямбда-функций.
  • terraform , который имеет конфигурацию инфраструктуры.

Чтобы следовать, нам понадобится:

  • учетная запись пользователя AWS
  • Terraform
  • Python 3
  • Bash / Powershell / утилиты

Учетная запись пользователя AWS и настройка CLI

Нам потребуется аккаунт AWS со следующими политиками IAM:

  • AWSLambdaFullAccess
  • IAMFullAccess

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

Обратите внимание, что с вас могут взимать плату за демонстрацию.

Terraform

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

Python 3

Наши функции Lambda будут написаны на Python 3, и мы будем использовать Python в сценариях, используемых для развертывания и обновления нашей инфраструктуры AWS.

Bash / Powershell / утилиты

Если вы работаете в Linux / OS X, вам понадобится bash для запуска сценариев. Для Windows вам потребуется powershell . В Linux / OS X мы будем использовать команду zip для создания артефактов развертывания Lambda.

Обзор архитектуры

Общая архитектура решения, которое мы будем строить в этой статье, будет выглядеть следующим образом:

1
AWS CloudWatch event -> Lambda function invoked -> Notifications

Мы сосредоточимся на двух мероприятиях:

  • Событие изменения состояния EC2. Это событие происходит, когда экземпляр AWS EC2 изменяет состояние — запускается новый экземпляр EC2 или завершается существующий экземпляр EC2.
  • Событие работоспособности CloudWatch. Событие работоспособности CloudWatch происходит, когда в вашей учетной записи AWS происходят изменения инфраструктуры, связанные со здоровьем.

Чтобы событие CloudWatch автоматически запускало функцию Lambda, нам нужно настроить cloudwatch rule . Независимо от того, какое событие мы обрабатываем или что мы делаем с этим событием, наша лямбда-функция, которая получает событие, будет иметь одинаковую базовую структуру.

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

1
2
def handler(event, context):
    print(event)

Имя функции — handler , который принимает два параметра: event и context . Объект event имеет полезную нагрузку для события, которое запускает функцию Lambda, а объект context имеет различные метаданные, связанные с конкретным событием.

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

Демонстрация: Уведомление о запуске экземпляра EC2

Лямбда-функция для этого уведомления выглядит следующим образом и сохраняется в файле main.py :

1
2
def handler(event, context):
    print(event)

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

Конфигурация Terraform находится в файле ec2_state_change.tf . Он определяет следующие ключевые ресурсы terraform:

aws_cloudwatch_event_rule

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

1
2
"source": [ "aws.ec2" ],
"detail-type": [ "EC2 Instance State-change Notification" ]

aws_cloudwatch_event_target

Далее мы определяем, что вызывается, когда событие происходит, используя этот ресурс. Основные параметры:

1
2
3
target_id = "InvokeLambda"
arn       = "${aws_lambda_function.ec2_state_change.arn}"
}

Параметр arn указывает имя ресурса Amazon для функции Lambda.

aws_lambda_function

Этот ресурс регистрирует лямбда-функцию и имеет следующие ключевые параметры:

1
2
3
4
5
6
7
8
function_name = "ec2_state_change"
role          = "${aws_iam_role.ec2_state_change_lambda_iam.arn}"
handler       = "main.handler"
runtime       = "python3.6"
 
s3_bucket         = "aws-health-notif-demo-lambda-artifacts"
s3_key            = "ec2-state-change/src.zip"
s3_object_version = "${var.ec2_state_change_handler_version}"

Приведенное выше имя_функции является идентификатором AWS и не имеет никакого отношения к названию вашей функции в вашем коде. Роль IAM-функции лямбда-функции, заданная другим ресурсом, имеет политику sts:AssumeRole по умолчанию и политику, которая позволяет передавать журналы наших функций в CloudWatch.

handler имеет форму <python-module>.<function> и указывает имя функции Python, которое вы хотите вызвать. runtime определяет время выполнения AWS Lambda.

s3_bucket указывает s3_bucket в которой будет жить код нашей s3_key , s3_key — имя ключа для лямбда-кода, а s3_object_version позволяет нам развертывать конкретную версию вышеуказанного объекта.

ec2_state_change_cloudwatch

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

1
2
3
4
action        = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.ec2_state_change.function_name}"
principal     = "events.amazonaws.com"
source_arn    = "${aws_cloudwatch_event_rule.ec2_state_change.arn}"

Загрузка лямбда-функции

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

1
2
3
4
5
6
# Create a .zip of src
$ pushd src
$ zip -r ../src.zip *
$ popd
 
$ aws s3 cp src.zip s3://aws-health-notif-demo-lambda-artifacts/ec2-state-change/src.zip

Мы можем сделать вышеуказанное выполнение частью непрерывного интеграционного конвейера.

Развертывание последней версии кода

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

01
02
03
04
05
06
07
08
09
10
11
$ version=$(aws s3api head-object --bucket aws-health-notif-demo-lambda-artifacts --key ec2-state-change/src.zip)
$ version=$(echo $version | python -c 'import json,sys; obj=json.load(sys.stdin); print(obj["VersionId"])')
 
# Deploy to demo environment
$ pushd ../../terraform/environments/demo
$ ./tf.bash cloudwatch_event_handlers apply -var ec2_state_change_handler_version=$version \
    -target=aws_lambda_function.ec2_state_change \
    -target=aws_lambda_permission.ec2_state_change_cloudwatch \
    -target=aws_cloudwatch_event_target.ec2_state_change \
    -target=aws_iam_role_policy.ec2_state_change_lambda_cloudwatch_logging
$ popd

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

Запуск демо

Чтобы настроить вышеуказанную лямбда-функцию и всю необходимую инфраструктуру в учетной записи AWS, нам просто нужно запустить functions\ec2_state_change\deploy.bash или functions\ec2_state_change\deploy.bash functions\ec2_state_change\deploy.ps1 . Как только это будет сделано, если вы создадите новый экземпляр EC2 или остановите / прекратите работу существующего, вы увидите журналы CloudWatch следующим образом:

1
2
3
4
5
[2018-07-04T09:46:18+10:00] (2018/07/03/[$LATEST]aa226226b6b24a0cae83a948dcc29b95) START RequestId: 4798542c-7f1b-11e8-8493-836165a23514 Version: $LATEST
 
[2018-07-04T09:46:18+10:00] (2018/07/03/[$LATEST]aa226226b6b24a0cae83a948dcc29b95) {'version': '0', 'id': '73c10269-00a0-644d-b92b-820846bb19db', 'detail-type': 'EC2 Instance State-change Notification', 'source': 'aws.ec2', 'account': '033145145979', 'time': '2018-07-03T23:46:16Z', 'region': 'ap-southeast-2', 'resources': ['arn:aws:ec2:ap-southeast-2:033145145979:instance/i-0e1153ece20b77590'], 'detail': {'instance-id': 'i-0e1153ece20b77590', 'state': 'pending'}}
 
[2018-07-04T09:46:18+10:00] (2018/07/03/[$LATEST]aa226226b6b24a0cae83a948dcc29b95) END RequestId: 4798542c-7f1b-11e8-8493-836165a23514

Демо: AWS Health Events -> Slack

Далее мы напишем функцию Lambda, которая будет публиковать события AWS Health на выбранный вами канал Slack. Сначала мы настроим входящий веб-крючок для нашего канала Slack. Пожалуйста, перейдите по этой ссылке, чтобы начать, как вы можете добавить один для своего канала. Если вы выполните настройку, у вас будет URL-адрес, похожий на https://hooks.slack.com/services/string/<string>/<string> . Помимо этого этапа, я предполагаю, что у нас есть этот URL-адрес веб-крючка.

Написание лямбда-функции

Лямбда-функция будет выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import os
import sys
import json
 
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(CWD, "libs"))
 
import requests
 
def handler(event, context):
 
    WEBHOOK_URL = os.getenv("WEBHOOK_URL")
    if not WEBHOOK_URL:
        print("WEBHOOK_URL not defined or empty")
        return
    # see: https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html#health-event-types for event structure
    r = requests.post(
        WEBHOOK_URL,
        json = {'text': '*New AWS Health event* ```{0}```'.format(str(event))}
    )
    print(r)

Конфигурация инфраструктуры

Конфигурация инфраструктуры для этой лямбда-функции точно такая же, как и предыдущая, за исключением aws_cloudwatch_event_rule который определяет event_pattern следующим образом:

1
2
"source": [ "aws.health" ],
"detail-type": [ "AWS Health Event" ]

Развертывание лямбда-функции

Как и в приведенной выше демонстрации изменения состояния EC2, для развертывания вышеуказанной функции мы запустим сценарий развертывания из каталога functions/health_event :

1
$ HEALTH_EVENT_WEBHOOK_URL="<your webhook url>" ./deploy.bash

Это создаст необходимые правила событий CloudWatch и установит цели в дополнение к развертыванию функции Lambda.

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

1
$ aws lambda invoke --invocation-type RequestResponse --function-name health_event --log-type Tail --payload '{"message":"hello"}' outfile.txt

Вы должны увидеть сообщение Slack в вашем настроенном канале:

cloudwatch

Слабое сообщение

Резюме

В этой статье мы узнали, как мы можем настроить функции Lambda в качестве целей для событий CloudWatch. Демонстрация Slack уведомлений работает, но может быть улучшена несколькими способами:

  • URL-адрес webhook может быть зашифрован в состоянии покоя с помощью AWS Secrets Manager.
  • Уведомление может быть обогащено за счет возможности обработки входящих сообщений.

Репозиторий, используемый для этой статьи, доступен здесь .

Следующие ресурсы должны быть полезны для получения дополнительной информации:

См. Оригинальную статью здесь: Уведомления о событиях CloudWatch с использованием AWS Lambda.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.