Alerty telefoniczne z CloudWatch bez Lambda i Twilio

SNS nie zadzwoni na telefon, a jego payload to kłopot. Alarmy CloudWatch przez EventBridge do dzwoniącego webhooka: konfiguracja, filtry, pułapki.

Aktualizacja

Spis treści

Alarm CloudWatch nie może zadzwonić na Twój telefon. Jego jedyne wbudowane akcje to SNS, Auto Scaling, EC2 i Systems Manager, a SNS kończy się mailem, SMS-em albo kolejką, w którą trzeba akurat patrzeć. Poradniki, które faktycznie doprowadzają do dzwoniącego telefonu, stawiają funkcję Lambda i konto Twilio. Możesz pominąć jedno i drugie: pozwól EventBridge odebrać zmianę stanu alarmu, przekształcić ją w mały JSON i wysłać POST-em na kanał telefoniczny Echobell.

Ten przewodnik obejmuje całą drogę — kanał, regułę EventBridge, transformator wejścia, filtry, dzięki którym dzwonek ma znaczenie, oraz powód, dla którego skierowanie SNS prosto na webhook wygląda, jakby miało działać, a w większości przypadków nie działa.

Dlaczego zwykła droga przez SNS jest kłopotliwa

Domyślna rada brzmi: „alarm → temat SNS → subskrybent”. Dla połączenia telefonicznego oznacza to trzy problemy:

  • SNS nie ma protokołu głosowego. Typy subskrybentów to e-mail, SMS, push mobilny, SQS, Lambda, Firehose i HTTP(S). SMS łatwo przegapić i blokuje go tryb Nie przeszkadzać; połączenie głosowe wymaga czegoś, co zbudujesz sam.
  • Wiadomość HTTPS to napis wewnątrz napisu. SNS dostarcza swoją kopertę z Content-Type: text/plain, a sam alarm jest dokumentem JSON zserializowanym w polu Message tej koperty. Odbiorca webhooka, który czyta pola — AlarmName, NewStateValue — znajduje tylko jeden nieprzezroczysty blok.
  • Koperta jest duża. Przez podpis, adres URL certyfikatu i link do wypisania się koperta dodaje znacznie ponad kilobajt, zanim zacznie się tekst alarmu. Echobell przycina treść powiadomienia do 1500 bajtów, więc szablon „wrzuć całą treść” ucina wiadomość dokładnie tam, gdzie zaczyna się to, co przydatne.

Możesz sprawić, że SNS zadziała, dokładając Lambdę, która rozpakuje wiadomość i prześle ją dalej. Ale CloudWatch już teraz publikuje każdą zmianę stanu alarmu do EventBridge jako czyste zdarzenie JSON — bez konfigurowania żadnej akcji — a EventBridge potrafi je przekształcić i samodzielnie wywołać endpoint HTTPS.

Czego potrzebujesz

  • Konta AWS i uprawnień do tworzenia reguł EventBridge, połączeń i API destinations
  • Zainstalowanego Echobella (App Store / Google Play)
  • Co najmniej jednego istniejącego alarmu CloudWatch
  • Dziesięciu minut

Nic po Twojej stronie nie musi przyjmować ruchu przychodzącego. Żądanie wychodzące wysyła EventBridge.

Krok 1 — Utwórz kanał, który dzwoni

W Echobellu utwórz kanał i ustaw typ powiadomienia na Połączenie. Kanał telefoniczny zachowuje się jak połączenie przychodzące, a nie jak push, więc przechodzi przez tryb Skupienia i Nie przeszkadzać w iOS.

Transformator z kroku 3 wysyła płaską treść z polami alarm, state, reason, region i link, więc szablony zostają krótkie:

Tytuł: {{state}}: {{alarm}}
Treść: {{reason}}

W ustawieniach zaawansowanych ustaw szablon linku, żeby dotknięcie powiadomienia otwierało alarm w konsoli:

{{link}}

Skopiuj adres webhooka kanału z widoku szczegółów:

https://hook.echobell.one/t/<channel-token>

Krok 2 — Utwórz połączenie i API destination

EventBridge wywołuje zewnętrzne endpointy HTTPS przez API destination, które wymaga połączenia (connection) przenoszącego uwierzytelnianie. Echobell uwierzytelnia po tajnym tokenie w adresie URL, więc połączenie jest czystą formalnością — ale EventBridge nie utworzy destination bez niego.

  1. Otwórz EventBridge → Integration → API destinations → Connections → Create connection
  2. Name: echobell
  3. Authorization type: API key
  4. API key name: x-source, Value: eventbridge
  5. Utwórz je, potem przejdź do API destinations → Create API destination
  6. Name: echobell-calling
  7. API destination endpoint: adres kanału Echobell z kroku 1
  8. HTTP method: POST
  9. Invocation rate limit: 1 na sekundę w zupełności wystarczy i działa jak zawór bezpieczeństwa
  10. Connection: echobell

