Изящное завершение работы модулей Kubernetes с помощью TCP-соединений (загрузка Spring)
Я размещаю свои услуги в облаке Azure, иногда я получаю BackendConnectionFailure без какой-либо очевидной причины, после расследования я обнаружил корреляцию между этим исключением и автомасштабированием (уменьшением) почти в одну и ту же секунду в большинстве случаев.
Согласно документации льготный период прекращения действия по умолчанию составляет 30 секунд, что так и есть. Модуль будет помечен как завершающий, и балансировщик нагрузки больше не будет его рассматривать, поэтому больше не будет получать запросов. В соответствии с этим, если мой сервис занимает гораздо меньше времени, чем 30 секунд, мне не понадобится prestop hook или какая-либо специальная реализация в моем приложении (пожалуйста, поправьте меня, если я ошибаюсь).
Если предыдущий абзац верен, почему это исключение возникает относительно часто? Я думаю, что модуль помечается как завершающий, и балансировщик нагрузки больше не пересылает запросы на модуль, хотя должен.
Изменить 1:
Архитектура просто такая
Клиент -> Брандмауэр (azure) -> API(azure APIM) -> Microservices(Spring boot) -> backend(сторонний) или azure RDB в зависимости от службы
Я думаю, что исключение исходит из APIM, я нашел два шаблона для этого исключения:
Message The underlying connection was closed: The connection was closed unexpectedly. Exception type BackendConnectionFailure Failed method forward-request
Response time 10.0 s
Message The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. Exception type BackendConnectionFailure Failed method forward-request
Response time 3.6 ms
3 ответа
Spring Boot по умолчанию не выполняет постепенного завершения.
Приложение Spring Boot и его контейнер приложения (не контейнер linux) контролируют, что происходит с существующими соединениями в течение льготного периода завершения. Используемые протоколы и то, как клиент реагирует на "закрытие", также имеют значение.
Если вы дойдете до конца льготного периода, все будет полностью сброшено.
Kubernetes
Когда модуль удаляется в k8s, удаление конечной точки модуля из служб запускается одновременно с удалением модуля.SIGTERM
сигнал к контейнеру (ам).
На этом этапе узлы кластера будут перенастроены, чтобы удалить все правила, направляющие новый трафик в Pod. Любые существующие TCP-соединения с Pod/ контейнерами будут оставаться в режиме отслеживания соединений до тех пор, пока они не будут закрыты (клиентом, сервером или сетевым стеком).
Для служб HTTP Keep Alive или HTTP/2 клиент будет продолжать обращаться к той же конечной точке Pod, пока ему не будет сказано закрыть соединение (или оно не будет принудительно сброшено)
Приложение
Основные правила SIGTERM: приложение должно:
- Разрешить выполнение текущих транзакций
- Выполните любую требуемую очистку приложения
- Прекратите принимать новые подключения, на всякий случай
- Закройте все неактивные соединения, которые он может (сохраните активные запросы, веб-сокеты)
Некоторые обстоятельства, с которыми вы не сможете справиться (зависит от клиента)
- Поддерживающее соединение, которое не выполняет запрос в льготный период, не может получить
Connection: close
заголовок. Для этого потребуется закрытие FIN уровня TCP. - Медленный клиент с длительной передачей, в одностороннем порядке HTTP-передача, их придется ждать или принудительно закрывать.
Хотя клиенты поддержки активности должны соблюдать закрытие TCP FIN, каждый клиент реагирует по-разному. Microsoft APIM может быть чувствительным и вызывать ошибку, даже если это не повлияло на реальный мир. Лучше всего провести нагрузочное тестирование вашей установки во время масштабирования, чтобы увидеть, есть ли влияние в реальном мире.
Для получения дополнительной информации о весенней загрузке см.:
https://github.com/spring-projects/spring-boot/issues/4657 https://github.com/corentin59/spring-boot-graceful-shutdown https://github.com/SchweizerischeBundesbahnen/springboot-graceful-shutdown
При необходимости вы можете использовать предварительный сон. Хотя модуль немедленно удаляется из конечных точек службы, по-прежнему требуется время (10-100 мс) для отправки обновления конечной точки на каждый узел и для обновления ими iptables.
Когда ваши приложения получают SIGTERM
(после завершения работы модуля) ему необходимо сначала прекратить сообщать, что он готов (сбой readinessProbe
), но по-прежнему обслуживают запросы по мере их поступления от клиентов. Через определенное время (в зависимости от вашегоreadinessProbe
settings) вы можете закрыть приложение.
Для Spring Boot есть небольшая библиотека, которая делает именно это: springboot-graceful-shutdown