nginx зворотний проксі - спробуйте A, потім B, потім A ще раз


22

Я намагаюся налаштувати 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), незважаючи на те, що сервер управління надсилає заголовок "Location". Зрештою, я його "працюю", змінивши рядок сторінки помилок наerror_page 502 =307 @handle_502;, таким чином, змушуючи всі відповіді сервера управління надсилатися назад клієнту з кодом 307. Це дуже хитро і небажано, тому що 1) немає контролю над тим, що nginx повинен робити далі, залежно від відповіді сервера управління (в ідеалі ми хочемо повторити спробу лише в тому випадку, якщо сервер управління повідомляє про успіх), і 2) не весь HTTP клієнти підтримують переспрямування HTTP (наприклад, користувачі curl та додатки, що використовують libcurl, повинні включати явні наступні переадресації).

Який правильний спосіб отримати nginx, щоб спробувати проксі-сервер на верхньому сервері A, потім B, потім A знову (в ідеалі, лише коли B повертає певний код статусу)?

Відповіді:


20

Ключові моменти:

  • Не турбуйтеся з 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блоці як вниз, тому він не намагається сервери впорядковувати послідовні запити. Я намагався додавати weight=1000000000 fail_timeout=1до першого запису без ефекту. Поки що я не знайшов жодного рішення, яке б не передбачало переадресацію клієнта.


Редагувати: Ще одне, що я хотів би знати - щоб отримати статус помилки від error_pageобробника, використовуйте цей синтаксис: error_page 502 = @handle_502;- знак рівності призведе до того, що nginx отримає статус помилки від обробника.


Редагувати: І я працював! Окрім error_pageвиправленого вище, все, що було потрібно, було враховувати recursive_error_pages!


1
Для мене proxy_next_upstreamзробив трюк (добре, що мій сценарій був не таким складним, як ваш), я просто хотів nginx спробувати наступний сервер, якщо сталася помилка, тому мені довелося додати proxy_next_upstream error timeout invalid_header non_idempotent;( non_idempotentтому що я хочу в основному пересилати POSTзапити).
Філіпп

1

Ви можете спробувати щось подібне

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;
    }

}

nginx не повторить спробу, a.example.netколи не вдалося один раз за тим самим запитом. Він надішле клієнтові помилку, що виникає при спробі підключення до нього b.example.net, яка не буде такою, яку вони очікували, якщо я також не реалізую проксі на сервері управління.
Володимир Пантелеев

І що було б з вашим конфігуратором у наступній ситуації: запит до висхідного потоку Помилка повернення, повернення вгору за течією В, помилка, потім ми знову намагаємося вгору за течією А, а також отримуємо збій (502)?
ALex_hha

Верхній В - сервер управління. Її мета - переконатися, що наступний запит на течію А буде успішним. Мета - спробувати вгору по течії А, якщо вона не вдалася спробувати вгору за течією В, якщо вона "вдалася" (використовуючи внутрішню конвенцію про "успіх"), спробуйте знову за течією А. Якщо моє питання було недостатньо зрозумілим, дайте мені знати, як я можу його покращити.
Володимир Пантелеев

Хм, припустимо, що вгору А по течії вниз, наприклад, деякі проблеми з обладнанням. Що зробить B вище за течією? Чи здатний він відповісти на запит клієнта?
ALex_hha

Ця проблема не полягає у відмові від апаратних збоїв. Ця проблема полягає у запуску верхових пакетів на вимогу. Якщо сервер управління (upstream B) не може повторно активувати бекенд (upstream A), тоді в ідеалі користувач повинен отримати відповідне повідомлення про помилку, але це не проблема, яку я намагаюся вирішити - проблема полягає в тому, щоб nginx повторити спробу Знову після A, у рамках того ж запиту.
Володимир Пантелеев
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.