HAProxy로드 밸런싱 서버를 실행하여로드를 여러 Apache 서버로 밸런싱합니다. 로드 밸런싱 알고리즘을 변경하려면 주어진 시간에 HAProxy를 다시로드해야합니다.
단일 패킷을 잃지 않고 서버를 다시로드해야한다는 사실을 제외 하고는이 모든 것이 잘 작동합니다 (재로드가 평균 99.76 %의 성공을 거두며 5 초 동안 초당 1000 건의 요청으로). 이것에 대해 많은 시간을 연구했으며 HAProxy 서버를 “정상적으로 다시로드”하는 다음 명령을 찾았습니다.
haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)
그러나 이것은 평범한 오래된 것에 비해 거의 또는 전혀 영향을 미치지 않으며 service haproxy reload
여전히 평균 0.24 % 감소합니다.
사용자로부터 단일 패킷 손실없이 HAProxy 구성 파일을 다시로드하는 방법이 있습니까?
답변
https://github.com/aws/opsworks-cookbooks/pull/40 및 이에 따라 http://www.mail-archive.com/haproxy@formilux.org/msg06885.html 에 따르면 다음을 수행 할 수 있습니다.
iptables -I INPUT -p tcp --dport $PORT --syn -j DROP
sleep 1
service haproxy restart
iptables -D INPUT -p tcp --dport $PORT --syn -j DROP
이는 다시 시작하기 전에 SYN을 삭제하는 효과가 있으므로 클라이언트는이 SYN이 새 프로세스에 도달 할 때까지 다시 보냅니다.
답변
Yelp는 세심한 테스트를 기반으로보다 정교한 접근 방식을 공유했습니다. 블로그 기사는 심도 깊은 다이빙이며 시간을 투자하여 가치를 충분히 인정할 가치가 있습니다.
tl; dr은 HAProxy가 다시로드되고 동일한 포트 ( SO_REUSEPORT
)에 두 개의 pid가 연결되어있는 동안 Linux tc (트래픽 제어) 및 iptables를 사용하여 SYN 패킷을 일시적으로 대기열에 넣습니다 .
ServerFault에 대한 전체 기사를 다시 게시하는 것이 불편합니다. 그럼에도 불구하고 관심을 끌기위한 몇 가지 발췌문이 있습니다.
각 시스템에서 실행되는 HAProxy로드 밸런서로 들어오는 SYN 패킷을 지연시킴으로써 HAProxy 재로드시 트래픽에 미치는 영향을 최소화 할 수 있으므로 사용자 트래픽에 큰 영향을 줄 염려없이 SOA 내에서 서비스 백엔드를 추가, 제거 및 변경할 수 있습니다.
# plug_manipulation.sh
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --buffer
service haproxy reload
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --release-indefinite
# setup_iptables.sh
iptables -t mangle -I OUTPUT -p tcp -s 169.254.255.254 --syn -j MARK --set-mark 1
# setup_qdisc.sh
## Set up the queuing discipline
tc qdisc add dev lo root handle 1: prio bands 4
tc qdisc add dev lo parent 1:1 handle 10: pfifo limit 1000
tc qdisc add dev lo parent 1:2 handle 20: pfifo limit 1000
tc qdisc add dev lo parent 1:3 handle 30: pfifo limit 1000
## Create a plug qdisc with 1 meg of buffer
nl-qdisc-add --dev=lo --parent=1:4 --id=40: plug --limit 1048576
## Release the plug
nl-qdisc-add --dev=lo --parent=1:4 --id=40: --update plug --release-indefinite
## Set up the filter, any packet marked with “1” will be
## directed to the plug
tc filter add dev lo protocol ip parent 1:0 prio 1 handle 1 fw classid 1:4
요점 : https://gist.github.com/jolynch/97e3505a1e92e35de2c0
놀라운 통찰력을 공유 한 Yelp를 응원합니다.
답변
진정한 다운 타임없이 haproxy를 다시로드하는 또 다른 훨씬 간단한 방법이 있습니다. iptables flipping 이라는 이름이 붙습니다 (이 기사는 실제로 Yelp 솔루션에 대한 Unbounce 응답입니다). 긴 재로드에 문제를 일으킬 수있는 패킷을 삭제할 필요가 없으므로 허용되는 답변보다 깨끗합니다.
간단히, 솔루션은 다음 단계로 구성됩니다.
- 트래픽을 수신하는 첫 번째 활성 인스턴스와 트래픽을 수신하지 않는 대기중인 두 번째 인스턴스 인 haproxy 인스턴스가 있습니다.
- 대기 인스턴스는 언제든지 재구성 (재로드)합니다.
- 새 구성으로 대기가 준비되면 모든 새로운 연결을 대기 노드로 전환하여 새 활성이 됩니다. Unbounce는 간단한
iptable
명령으로 플립을 수행하는 bash 스크립트를 제공 합니다 . - 잠시 동안 두 개의 활성 인스턴스가 있습니다. 이전 활성에 대한 열린 연결 이 중단 될 때까지 기다려야 합니다. 시간은 서비스 동작 및 연결 유지 설정에 따라 다릅니다.
- 새 활성 상태 가되는 이전 활성 중지로의 트래픽 -1 단계로 돌아갑니다.
또한이 솔루션은 모든 종류의 서비스 (nginx, apache 등)에 적용 할 수 있으며 온라인 상태가되기 전에 대기 구성을 테스트 할 수 있으므로 내결함성이 있습니다.
답변
편집 : 내 대답은 커널이 SO_REUSEPORT로 열어야 할 가장 최근 포트로 트래픽을 보내는 반면, 실제로는 의견 중 하나에 설명 된 것처럼 모든 프로세스로 트래픽을 보냅니다. 다시 말해, iptables 댄스는 여전히 필요합니다. 🙁
SO_REUSEPORT를 지원하는 커널을 사용하는 경우이 문제가 발생하지 않아야합니다.
haproxy가 다시 시작될 때 수행하는 프로세스는 다음과 같습니다.
1) 포트를 열 때 SO_REUSEPORT를 설정하십시오 ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798 )
2) 포트를 열어보십시오 (SO_REUSEPORT로 성공)
3) 성공하지 못하면 이전 프로세스에 신호를 보내 포트를 닫고 10ms 동안 기다렸다가 다시 시도하십시오. ( https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/haproxy.c#L1554-L1577 )
Linux 3.9 커널에서 처음 지원되었지만 일부 배포판에서는이를 백 포트했습니다. 예를 들어 2.6.32-417.el6의 EL6 커널이이를 지원합니다.
답변
설정 내용과 우아한 리로드 문제를 해결하는 방법을 설명하겠습니다.
HAproxy를 실행하고 유지하는 2 개의 노드로 일반적인 설정이 있습니다. Keepalived는 dummy0 인터페이스를 추적하므로 “ifconfig dummy0 down”을 수행하여 강제로 전환 할 수 있습니다.
진짜 문제는, 왜 “하프 프록시 리로드”가 여전히 모든 ESTABLISHED 연결을 삭제하는지 모른다는 것입니다. 일부 시나리오에서는 IP 주소가 적합하지 않습니다.
대신 CONNMARK dirty hack을 사용하여 NEW 연결에 속하는 패킷을 표시 한 다음 표시된 패킷을 다른 노드로 리디렉션하기로 결정했습니다.
iptables 규칙 세트는 다음과 같습니다.
iptables -t mangle -A PREROUTING -i eth1 -d 123.123.123.123/32 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
처음 두 규칙은 새 흐름에 속하는 패킷을 표시합니다 (123.123.123.123은 프런트 엔드를 바인딩하기 위해 haproxy에서 사용되는 keepalived VIP입니다).
세 번째 및 네 번째 규칙은 패킷 FIN / RST 패킷을 표시합니다. TEE가 FIN / RST 패킷을 “무시”하는 이유를 모르겠습니다.
다섯 번째 규칙은 표시된 모든 패킷의 복제본을 다른 HAproxy (192.168.0.2)로 보냅니다.
여섯 번째 규칙은 원래 플로우에 도달하지 못하도록 새 플로우에 속하는 패킷을 삭제합니다.
인터페이스에서 rp_filter를 비활성화하면 커널에서 해당 화성 패킷을 삭제합니다.
마지막으로 돌아 오는 패킷을 염두에 두십시오! 필자의 경우 비대칭 라우팅이 있습니다 (요청은 클라이언트-> haproxy1-> haproxy2-> 웹 서버에오고 응답은 웹 서버-> haproxy1-> 클라이언트에서옵니다). 잘 작동합니다.
가장 우아한 해결책은 iproute2를 사용하여 전환을 수행하는 것이지만 첫 번째 SYN 패킷에서만 작동했습니다. ACK (3 방향 핸드 셰이크의 세 번째 패킷)를 받았을 때 표시하지 않았습니다. (TEE 대상과 작동하는 것을 보자 마자 조사에 많은 시간을 할애 할 수 없었습니다. 물론 iproute2로 자유롭게 사용해보십시오.
기본적으로 “유예 재 장전”은 다음과 같이 작동합니다.
- iptables 규칙 세트를 활성화하고 새 연결이 다른 HAproxy로 이동하는 것을 즉시 봅니다.
- “배수”프로세스를 감독하기 위해 “netstat -an | grep ESTABLISHED | wc -l”을 주시합니다.
- 연결이 몇 개 (또는 0 개) 만 있으면 “ifconfig dummy0 down”을 유지하여 강제로 장애 조치를 유지하므로 모든 트래픽이 다른 HAproxy로 이동합니다.
- iptables 규칙 세트를 제거합니다
- ( “비 선점”keepalive 구성에만 해당) “ifconfig dummy0 up”.
IPtables 규칙 세트는 시작 / 중지 스크립트에 쉽게 통합 될 수 있습니다.
#!/bin/sh
case $1 in
start)
echo Redirection for new sessions is enabled
# echo 0 > /proc/sys/net/ipv4/tcp_fwmark_accept
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
iptables -t mangle -A PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -A PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -A PREROUTING -i eth1 -m mark --mark 1 -j DROP
;;
stop)
iptables -t mangle -D PREROUTING -i eth1 -m mark --mark 1 -j DROP
iptables -t mangle -D PREROUTING -i eth1 -m mark ! --mark 0 -j TEE --gateway 192.168.0.2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags RST RST -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -i eth1 -p tcp --tcp-flags FIN FIN -j MARK --set-mark 2
iptables -t mangle -D PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -D PREROUTING -i eth1 ! -d 123.123.123.123 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1
echo Redirection for new sessions is disabled
;;
esac