Como enviar notificações push de uma função Lambda

Neste artigo, mostrarei uma maneira simples e eficiente de enviar notificações push usando uma função Lambda e o ntfy.

Para ilustrar isso, criaremos uma função Lambda que envia uma notificação toda vez que um notebook SageMaker ficar em execução por muito tempo (o que acontece comigo mais frequentemente do que gostaria 😅).

Existem vários métodos para enviar notificações para dispositivos móveis. Para aplicações de nível empresarial, eu normalmente optaria por uma integração usando o AWS SNS, juntamente com uma solução application-to-person ou application-to-application. No entanto, como estou implementando isso em minha conta pessoal da AWS, escolhi uma solução mais simples: ntfy.

ntfy

ntfy (pronuncia-se notifai) é um serviço de notificação gratuito que permite enviar notificações para vários dispositivos através de uma API REST.

Para consumir as notificações, o ntfy oferece um aplicativo para Android e iOS, aplicações para desktop e um aplicativo web acessível em https://ntfy.sh/app (e o melhor, sem necessidade de login 😉).

O que eu mais gostei no ntfy pessoalmente é sua simplicidade. Não há necessidade de criar uma conta ou aprender APIs obscuras. Basta visitar o site deles, escolher um nome do tópico (canal de comunicação), se inscrever no tópico a partir de um cliente, e você está pronto para começar a enviar notificações via requisições POST.

Como usar o ntfy

Para começar, escolha um aplicativo cliente - no meu caso, eu instalei o aplicativo iOS. Em seguida, inscreva-se em um tópico específico; para este exemplo, usei blogpostdemo. Depois de configurar o aplicativo cliente, você pode começar a enviar notificações push usando requisições POST. Os exemplos abaixo demonstram como enviar essas requisições usando curl e Python.

curl:

curl -d "Any message you want 😀" ntfy.sh/blogpostdemo

Python:

from urllib import request

data = "Any message you want 😀".encode('utf-8')
req = request.Request("https://ntfy.sh/blogpostdemo", data=data)
request.urlopen(req) # Envia a requisição

Todos os tópicos são públicos, considere um nome complexo para evitar conflitos. Ou hospede o ntfy você mesmo.

Depois de enviar a requisição POST, você receberá uma notificação como a abaixo.

Notificação push disparada pelo ntfy.

Você também pode adicionar outros tipos de mídia à sua notificação, como imagens, ícones e outros anexos. O tamanho máximo para qualquer anexo é 15MB. Você pode encontrar informações mais detalhadas aqui.

Arquitetura

A arquitetura é bastante simples. Uma função Lambda é acionada pelo EventBridge a cada 1 hora e verifica se há algum notebook SageMaker em execução por mais de 3 horas. Esses valores de tempo podem ser ajustados conforme necessário. Se um notebook em execução por muito tempo for detectado, a função Lambda enviará uma notificação para um tópico ntfy. O ntfy por sua vez enviará essa notificação para os aplicativos clientes.

Diagrama de arquitetura para a integração usando AWS Lambda e ntfy.

Implementação

Função Lambda

Para o ambiente de execução, usaremos o Python 3.12, mas outras versões recentes também devem funcionar. Essa implementação não requer nenhum módulo adicional. Você pode ver o código completo na seção abaixo.

from urllib import request
import boto3
from datetime import timedelta, datetime, timezone

sagemaker_client = boto3.client("sagemaker")


def lambda_handler(event, context):
    hours_delta = 3
    topic_name = "savemaker"
    current_time = datetime.now()

    # Define o delta de tempo de X horas.
    x_hs_ago = current_time - timedelta(hours=hours_delta)

    # Itera sobre todos os notebooks e filtra os que estão em execução há mais de 3 horas
    n_long_running_instances = 0

    for nb in sagemaker_client.list_notebook_instances()["NotebookInstances"]:
        nb_in_service = nb["NotebookInstanceStatus"] == "InService"
        nb_running_too_long = nb["LastModifiedTime"].replace(tzinfo=None) < x_hs_ago

        if nb_in_service and nb_running_too_long:
            n_long_running_instances += 1

    if n_long_running_instances:
        message = f"You have {n_long_running_instances} instance(s) running 💸"

        # Prepara a requisição
        req = request.Request(
            f"https://ntfy.sh/{topic_name}",
            headers={"Title": "SaveMaker"},
            data=message.encode("utf-8"),
        )

        # Envia a requisição
        request.urlopen(req)

É importante garantir que o IAM (Identity and Access Management) role usado pela função lambda possa listar e descrever os notebooks SageMaker. Para permitir isso, adicionei as seguintes permissões ao IAM role da lambda.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SageMakerNotebookInstanceReadOnlyAccess",
      "Effect": "Allow",
      "Action": [
          "sagemaker:ListNotebookInstances",
          "sagemaker:DescribeNotebookInstance"
      ],
      "Resource": "*"
    }
  ]
}

Regra do EventBridge para acionar a lambda

O último passo é configurar uma regra do EventBridge para agendar a função Lambda. Primeiro, vá para o console do AWS EventBridge e crie uma nova regra. Na seção Event Source, escolha Schedule. Configurei minha regra para disparar a cada hora usando a seguinte expressão cron 0 * * * ? *. Ela representa o início de cada hora.

Em seguida, na seção Targets, selecione Lambda function e escolha a função Lambda que você criou anteriormente. Você pode encontrar a documentação oficial para essa configuração aqui. A imagem abaixo ilustra como a página de revisão deve ficar.

Clique aqui para ver a imagem.
Configuração da regra do EventBridge para acionar a função Lambda.


Testes

Para testar a integração, criei um notebook SageMaker e o deixei em execução por mais de 3 horas. Depois disso, recebi a seguinte notificação no meu celular.

Notificação push disparada pelo ntfy.

Note que o título da notificação foi definido para SaveMaker. Isso foi feito usando o header Title na requisição. Você pode encontrar instruções sobre como os diferentes headers funcionam aqui.

Conclusão e considerações

Neste post, exploramos como enviar notificações push usando o ntfy a partir de uma função Lambda. Também exploramos a integração dessa configuração com o AWS SageMaker, focando especificamente na configuração de alertas para notebooks que estão em execução por muito tempo.

Uma importante consideração é o risco associado ao uso da versão gratuita do ntfy para informações sensíveis. Com ela, qualquer usuário pode se inscrever em qualquer tópico e receber as notificações. Para transmitir dados sensíveis, considere hospedar o seu próprio ntfy, que oferece mais privacidade e controle. Também avalie outros serviços como Amazon SNS para mensagens escaláveis, Amazon Pinpoint para enviar mensagens de engajamento para clientes ou Amazon SES para envio de e-mails em geral.

Espero que você tenha gostado deste post. Nos vemos no próximo 👋.