Перейти к содержанию

Лабораторная: диагностика Nginx 502/504

В паре со статьёй: Обратный прокси Nginx и диагностика 502/504

Окружение: одна одноразовая VM с установленным nginx и доступным Python 3 (для простейшего backend). Если вы предпочитаете контейнеры вместо VM, файлы ниже воспроизводят ту же лабораторную.

Версия лабораторной на Docker Compose (необязательно)
# docker-compose.yml
services:
  nginx:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - backend

  backend:
    image: python:3.12-alpine
    command: python3 -m http.server 8000
# nginx.conf (next to docker-compose.yml)
upstream app_backend {
    server backend:8000;
}

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://app_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 5s;
    }
}

Чтобы воспроизвести сценарий 502, выполните docker compose stop backend; чтобы вернуть всё обратно — docker compose start backend.

Начальные условия

  1. nginx установлен и работает.
  2. Конфигурация обратного прокси из родительской статьи, указывающая на 127.0.0.1:8000.
  3. Простейший backend запущен и подтверждена доступность через nginx:
    python3 -m http.server 8000 &
    curl -i http://localhost/
    
    Это должно вернуть HTTP/1.1 200 OK.

Инструкции

Часть 1 — Получите 502

  1. Остановите backend: kill %1 (или найдите его PID через jobs -l / pgrep -f http.server и завершите его).
  2. Выполните curl -i http://localhost/ и понаблюдайте за ответом.
  3. Проверьте самую новую запись в /var/log/nginx/error.log.

Часть 2 — Получите 504

  1. Перезапустите backend, но на этот раз сделайте его искусственно медленным (симуляция зависшего запроса):
    python3 -c "
    import http.server, time
    class Handler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            time.sleep(90)
            self.send_response(200)
            self.end_headers()
    http.server.HTTPServer(('', 8000), Handler).serve_forever()
    " &
    
  2. Установите короткий proxy_read_timeout 5s; в конфиге nginx (временно, для этого теста) и перезагрузите nginx.
  3. Выполните curl -i http://localhost/ и замерьте время до получения ответа.

Ожидаемые результаты

  • Часть 1: curl получает HTTP/1.1 502 Bad Gateway практически мгновенно (без ожидания — nginx сразу знает, что соединение было отказано). Лог ошибок показывает connect() failed (111: Connection refused) со ссылкой на 127.0.0.1:8000.
  • Часть 2: curl ждёт примерно 5 секунд (соответствует proxy_read_timeout), а затем получает HTTP/1.1 504 Gateway Timeout — заметно не мгновенно, и не полные 90 секунд, которые backend в итоге потратил бы на ответ.

Чек-лист диагностики

  • [ ] Подтвердили ли вы, что 502 был мгновенным (сбой на уровне соединения) против 504, задержанного примерно на настроенный таймаут (ответ, который просто не пришёл вовремя)?
  • [ ] Проверили ли вы error.log для части 1 и правильно интерпретировали Connection refused как «ничего не слушает», отличая это от сброса соединения или таймаута?
  • [ ] Для части 2: подтвердили ли вы прямым curl к 127.0.0.1:8000, минуя nginx полностью, что именно сам backend медленный (а не что-то не так конкретно на участке nginx→backend)?

Критерии успеха

Вы можете объяснить для любого будущего 502 или 504 в вашем собственном продакшн-nginx, в какую из этих двух категорий он попадает и какой должна быть ваша следующая диагностическая команда — без необходимости перечитывать родительскую статью.

Очистка / откат

# Остановить любой зависший тестовый backend:
pkill -f http.server

# Откатить временный proxy_read_timeout, если меняли:
sudo sed -i 's/proxy_read_timeout 5s;/proxy_read_timeout 60s;/' /etc/nginx/sites-available/app.conf
sudo nginx -t && sudo systemctl reload nginx

Убедитесь, что curl -i http://localhost/ снова возвращает нормальный 200 OK (с реально работающим backend), прежде чем считать окружение восстановленным.