← All posts

Monitoring as Code: як ми перевірили, що сайт працює?

AWSMonitoringTerraformLambdaPython

Картина: чоловік, тіло якого вкрите безліччю очей

🔧 Цього тижня ми провели заключний практичний воркшоп з благодійної серії AWS & Terraform & GitLab від DevOps 01.

Нагадую, як усе було:

👉 1-й воркшоп - розібрали React-based аплікейшн на атоми, підготували інфраструктуру в AWS (CloudFront, S3, Route53, ACM)

👉 2-й воркшоп - написання Terraform-ресурсів + імпорт у стейт

👉 3-й воркшоп - CI/CD як для application repo, так і для Terraform repo

Схема статичного сайту в AWS: Route 53, Certificate Manager, CloudFront і S3 AWS Architecture


🔍 Наче основні компоненти у нас присутні

✅ Є аплікейшн

✅ Ми знаємо технічний стек

✅ Розуміємо його бізнес-логіку

✅ Вміємо автоматично збирати за комітом у репозиторій та робити деплоймент в AWS environment, який покритий Terraform-кодом зі своїм CI/CD

Але як ми дізнаємося, що наш сайт успішно працює?

Та сама схема AWS зі знаком питання «Monitoring?» від користувача до сайту problem statement


📌 Дотримуючись нашого принципу - рухатися від PoC до MVP - можна написати Python-скрипт, який використовує бібліотеку requests.

Завдяки їй ми можемо відправляти HTTP-запити, а потім оперувати інформацією з response, щоб зробити висновок: працює сайт чи ні.

import os
import logging
import requests

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def main():
    url = os.environ.get("TARGET_URL")
    if not url:
        logger.error("TARGET_URL not set")
        return

    try:
        response = requests.get(url, timeout=5)
        if response.status_code != 200:
            logger.warning(f"Site is down: {url} returned {response.status_code}")
        else:
            logger.info(f"Site is up: {url} returned {response.status_code}")
    except Exception as e:
        logger.error(f"Site unreachable: {e}")

if __name__ == "__main__":
    main()

Мем із наляканим чорним котом: «There has been a failure on the INTERNETS»


📨 Далі залишається визначитися, що ми будемо робити з цією інформацією

Бо моніторинг - це не лише перевірка endpoints та оперування метриками. Це ще й notifications для тих, хто має бути в курсі: чи живий сайт, чи ні?

У рамках PoC можемо почати з email-повідомлень.

import os
import smtplib
import logging
import requests
from email.message import EmailMessage

logger = logging.getLogger()
logger.setLevel(logging.INFO)

SMTP_SERVER = os.environ.get("SMTP_SERVER")     
SMTP_PORT = int(os.environ.get("SMTP_PORT", 587))
SMTP_USER = os.environ.get("SMTP_USER")       
SMTP_PASS = os.environ.get("SMTP_PASS")    

def send_alert_emails(subject, message):
    emails = os.environ.get("ALERT_EMAILS", "")
    recipient_list = [email.strip() for email in emails.split(",") if email.strip()]
    
    msg = EmailMessage()
    msg["From"] = SMTP_USER
    msg["Subject"] = subject
    msg.set_content(message)

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.starttls()
            server.login(SMTP_USER, SMTP_PASS)
            for recipient in recipient_list:
                msg["To"] = recipient
                server.send_message(msg)
                logger.info(f"Email sent to {recipient}")
                del msg["To"]
    except Exception as e:
        logger.error(f"Failed to send email: {e}")

def main():
    url = os.environ.get("TARGET_URL")
    if not url:
        logger.error("TARGET_URL not set")
        return

    try:
        response = requests.get(url, timeout=5)
        if response.status_code != 200:
            send_alert_emails("Site is down", f"{url} returned {response.status_code}")
    except Exception as e:
        send_alert_emails("Site unreachable", str(e))

if __name__ == "__main__":
    main()

Схема PoC: Python-код із requests і boto3 перевіряє сайт і надсилає email-сповіщення monitoring PoC


📅 Як часто перевіряти наш сайт?

Нам потрібен якийсь розклад. Наприклад, schedule у стилі crontab.


🤔 Але де запускати цей код?

  • Контейнер?
  • Віртуалка?

Це вже тягне за собою необхідність зібрати службовий імедж, десь його запустити (або взагалі тягнути Prometheus + Blackbox exporter), адмініструвати цю інфраструктуру…

Схема з Python-кодом на інстансі за crontab усередині AWS-акаунта

🧠 Тут на допомогу приходить AWS Lambda - класичний представник Serverless Managed Services, який працює за простою моделлю:

