Перезагрузка HAProxy и потеря запросов

Date November 7th, 2013 Author Vitaly Agapov

В другой витрине толстяк в фартуке мясника резал младенцев. Это была наглядная пропаганда общественной благотворительности.

Борис Виан «Пена дней»

lost

Разбирая странные ошибки фронтенда, возникновение которых удивительным образом коррелирует с моментами, когда я произвожу перезагрузку 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: , ,
Category: HAProxy, Linux | No comments »

Comments

Leave a comment

 Comment Form