За три дня сайт пять раз пропадал из сети, самый долгий раз на пять часов. Сервер при этом работал без единой ошибки: не перезагружался, не падал, исправно собирал статистику. Пропадала только дорога от него до интернета. Вот как я это выяснял и что поменял, чтобы в следующий раз ответ был готов сам.
Стенд
Небольшой сервер, на котором живут мой сайт-портфолио и SSH-ловушка для ботов. Сайт в Docker за обратным прокси, рядом — сторож на bash и systemd-таймерах: раз в десять минут проверяет, что всё работает, и шлёт пуш, если что-то сломалось. Снаружи сайт раз в пять минут проверяет внешний монитор.
«Проблем: 0», а сайт лежит
Первыми о сбоях узнал не я, а внешний монитор: он присылает письмо, когда сайт перестаёт отвечать. Своя проверка на сервере в это же время каждые десять минут писала в журнал «проблем: 0».
Ошибки в ней не было. Она проверяет то, что внутри: свежие ли данные, работают ли контейнеры и службы, хватает ли места на диске. Внутри всё было в порядке. Это первый урок истории: проверка изнутри видит сервер, но не путь до него. Посетителю всё равно, что процессы живы, если до них нельзя достучаться.
Что показали журналы
Сначала я исключил очевидное: сервер не перезагружался шесть дней, контейнеры не перезапускались, в журнале ядра нет ни нехватки памяти, ни ошибок диска. Зато Docker в нужные минуты сыпал одной и той же ошибкой: не может спросить у DNS адрес чего угодно, от удостоверяющего центра до собственных доменов. Тайм-аут, тайм-аут, тайм-аут.
Чтобы увидеть границы сбоев, достаточно разложить эти ошибки по десятиминуткам:
journalctl -u docker --since today -o short-iso --no-pager | grep 'failed to query external DNS' | cut -c1-15 | uniq -c
Получилась аккуратная картина: тишина, всплеск на полчаса, снова тишина, потом полтора часа сплошных ошибок. Время всплесков с точностью до минут совпало с письмами монитора. Сервер одновременно не мог выйти наружу и не принимал соединений снаружи, то есть пропадала связь целиком.
Сторож учится замечать обрыв
Первое исправление очевидное: проверять ещё и внешнюю связь. Теперь сторож раз в десять минут спрашивает DNS и пробует подключиться к паре публичных адресов.
Сложность в другом: пока связи нет, сообщить о сбое нельзя, пуш просто не уйдёт. Поэтому сторож запоминает время первой неудачной проверки, а когда связь возвращается, присылает одно сообщение «был без связи с … до …» и сам готовит черновик разбора. На странице доступности день сразу краснеет, не дожидаясь, пока я сяду писать текст.
Ложная тревога
Через два дня сторож поймал три обрыва. Я уже собирался писать разборы, но сначала решил проверить, видели ли сбой посетители. Лучший свидетель тут журнал самого сайта: если внешние запросы продолжали приходить, сайт был доступен, что бы ни думал сторож.
python3 -c 'import sys, json, datetime, ipaddress, collectionsc = collections.Counter()for line in sys.stdin: e = json.loads(line) if ipaddress.ip_address(e["request"]["remote_ip"]).is_global: c[datetime.datetime.fromtimestamp(e["ts"]).strftime("%H:%M")[:4] + "0"] += 1print(sorted(c.items()))' < access.log
Скрипт считает запросы только с внешних адресов: внутренние, например проверки из локальной сети, не в счёт.
Два обрыва подтвердились: в эти часы снаружи не пришло ни одного запроса. А вот первый, десятиминутный, оказался ложным. Отказывал только DNS, а посетители спокойно заходили. Сторож увидел, что сервер не может выйти наружу, и решил, что сайт недоступен.
Это второй урок: «сервер не может выйти наружу» и «сайт недоступен» — разные события, и проверять нужно оба. Теперь, прежде чем писать черновик, сторож сам смотрит журнал сайта. Если запросы шли хотя бы в половине отрезков перерыва, придёт только пуш с пометкой «сайт при этом был доступен», а полоса доступности не покраснеет.
Где именно рвётся
Осталось понять, на чьей стороне обрыв. Для этого нужно знать два адреса: шлюз, через который сервер выходит в сеть, и первый узел провайдера за ним. Шлюз берётся из таблицы маршрутов, а первый узел провайдера можно найти простым приёмом: отправить ping с временем жизни 2.
ping -n -c1 -t 2 1.1.1.1
Пакет умрёт на втором прыжке, и именно этот узел ответит «Time to live exceeded», назвав свой адрес. Пока связь есть, сторож раз в сутки запоминает оба адреса. Во время обрыва он каждые десять минут пингует их и по итогам пишет вывод в черновик:
-
не отвечал шлюз — сбой на нашей стороне;
-
шлюз отвечал, а узел провайдера нет — обрыв на линии провайдера;
-
отвечали оба — проблема дальше, в сети провайдера, или только у DNS.
С таким выводом уже можно идти в поддержку не с «у меня иногда не работает», а с точным временем и участком.
Взгляд снаружи, который работает во время сбоя
Последний штрих: страница статуса, которая открывается, даже когда сайт недоступен. Она живёт на GitHub Pages, проверяет сайт каждые пять минут из GitHub Actions и стоит на поддомене status.ogurexxx.ru.
Тут нашлась своя ловушка. У сайта включён HSTS для всех поддоменов: браузер открывает их только по HTTPS с действующим сертификатом и не даёт пропустить предупреждение. Пока сертификат на новом поддомене не выпущен, страница просто не открывается, даже если DNS настроен правильно. А некоторые сервисы мониторинга выпускают сертификат для своего домена только на платном тарифе.
Сколько простоя можно себе позволить
После этой истории на странице инцидентов появилась таблица надёжности: число сбоев, суммарный простой, среднее время восстановления (MTTR) и среднее время между сбоями (MTBF). И бюджет ошибок, как в SRE. Цель — 99,5% доступности в месяц, то есть около 3 часов 40 минут простоя.
Ноябрь этот бюджет исчерпал за первую неделю: только два сбоя 5 ноября дали больше пяти часов. Это неприятная цифра, но полезная. Пока бюджет не потрачен, можно спокойно экспериментировать. Когда он исчерпан, приоритет один — найти причину обрывов, а новые функции подождут.
Что я вынес
-
Проверка изнутри и проверка снаружи отвечают на разные вопросы. Нужны обе.
-
Во время обрыва сервер не может сообщить о нём сам. Значит, сообщение должно прийти после восстановления и сразу с фактами: сколько длилось и где рвалось.
-
Прежде чем писать разбор, стоит спросить свидетеля, которого сбой не касается. Журнал сайта честно показал, какой обрыв был настоящим.
-
Пять обрывов за три дня — уже не случайность, а неисправность. Теперь у меня есть данные, чтобы её найти.
Разборы каждого сбоя — на странице «Инциденты», текущее состояние — на status.ogurexxx.ru.
Источник: habr.com