Nagłówek z kluczem API jest wysyłany z każdym żądaniem. Możesz go sprawdzać w warunku kanału Echobell, jeśli chcesz mieć drugą bramkę przed tokenem:

header["x-source"] == "eventbridge"

Krok 3 — Utwórz regułę i transformator wejścia

Wejdź w EventBridge → Rules → Create rule w tym samym regionie co alarmy. Reguły są regionalne; alarm z eu-west-1 jest niewidoczny dla reguły w us-east-1.

Użyj domyślnej szyny zdarzeń (event bus) i reguły typu event pattern. Ten wzorzec dopasowuje każdy alarm, który przechodzi w stan ALARM:

{
  "source": ["aws.cloudwatch"],
  "detail-type": ["CloudWatch Alarm State Change"],
  "detail": {
    "state": { "value": ["ALARM"] }
  }
}

Jako cel wybierz EventBridge API destination i wskaż echobell-calling. Pozwól konsoli utworzyć nową rolę wykonawczą — nadaje ona events:InvokeApiDestination dla tego destination i nic więcej.

Potem rozwiń Additional settings → Configure target input → Input transformer:

Input path

{
  "alarm": "$.detail.alarmName",
  "state": "$.detail.state.value",
  "reason": "$.detail.state.reason",
  "region": "$.region",
  "time": "$.time"
}

Template

{
  "alarm": "<alarm>",
  "state": "<state>",
  "reason": "<reason>",
  "region": "<region>",
  "time": "<time>",
  "link": "https://console.aws.amazon.com/cloudwatch/home?region=<region>#alarmsV2:alarm/<alarm>"
}

EventBridge wysyła wynik jako application/json, dzięki czemu Echobell może czytać {{alarm}} i {{state}} bezpośrednio.

Krok 4 — Poznaj, jak naprawdę wygląda zdarzenie

Input path czyta ze zdarzenia, które publikuje CloudWatch. W skróconej formie wygląda tak:

{
  "version": "0",
  "id": "c4c1c1c9-6542-e61b-6ef0-8c4d36933a92",
  "detail-type": "CloudWatch Alarm State Change",
  "source": "aws.cloudwatch",
  "account": "123456789012",
  "time": "2026-10-16T03:12:44Z",
  "region": "us-east-1",
  "resources": ["arn:aws:cloudwatch:us-east-1:123456789012:alarm:prod-api-5xx"],
  "detail": {
    "alarmName": "prod-api-5xx",
    "state": {
      "value": "ALARM",
      "reason": "Threshold Crossed: 1 out of the last 1 datapoints [42.0] was greater than the threshold (10.0).",
      "timestamp": "2026-10-16T03:12:44.000+0000"
    },
    "previousState": { "value": "OK" },
    "configuration": { "description": "API 5xx errors on the prod load balancer" }
  }
}

Co warto wiedzieć, zanim zaczniesz edytować szablon:

  • Alarm mieszka pod detail. $.alarmName nie dopasuje niczego; $.detail.alarmName — tak.
  • state.reason to tekst generowany automatycznie. Wskazuje punkt danych i próg, czyli dokładnie to, co chcesz usłyszeć o 3 w nocy.
  • Nazwy alarmów są wstawiane bez zmian. Cudzysłów w nazwie alarmu lub w powodzie zepsułby JSON produkowany przez transformator. Trzymaj się w nazwach alarmów liter, cyfr, myślników i podkreślników — co i tak jest dobrą praktyką — bo inaczej połączenie zakończy się błędem 400.
  • Link wymaga zwykłej nazwy. Nazwa ze spacjami lub znakiem # nie zadziała poprawnie jako głęboki link; wyszukiwarka w konsoli i tak ją znajdzie.

Krok 5 — Zawęź do tego, co zasługuje na telefon

Kanał alarmów, który dzwoni przy każdym ALARM, to najszybszy sposób, by nauczyć się go ignorować. Filtruj w dwóch miejscach.

We wzorcu EventBridge, który działa, zanim cokolwiek zostanie wysłane:

CelZmiana wzorca
Tylko alarmy produkcyjne"alarmName": [{ "prefix": "prod-" }] wewnątrz detail
Pomijaj wszystko oznaczone jako niepilneNazwij je warn-… i użyj "alarmName": [{ "anything-but": { "prefix": "warn-" } }]
Tylko wybrane alarmy"alarmName": ["prod-api-5xx", "prod-db-cpu"]
Tylko prawdziwe alarmy, nie stany testoweZostaw "state": { "value": ["ALARM"] } — zdarzenie INSUFFICIENT_DATA nigdy nie pasuje
Tylko jedno konto lub regionDodaj "account": ["123456789012"] albo "region": ["us-east-1"] na najwyższym poziomie

