Назад | Перейти на главную страницу

Обратный прокси nginx - попробуйте восходящий поток A, затем B, затем снова A

Я пытаюсь настроить nginx в качестве обратного прокси с большим количеством внутренних серверов. Я хотел бы запускать бэкэнд по запросу (по первому входящему запросу), поэтому у меня есть процесс управления (управляемый HTTP-запросами), который запускает бэкэнд в зависимости от полученного запроса.

Моя проблема заключается в настройке nginx для этого. Вот что у меня есть на данный момент:

server {
    listen 80;
    server_name $DOMAINS;

    location / {
        # redirect to named location
        #error_page 418 = @backend;
        #return 418; # doesn't work - error_page doesn't work after redirect

        try_files /nonexisting-file @backend;
    }

    location @backend {
        proxy_pass http://$BACKEND-IP;
        error_page 502 @handle_502; # Backend server down? Try to start it
    }

    location @handle_502 { # What to do when the backend server is not up
        # Ping our control server to start the backend
        proxy_pass http://127.0.0.1:82;
        # Look at the status codes returned from control server
        proxy_intercept_errors on;
        # Fallback to error page if control server is down
        error_page 502 /fatal_error.html;
        # Fallback to error page if control server ran into an error
        error_page 503 /fatal_error.html;
        # Control server started backend successfully, retry the backend
        # Let's use HTTP 451 to communicate a successful backend startup
        error_page 451 @backend;
    }

    location = /fatal_error.html {
        # Error page shown when control server is down too
        root /home/nginx/www;
        internal;
    }
}

Это не работает - кажется, что nginx игнорирует любые коды состояния, возвращаемые управляющим сервером. Ни один из error_page директивы в @handle_502 определение местоположения, и код 451 отправляется клиенту как есть.

Я отказался от попыток использовать для этого внутреннее перенаправление nginx и попытался изменить сервер управления, чтобы он отправлял перенаправление 307 в то же место (чтобы клиент повторил тот же запрос, но теперь с запущенным внутренним сервером). Однако теперь nginx тупо перезаписывает код состояния тем, который он получил при попытке внутреннего запроса (502), несмотря на то, что сервер управления отправляет заголовок «Местоположение». Я наконец добился того, что он "заработал", изменив строку error_page на error_page 502 =307 @handle_502;, таким образом заставляя все ответы сервера управления отправляться обратно клиенту с кодом 307. Это очень хитроумно и нежелательно, потому что 1) нет контроля над тем, что nginx должен делать дальше в зависимости от ответа сервера управления (в идеале мы хотим повторить попытку бэкэнда только в том случае, если сервер управления сообщает об успехе), и 2) не все HTTP клиенты поддерживают переадресацию HTTP (например, пользователи curl и приложения, использующие libcurl, должны явно включать следующие переадресации).

Как правильно заставить nginx попытаться проксировать сервер A, затем B, затем A снова (в идеале, только когда B возвращает определенный код состояния)?

Ключевые моменты:

  • Не беспокойтесь о upstream блоки для аварийного переключения, если пинг одного сервера приведет к запуску другого - нет способа сообщить nginx (по крайней мере, не версии FOSS), что первый сервер снова работает. nginx будет проверять серверы по порядку при первом запросе, но не при последующих запросах, несмотря на любые backup, weight или fail_timeout настройки.
  • Вы должен включить recursive_error_pages при реализации аварийного переключения с использованием error_page и названные места.
  • включить proxy_intercept_errors для обработки кодов ошибок, отправленных вышестоящим сервером.
  • В = синтаксис (например, error_page 502 = @handle_502;) требуется для правильной обработки кодов ошибок в указанном месте. Если = не используется, nginx будет использовать код ошибки из предыдущего блока.

Исходный ответ / журнал исследования:


Вот лучше Я нашел обходной путь, который является улучшением, поскольку не требует перенаправления клиента:

upstream aba {
    server $BACKEND-IP;
    server 127.0.0.1:82 backup;
    server $BACKEND-IP  backup;
}

...

location / {
    proxy_pass http://aba;
    proxy_next_upstream error http_502;
}

Затем просто заставьте управляющий сервер вернуть 502 в случае «успеха» и надейтесь, что код никогда не будет возвращен серверными модулями.


Обновление: nginx продолжает отмечать первую запись в upstream block as down, поэтому серверы не проверяются по порядку при последовательных запросах. Я пробовал добавить weight=1000000000 fail_timeout=1 до первой записи без эффекта. До сих пор я не нашел решения, не предполагающего перенаправления клиента.


Изменить: еще одна вещь, о которой я хотел бы знать, - чтобы получить статус ошибки из error_page обработчик, используйте этот синтаксис: error_page 502 = @handle_502; - этот знак равенства заставит nginx получить статус ошибки от обработчика.


Изменить: И у меня это работает! В добавок к error_page исправить выше, все, что было нужно, это включить recursive_error_pages!

Вы можете попробовать что-то вроде следующего

upstream backend {
    server a.example.net;
    server b.example.net backup;
}

server {
    listen   80;
    server_name www.example.net;

    proxy_next_upstream error timeout http_502;

    location / {
        proxy_pass http://backend;
        proxy_redirect      off;
        proxy_set_header    Host              $host;
        proxy_set_header    X-Real-IP         $remote_addr;
        proxy_set_header    X-Forwarded-for   $remote_addr;
    }

}