🔹 ми платимо AWS за час виконання коду,

🔹 а все інше AWS зробить за нас.

import os
import logging
import requests

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    url = os.environ.get("TARGET_URL")
    try:
        response = requests.get(url, timeout=5)
        if response.status_code != 200:
            send_alert_emails("Site is down", f"{url} returned {response.status_code}")
    except Exception as e:
        send_alert_emails("Site unreachable", str(e))

Логотип AWS Lambda Схема, де код перевірки тепер працює в AWS Lambda

Нам залишається лише:

✅ створити Lambda function

✅ обрати runtime (python3.13)

✅ визначити Lambda execution IAM role

Але не забуваймо - Lambda by design не має всіх dependencies у середовищі. Тому для requests використовуємо Lambda Layers, куди закидаємо бібліотеку окремо.

#підготовка zip для AWS Lambda Layer
mkdir requests-layer && cd requests-layer
pip install requests -t python
zip -r ../requests-layer.zip python

Схема Lambda Layers: код функції та шари в середовищі виконання Lambda layers


Як надсилаємо emails?

📤 Надсилати email будемо через AWS SES - керований сервіс для автоматизації email workflow.

Що треба:

🔹 налаштувати email identity, з якої будемо надсилати листи

🔹 взаємодіяти з SES з Lambda-коду через boto3 (AWS SDK for Python)

🔹 не забути додати в IAM role Lambda дозвіл на доступ до SES

Схема роботи Amazon Simple Email Service AWS SES

#lambda/lambda_function.py

import os
import logging
import requests
import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ses = boto3.client('ses')

def send_alert_emails(subject, message):
    emails = os.environ.get("ALERT_EMAILS", "")
    recipient_list = [email.strip() for email in emails.split(",") if email.strip()]
    for email in recipient_list:
        ses.send_email(
            Source=email,
            Destination={"ToAddresses": [email]},
            Message={
                "Subject": {"Data": subject},
                "Body": {"Text": {"Data": message}}
            }
        )

def lambda_handler(event, context):
    url = os.environ.get("TARGET_URL")
    try:
        response = requests.get(url, timeout=5)
        if response.status_code != 200:
            send_alert_emails("Site is down", f"{url} returned {response.status_code}")
    except Exception as e:
        send_alert_emails("Site unreachable", str(e))

🧩 На цей момент ми покрили

  • запуск Python-коду за допомогою AWS Lambda
  • додавання залежностей з PyPI через Lambda Layer
  • надсилання email через AWS SES

⏱️ Залишилося вирішити лише питання розкладу

Тут нам допоможе AWS EventBridge:

🔹 Lambda можна викликати або через HTTP endpoint (вручну), або через тригери

🔹 Ми обираємо EventBridge schedule rule - rate(2 minutes) або cron(* * * * *)

🔹 Прив’язуємо правило до Lambda та дозволяємо виклик

📝 Логи роботи функції автоматично потрапляють у CloudWatch Log Group.

Повна схема: Scheduler запускає Lambda, Lambda через SES сповіщає підписників


І нарешті - описуємо все в Terraform-коді (можна своїм модулем або скористатися готовим модулем від Anton Babenko).

module "lambda_site_checker" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 7.0"

  function_name = "site_checker_v3"
  handler       = "lambda_function.lambda_handler"
  runtime       = "python3.13"
  source_path   = "./lambda"
  create_package = true

  environment_variables = {
    TARGET_URL   = var.target_url
    ALERT_EMAILS = var.alert_emails
  }

  layers = [data.aws_lambda_layer_version.requests.arn]
  create_current_version_allowed_triggers = false

  cloudwatch_logs_retention_in_days = 14

  allowed_triggers = {
    cloudwatch = {
      principal  = "events.amazonaws.com"
      source_arn = aws_cloudwatch_event_rule.site_checker_trigger.arn
    }
  }

  attach_policy = false
  policy        = aws_iam_policy.ses_send_email.arn
}

Мем «Bon appetit»: кухар елегантно посипає сіллю


Ось так, крок за кроком, ми рухаємося від ідеї до повноцінного MVP.

Серія воркшопів була частиною ініціативи DevOps01, і я дякую всім, хто приєднався або підтримав 💛

Основної мети досягнуто - ми зібрали гроші для ЗСУ та навчилися чогось нового!

Окрема подяка TEMABIT Software Development та Stanislav Kolenkin за технічну підтримку DevOps 01.

Посилання

Lambda

EventBridge->Lambda

SES

#AWSCommunity #DevOps01


Вперше опубліковано на LinkedIn 12 квітня 2025 р.: оригінал.