---
title: "Alerty telefoniczne z CloudWatch bez Lambda i Twilio"
description: "SNS nie zadzwoni na telefon, a jego payload to kłopot. Alarmy CloudWatch przez EventBridge do dzwoniącego webhooka: konfiguracja, filtry, pułapki."
date: 2026-10-16
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - AWS
  - CloudWatch
  - EventBridge
  - alerty telefoniczne
  - webhooki
  - dyżur
---

# Alerty telefoniczne z alarmów CloudWatch: od EventBridge do telefonu, bez Lambdy

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](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-pl&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid))
- 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`:

```json
{
  "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**

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

**Template**

```json
{
  "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:

```json
{
  "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:

| Cel | Zmiana wzorca |
| --- | --- |
| Tylko alarmy produkcyjne | `"alarmName": [{ "prefix": "prod-" }]` wewnątrz `detail` |
| Pomijaj wszystko oznaczone jako niepilne | Nazwij 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 testowe | Zostaw `"state": { "value": ["ALARM"] }` — zdarzenie `INSUFFICIENT_DATA` nigdy nie pasuje |
| Tylko jedno konto lub region | Dodaj `"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](/pl/docs/conditions).

## 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ła | Stan | Typ kanału | Zachowanie |
| --- | --- | --- | --- |
| `alarms-to-call` | `ALARM`, prefiks `prod-` | Połączenie | Dzwoni przez tryb Skupienia |
| `alarms-warn` | `ALARM`, prefiks `warn-` | Pilne | Ekran blokady, bez dzwonka |
| `alarms-recovered` | `OK` | Normalne | Zwykły push |

## Przetestuj, zanim zaufasz

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

```bash
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:

```bash
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](/pl/docs/email-trigger) 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](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-pl&mt=8) albo [weź go z Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), a potem uruchom test `set-alarm-state`.

## Powiązane artykuły

- [Dokumentacja warunków kanału](/pl/docs/conditions)
- [Przewodnik po integracji z webhookami](/pl/docs/webhook)
- [Przewodnik po wyzwalaczu e-mail](/pl/docs/email-trigger)
- [Alerty telefoniczne Sentry](/pl/blog/sentry-phone-call-alerts)
- [Alerty telefoniczne przy awarii API](/pl/blog/phone-call-alerts-api-downtime)
- [Poradnik dla programistów o zmęczeniu alertami](/pl/blog/fix-alert-fatigue-developer-guide)
- [Przebijanie trybu Skupienia iOS przy alertach krytycznych](/pl/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)
