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

Как приостановить запросы Nginx во время обновления серверной части

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

В основном я бы хотел выполнить следующую последовательность операций:

  1. сказать Nginx, чтобы он прекратил пересылку запросов к моему бэкэнду, вместо этого удерживая их в своих асинхронных очередях;
  2. ждать и получать уведомления, когда все ожидающие запросы к бэкэнду будут выполнены;
  3. обновлять, перезапускать или иным образом работать с бездействующей серверной службой;
  4. быстро проверить работоспособность серверной службы, используя ее частный адрес;
  5. открыть шлюзы Nginx и пропустить все ожидающие запросы.

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

я нашел это решение, но он не обращается к пункту 2. Более того, ему нужен интерпретатор Lua, скомпилированный в Nginx, с любыми утечками памяти и проблемами безопасности, которые могут возникнуть.

Есть ли какой-нибудь трюк с настройкой или модуль Nginx, специально предназначенный для этой проблемы? Можно ли это сделать с помощью стандартного Nginx, возможно, проверив наличие контрольного файла?

Как другие администраторы решают эту проблему в целом?

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

Я предполагаю, что вы выполняете сценарий обслуживания с минимальным временем простоя.

Я бы порекомендовал вам использовать резервный сервер для удержания запроса в течение необходимых секунд <20> (или) даже <100> и перенаправления на исходный URI после перезапуска приложения.

Вы можете следить за потоком ниже nginx, где используется общее решение.

http://forum.nginx.org/read.php?2,177,177#msg-177

вы можете настроить nginx для выполнения сценария, чтобы массировать и передавать http-вызов некоторому процессу очереди, например beanstalkd, используя обработку ошибок на 502 BAD GATEWAY и / или 503 SERVICE UNAVAILABLE. (ошибка, когда ваша серверная служба недоступна).

затем, после обновления серверной части, извлеките запросы из beanstalkd и обработайте их в своей серверной службе.

Кроме того, если ваша внутренняя служба когда-либо непреднамеренно выйдет из строя, это может удвоиться как решение высокой доступности, чтобы не терять вызовы api. настроить jenkins / cron для автоматической проверки и обработки любой очереди beanstalk.

Я никогда этого не делал, но если бы я когда-нибудь попробовал, думаю, я бы использовал брандмауэр. Мне, вероятно, понадобится сценарий решения, которое будет выглядеть так:

  1. Сообщите брандмауэру разрешить существующие подключения, но фильтровать новые подключения на порту 80 (или 443).
  2. Дождитесь завершения ожидающих запросов к бэкэнду (даже если соединения nginx с клиентами все еще открыты).
  3. Обновите, перезапустите, что угодно.
  4. Скажите брандмауэру снова разрешить соединения.

Если шаги 2 + 3 не занимают слишком много времени, клиенты будут повторять попытку и в конечном итоге смогут подключиться на шаге 4. Если они займут слишком много времени, у клиентов будет тайм-аут, но это не проблема, поскольку терпение пользователей время истекло раньше, нет?

У решения есть некоторые уловки. Клиент может получить главную страницу, а затем он не сможет продолжить получение статических файлов (тогда как с учетом ваших задумок, возможно). Однако это не будет проблемой, если вы обслуживаете свои статические файлы с другой машины или CDN.

Кроме того, я считаю, что обычно кто-то будет беспокоиться о том, о чем вы беспокоитесь, только после того, как он уже настроил какое-то решение высокой доступности, например два сервера плюс IP-адрес, который можно перенести с одного сервера на другой. Когда вы перемещаете IP-адрес с одного сервера на другой, пользователи, у которых открыто соединение, отключаются. Я думаю, что это приемлемо, потому что эти пользователи получают что-то вроде пустой страницы в своем браузере, задаются вопросом, что, черт возьми, произошло, нажимают «перезагрузить», на этот раз это работает, и они больше не беспокоятся; они даже не помнят инцидент через несколько минут. Использование уловки с брандмауэром, чтобы избежать этого отключения, создало бы больше проблем, чем решило бы, потому что шаг 2 нужно было бы изменить, чтобы ждать, пока nginx завершит обслуживание запросов к клиентам, а это может занять слишком много времени, если у пользователя медленное соединение. В любом случае, я думаю, что высокая доступность имеет столько проблем, что она получит такой низкий приоритет, что это никогда не будет выполнено (если вы не Google или кто-то еще).

В поисках решения этой проблемы я наткнулся на небольшой репозиторий GitHub под названием бессонница это иллюстрирует простой и элегантный подход с использованием Lua. Предполагая, что у вас установлен модуль Lua, вы сначала захотите включить его в верхней части экрана. nginx.conf:

load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;

Затем в вашем http блок, настройте общую переменную Lua для отслеживания состояния приостановки / возобновления:

http {
    lua_shared_dict state 12k;
    init_by_lua_block {
        ngx.shared.state:set("suspend", false)
    }
    # rest of your http block
}

Затем в блоке сервера настройте секретное место для сообщения серверу о приостановке / возобновлении:

location = /suspend/MySuperSecretMagicString {
    if ($request_method = PUT) {
        content_by_lua_block {
            ngx.req.read_body()
            content = ngx.req.get_body_data()
            if (content == "go2sleep") then
                ngx.shared.state:set("suspend", true)
            else
                ngx.shared.state:set("suspend", false)
            end
        }
    }
}

И в вашем основном блоке местоположения добавьте еще один бит Lua:

location / {
    access_by_lua_block {
        while (ngx.shared.state:get("suspend") == true) do
            ngx.sleep(0.2)
        end
    }
    proxy_pass http://my-backend;
}

Теперь вы можете отправить запрос на переход в ждущий режим:

curl -X PUT -d go2sleep http://localhost/suspend/MySuperSecretMagicString

И чтобы выпустить все буферизованные запросы на бэкэнд, просто замените go2sleep чем-нибудь еще:

curl -X PUT -d UnleashTheHounds http://localhost/suspend/MySuperSecretMagicString

Обратите внимание, что у каждого приостановленного запроса будет свой рабочий, поэтому вам понадобится достаточно worker_connections справиться с ожидаемым отставанием. Увидеть хранилище бессонницы для дальнейших комментариев и некоторых дополнительных функций. Похожий, но более сложный подход можно найти в Basecamp антракт репозиторий.

Опять же, я не придумал эту технику. Вся заслуга по праву принадлежит пользователю GitHub "solso".

В твоем server раздел

if ( -f /etc/nginx/sites-enabled/maintenance.ngx ) {
    return 503;
}
error_page  503 /maintenance.html

Использовать

# cd /etc/nginx/sites-enabled && mv maintenance.ngx_ maintenance.ngx

и вернуться

# cd /etc/nginx/sites-enabled && mv maintenance.ngx maintenance.ngx_