W Echobellu użyj warunku kanału dla tego, czego wzorzec nie wyrazi, na przykład okna czasowego:

state == "ALARM" && (hour >= 17 || hour < 9)

Zmienne czasu w Echobellu są w UTC, więc najpierw przelicz ze swojej strefy. Pełną listę zmiennych ma dokumentacja warunków.

Krok 6 — Daj powrotom do normy cichsze drzwi

Druga reguła z "value": ["OK"] może powiedzieć Ci, że problem minął, bez dzwonienia. Utwórz drugi kanał Echobell typu Pilne (Time Sensitive) (albo normalny), drugie API destination wskazujące na niego i użyj ponownie tego samego transformatora wejścia. Szablon tytułu {{state}}: {{alarm}} czyta się już jako „OK: prod-api-5xx”.

RegułaStanTyp kanałuZachowanie
alarms-to-callALARM, prefiks prod-PołączenieDzwoni przez tryb Skupienia
alarms-warnALARM, prefiks warn-PilneEkran blokady, bez dzwonka
alarms-recoveredOKNormalneZwykły push

Przetestuj, zanim zaufasz

Nie czekaj, aż zepsuje się produkcja. Wymuś na alarmie stan, który chcesz sprawdzić:

aws cloudwatch set-alarm-state \
  --alarm-name prod-api-5xx \
  --state-value ALARM \
  --state-reason "Manual test of the Echobell path"

Alarm wraca do swojego prawdziwego stanu przy następnej ewaluacji, ale zdarzenie zmiany stanu jest prawdziwe i przechodzi przez Twoją regułę. Żeby przetestować wyłącznie stronę Echobella, wyzwól kanał bezpośrednio:

curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"alarm":"prod-api-5xx","state":"ALARM","reason":"Manual test","region":"us-east-1","link":"https://console.aws.amazon.com/cloudwatch/"}'

Jeśli curl dzwoni, a test alarmu nie, problem leży w regule, transformatorze albo destination — nie w kanale.

A co z SNS i drogą „e-mail na telefon”?

Jeśli temat SNS masz już wszędzie i nie chcesz ruszać EventBridge, istnieją dwie inne drogi:

  • SNS → Lambda → webhook. Kilka linijek kodu parsuje Message, a potem wysyła płaską treść POST-em na ten sam adres Echobell. Działa, ale odtąd utrzymujesz funkcję, rolę i jej logi.
  • SNS → e-mail → wyzwalacz e-mail Echobella. Zasubskrybuj do tematu adres wyzwalacza e-mail kanału. Alarm przychodzi jako e-mail, a {{subject}} i {{text}} w szablonach stają się powiadomieniem. To najmniej konfiguracji ze wszystkich, z zastrzeżeniem, że subskrypcje e-mail w SNS wymagają jednorazowego kliknięcia potwierdzenia, a tekst nie jest przekształcany.

Zasubskrybowanie adresu webhooka Echobell bezpośrednio do tematu jako endpointu HTTPS to droga, której należy unikać: żądanie potwierdzenia przyjdzie jako powiadomienie, a każdy kolejny alarm to długa koperta opisana wyżej.

Limity, które mają znaczenie podczas incydentu

  • 1 MiB treści. Echobell odrzuca treści wyzwalacza powyżej 1 MiB z kodem HTTP 413. Przekształcony alarm to kilkaset bajtów, więc tutaj tego nie dotkniesz.
  • 120 żądań na minutę na token. Powyżej wyzwalacz odpowiada 429 z RATE_LIMIT_EXCEEDED i nagłówkiem Retry-After. Limit destination, 1 na sekundę, trzyma Cię poniżej tego progu, a EventBridge ponawia zdarzenia, których nie udało się dostarczyć.
  • 1500 bajtów treści powiadomienia. Dłuższy render jest przycinany. state.reason to zdanie lub dwa i się mieści.

Czego ta konfiguracja nie daje

  • Brak potwierdzenia. Odebranie połączenia nie mówi CloudWatch niczego i nie wycisza pozostałych subskrybentów.
  • Brak eskalacji i rotacji. Dzwonimy do wszystkich zapisanych na kanał; nikt nie jest następny w kolejce.
  • Brak deduplikacji poza samym alarmem. Alarm, który „miga” i co kilka minut przechodzi OK → ALARM, będzie dzwonił za każdym razem. Napraw to dłuższymi okresami ewaluacji albo DatapointsToAlarm w samym alarmie.
  • Brak synchronizacji dwukierunkowej. Usunięcie przyczyny niczego nie czyści na telefonie.

