Диагностика недоступного сервиса: от DNS до процесса и логов

Фраза «сайт не работает» объединяет разные сбои: имя не разрешается, TCP недоступен, TLS не подтверждён, proxy возвращает ошибку, процесс упал или dependency отвечает медленно. Надёжная диагностика проверяет слои по порядку и сохраняет наблюдения до перезапуска.

Сначала сформулируйте симптом

Зафиксируйте URL/host, время с timezone, точку наблюдения, ожидаемый status и фактическую ошибку. Один клиент может использовать VPN, IPv6, cache или proxy, которого нет у другого. Повторять запрос нужно из той же среды пользователя и отдельно с сервера.

Минимальный маршрут проверки

  1. getent/dig: какое имя и address получил клиент.
  2. ss/lsof на сервере: есть ли listener на ожидаемом address/port и кто owner.
  3. curl с timeout: дошли ли TCP, TLS и HTTP; какой status/body.
  4. systemctl/container status: жив ли управляемый экземпляр и не перезапускается ли он.
  5. journal/application logs в узком окне.
  6. Dependency probes: БД, очередь, диск, DNS и внешние API.
target=https://example.com/
body_file=$(mktemp)
trap 'rm -f -- "$body_file"' EXIT
date -Is
getent ahosts example.com
curl --silent --show-error --fail-with-body \
  --connect-timeout 3 --max-time 10 \
  --output "$body_file" \
  --write-out 'http=%{http_code} remote=%{remote_ip} total=%{time_total}\n' \
  "$target"
status=$?
printf 'curl_status=%s body_bytes=%s\n' \
  "$status" "$(wc -c <"$body_file")"
rm -f -- "$body_file"
trap - EXIT

Команда создаёт отдельный приватный временный файл, очищает его через trap и не смешивает HTTP-status с curl exit status. Поскольку mktemp каждый раз создаёт пустой уникальный файл, неуспешное соединение не оставит в расчёте body от предыдущего запуска. Не сохраняйте чувствительный ответ дольше расследования.

Локальная воспроизводимая модель

log=$(mktemp)
python3 -m http.server 18080 --bind 127.0.0.1 >"$log" 2>&1 &
pid=$!
trap 'kill "$pid" 2>/dev/null || true' EXIT
ss -ltnp | grep ':18080'
curl --fail --silent --show-error http://127.0.0.1:18080/ >/dev/null
kill -TERM "$pid"
wait "$pid"
trap - EXIT

Этот fixture позволяет наблюдать PID, listener, request и корректное завершение. Если порт занят, это тоже полезный заранее диагностируемый результат: найдите owner, не завершайте его автоматически.

Ошибка и диагностика

Самая дорогая ошибка — начать с restart. Он уничтожает состояние, очищает временный симптом и может сделать incident невоспроизводимым. Сначала соберите status, PID, listener, health response, последние logs и ресурсные ограничения. Restart допустим как контролируемое восстановление после evidence, но не заменяет root cause.

Также не проверяйте только localhost, если жалоба приходит извне: локальный curl обходит DNS, firewall, TLS termination и proxy. И наоборот, внешний 502 не доказывает падение backend — proxy мог иметь неверный upstream.

Практика

Запустите локальную модель, подтвердите каждый слой и сохраните короткий incident note. Затем измените curl port на соседний и объясните отличие Connection refused от timeout. После этого завершите процесс, повторите ss/curl и укажите, какое наблюдение локализует сбой раньше логов.

Что важно запомнить

Источники