Серверы и мониторинг

Одна проба врёт в обе стороны: как мы мерили доступность и дважды ошиблись

Первая проверка сказала «заблокировано», вторая — «всё работает». Обе были неверны. 28 проб за две минуты, 29 % отказов и почему доступность — это доля.

Александр Цапков10 сентября 20263 мин

Мы проверяли, доступен ли внешний API с наших боевых машин. Первая же проба дала таймаут на 8–10 секунд. Вывод напрашивался сам: заблокировано.

Вывод был неверный. Следующий, противоположный, — тоже.

Почему первая проба сказала «недоступно»

По IPv4 адрес действительно молчит. По IPv6 — отвечает за 44 миллисекунды.

Утилиты, которые по умолчанию берут A-запись, показывают пустой вывод, и из него рождается «недоступно». Никто не врёт: инструмент честно сообщает, что по тому адресу, который он спросил, ответа нет. Просто спросил он не всё.

Почему вторая проба сказала бы «работает» — и тоже соврала

Мы прогнали серию: 28 проб за две минуты. 20 успешных, 8 отказов — 29 %.

Одиночная успешная проба сказала бы «всё работает». А маршрут с почти третью отказов как основной не годится: на нём нельзя строить ни отправку, ни получение — он будет ронять каждый третий запрос и выглядеть при этом случайным сбоем.

Вот полная картина, которую даёт серия и не даёт одиночный запрос:

Что спросилиЧто ответило
IPv4, один разтаймаут 8–10 с
IPv6, один раз44 мс
IPv6, 28 раз за 2 минуты20 успехов, 8 отказов

Три ответа об одном и том же адресе, и первые два одинаково бесполезны.

Что мы делаем теперь

Проверяем обеими версиями протокола принудительно и смотрим на фактический remote_ip ответа, а не на имя хоста. Имя хоста не говорит, куда вы на самом деле пришли.

Меряем серией, а не одним запросом. Доступность внешней службы — это доля успеха на выборке, а не «да» или «нет». Число проб важнее их качества: двадцать восемь дешёвых говорят правду, одна тщательная — нет.

Помним про Happy Eyeballs. В Node fetch сам выбирает версию протокола, и на холодном соединении это иногда стоит ожидания мёртвой ветки. То есть ваш код может ждать восемь секунд там, где рабочий маршрут отвечает за сорок миллисекунд, — и в логе это будет выглядеть как «сервис тормозит».

Как проверить у себя

# принудительно каждой версией протокола
curl -4 -sS -o /dev/null -w "v4: %{http_code} %{time_total}s %{remote_ip}\n" https://api.example.com
curl -6 -sS -o /dev/null -w "v6: %{http_code} %{time_total}s %{remote_ip}\n" https://api.example.com

# серия: доля успеха, а не единичный ответ
ok=0; for i in $(seq 1 28); do
  curl -sS -o /dev/null --max-time 5 https://api.example.com && ok=$((ok+1))
done
echo "успешных: $ok из 28"

Если во второй команде получилось не 28 — у вас не «работает», у вас доля.

Что из этого следует

Проверка доступности отличается от обычного теста тем, что её результат непостоянен по природе. Тест либо проходит, либо нет; сеть — отвечает в восьмидесяти случаях из ста. Одиночная проба превращает распределение в двоичный ответ, и знак этого ответа определяется тем, в какую секунду вы нажали Enter.

Отсюда правило, которое мы держим и для себя, и для клиентских серверов: вывод о доступности делается по выборке, и в выводе называется размер выборки. «Сервис доступен» без числа проб — это не факт, а впечатление.


У нас проверки доступности идут снаружи и по расписанию, а не в тот момент, когда кто-то решил посмотреть: HTTP, TCP, TLS и остаток срока сертификата, с историей по каждой. Как это устроено — на странице Мониторинг доступности.

Мониторинг доступности сайтов и APIПроверки доступности снаружи: HTTP, TCP, TLS и остаток срока сертификата.

Читайте также

Автор

Александр ЦапковОснователь Скоупворк

Веду платформу и её боевой контур сам: разработка, выкат, дежурство. Пишу о том, на чём мы обожглись, — с датами, замерами и ссылками на решения в репозитории.