Jeśli to dyskwalifikuje, potrzebujesz pełnej platformy dyżurowej. Jeśli potrzebujesz „zadzwoń, gdy odpali alarm produkcyjny”, to garść ekranów w konsoli.

Rozwiązywanie problemów

Alarm się odpalił, ale nic nie przyszło. Otwórz zakładkę Monitoring reguły: Invocations pokazuje, czy wzorzec pasował, a FailedInvocations — czy dostarczenie się nie udało. Reguły w innym regionie niż alarm nigdy nie pasują.

Reguła pasuje, ale połączenie kończy się błędem 400. Transformator wyprodukował niepoprawny JSON. Poszukaj cudzysłowu lub backslasha w nazwie alarmu albo powodzie.

Powiadomienie jest puste. Szablon czyta pole, którego transformator nie wysłał. Treść jest płaska: {{alarm}}, nie {{detail.alarmName}}.

Dzwoni wielokrotnie przy jednym alarmie. Alarm „miga”. Wydłuż okres ewaluacji albo wymagaj więcej punktów danych; to ustawienie CloudWatch, nie Echobella.

Dzwoni przy alarmie, którego się nie spodziewasz. Sprawdź, czy we wzorcu nie brakuje filtra prefix, i pamiętaj, że alarmy złożone oraz alarmy tworzone przez inne narzędzia (na przykład polityki Auto Scaling) również emitują zmiany stanu.

Najczęstsze pytania

Czy CloudWatch potrafi zadzwonić na numer telefonu natywnie?

Nie. Akcje alarmów CloudWatch to powiadomienia SNS, Auto Scaling, akcje EC2 i Systems Manager. Połączenie głosowe wymaga usługi zewnętrznej: Lambdy dzwoniącej przez dostawcę, platformy dyżurowej albo dzwoniącego webhooka, jak Echobell.

Czy potrzebuję tematu SNS?

Nie. CloudWatch sam emituje zdarzenia zmiany stanu alarmu do EventBridge, niezależnie od tego, czy alarm ma jakieś akcje. Listę akcji alarmu możesz zostawić pustą.

Czy alarm CloudWatch przebije tryb Nie przeszkadzać?

Tylko jeśli dotrze jako alert w formie połączenia. Kanał Echobell typu połączenie zachowuje się jak połączenie przychodzące, które tryb Skupienia i Nie przeszkadzać w iOS przepuszczają. SMS ani zwykły push — nie.

Dlaczego nie zasubskrybować po prostu adresu webhooka do SNS?

SNS wysyła text/plain z alarmem zserializowanym wewnątrz napisu Message, więc szablon nie odczyta poszczególnych pól, a koperta jest na tyle długa, że zostaje przycięta. SNS wysyła też najpierw potwierdzenie subskrypcji, które przyszłoby jako powiadomienie.

Czy to kosztuje coś w AWS?

Zdarzenia zmiany stanu alarmu trafiają na domyślną szynę zdarzeń bez opłat; wywołania API destination są rozliczane za żądanie. Przy wolumenie zdrowego zestawu alarmów to kwota pomijalna, ale sprawdź aktualny cennik EventBridge dla swojego regionu.

Czy mogę dzwonić do różnych osób przy różnych alarmach?

Tak. Utwórz jeden kanał na grupę odbiorców, jedną regułę na kanał i pozwól każdej osobie zapisać się z wybranym typem powiadomienia. Potwierdzeń nie ma, więc dzwonimy do wszystkich, którzy wybrali „połączenie”.

A co z alarmami w kilku regionach lub kontach?

Reguły są per region, więc utwórz regułę w każdym używanym regionie (ta sama definicja API destination jest powtarzana w każdym regionie). Przy kilku kontach wysyłaj zdarzenia na centralną szynę w jednym koncie i tam umieść regułę.

Podsumowanie

Droga składa się z pięciu części: kanału telefonicznego, połączenia i API destination wskazującego na niego, reguły dopasowującej tylko te alarmy, dla których warto kogoś obudzić, transformatora wejścia spłaszczającego zdarzenie oraz wymuszonego testu set-alarm-state, zanim zaczniesz na czymkolwiek polegać. Cała reszta dotyczy utrzymania jej dostatecznie wąskiej, żeby za miesiąc dzwonek wciąż coś znaczył.

Pobierz Echobella na iPhone'a albo weź go z Google Play, a potem uruchom test set-alarm-state.

Powiązane artykuły