Перезагрузка HAProxy и потеря запросов
Date November 7th, 2013 Author Vitaly Agapov
Разбирая странные ошибки фронтенда, возникновение которых удивительным образом коррелирует с моментами, когда я произвожу перезагрузку HAProxy (service haproxy reload), я выяснил, эта самая перезагрузка работает совсем не так, как я думал. Вопреки ожиданием при перезагрузке процесс вовсе не перечитывает конфигурационные файлы и продолжает работать дальше. На самом деле порождается новый процесс haproxy с новым pid'ом. Он через unix-сокет забирает у старого процесса все сессионные данные и просит того освободить порт и умереть. Дождавшись освобождения порта, он биндится на него и как ни в чём не бывало продолжает принимать новые соединения и обслуживать старые. Но…
Само собой всегда бывает какое-то "Но". В данном случае оно заключается в том, что между освобождением порта и его занятием новым процессом (передачей файлового дескриптора для этого TCP-порта) проходит довольно ощутимый промежуток времени, в течение которого все попытки установить TCP-соединение будут неудачными.
Само собой этот промежуток будет сильно разниться в зависимости от обстоятельств, но прикинуть его размеры можно, например, таким образом:
time /etc/init.d/haproxy reload
real 0m0.031s
user 0m0.000s
sys 0m0.004s
Или с помощью эксперимента, в котором я натравливаю на HAProxy мой ApacheBench:
ab -r -n 10000 -c 5 http://<ip>:81/
А в процессе делаю перезагрузку оного HAProxy.
В выхлопе увидим результаты эксперимента:
Time taken for tests: 8.518 seconds
Complete requests: 10000
Failed requests: 58
(Connect: 0, Receive: 29, Length: 0, Exceptions: 29)
29 раз соединение не установилось. Не так уж много (из 10 тысяч попыток, что хорошо совпадает со временем релоада), но сам факт того, что это число больше нуля, говорит о наличии проблем.
Возможно делу поможет SO_REUSEPORT, который вроде как должен появиться в ядре Linux 3.9. Тогда на один порт смогут прибиндиться сразу два процесса. Но раз счастье ещё не наступило, то возможен к применению такой вот грязный хак: в скрипте /etc/init.d/haproxy в обработчике аргумента reload делаем такую обвязку для вызова процедуры haproxy_reload:
iptables -I INPUT -p tcp --dport 81 --syn -j DROP haproxy_reload iptables -F
При таком решении во время перезагрузки все входящие SYN'ы будут отбрасываться netfilter'ом, вынуждая клиентов повторять их отправку. После завершения перезагрузки правило снимается, и SYN попадает к новому хозяину порта. Такие запросы будут иметь удлинённое время обработки, но хотя бы не отвалятся с ошибкой. А ab показывает в такой ситуации требуемый
Failed requests: 0
Само собой, если на сервере присутсвуют другие правиля iptables, то iptables -F заменяем на:
iptables -D INPUT -p tcp –dport 81 –syn -j DROP
Tags: HAProxy, iptables, Linux
Category:
HAProxy, Linux |
No comments »