शून्य पैकेट नुकसान के साथ HAProxy सुशोभित रीलोड


42

मैं कई अपाचे सर्वर पर लोड को संतुलित करने के लिए एक HAProxy लोड संतुलन सर्वर चला रहा हूं। मुझे लोड संतुलन एल्गोरिदम को बदलने के लिए किसी भी समय 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 config फाइल को फिर से लोड करने का कोई तरीका है?


6
यदि आपको जरूरत है कि एक बेहतर समाधान HAproxy के एक से अधिक उदाहरणों को चलाने के लिए होगा जहां आप एक सेवा को फिर से लोड करने के लिए ले जा सकते हैं, इसे वापस रख सकते हैं और दूसरे के लिए दोहरा सकते हैं।
यूनिक्स

जवाबों:


32

के अनुसार 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 को फिर से भेज दें।



इन दोनों आज्ञाओं ने मुझे यह दिया:iptables v1.4.14: invalid port/service
दिमित्री DB

5
@DmitriDB आप $PORTवास्तविक पोर्ट haproxyपर सुन रहे हैं के साथ प्रतिस्थापित करने वाले हैं। यदि हाईप्रोफाइल कई पोर्ट्स पर सुन रहा है, तो इसके --dport $PORTसाथ बदलें --dports $PORTS_SEPARATED_BY_COMMAS, जैसे, लिखें --dports 80,443
पेपोलुआन

1
iptables 1.4.7 (Centos 6.7) - यदि आप का उपयोग करना चाहते हैं, तो आपको mulitport को भी निर्दिष्ट करना होगा। तो इसका "iptables -I INPUT -p tcp -m multiport --dports 80,443 --syn -j DROP" और इसी तरह -D
carpii

25

येल्प ने सूक्ष्म परीक्षण के आधार पर अधिक परिष्कृत दृष्टिकोण साझा किया। ब्लॉग लेख एक गहरा गोता है, और इसे पूरी तरह से सराहना करने के लिए समय के निवेश के लायक है।

ट्रू ज़ीरो डाउनटाइम HAProxy रीलोडेड

tl; ड्र; लिनक्स tc (ट्रैफिक कंट्रोल) और iptables का उपयोग अस्थायी रूप से SYN पैकेट को कतारबद्ध करने के लिए करते हैं जबकि HAProxy पुनः लोड हो रहा है और इसमें एक ही पोर्ट से जुड़े दो ग्रिड हैं ( SO_REUSEPORT)।

मैं सर्वरफॉल्ट पर पूरे लेख को फिर से प्रकाशित करने के लिए सहज नहीं हूं; फिर भी, यहाँ कुछ अंश हैं जो आपकी रुचि को बढ़ाते हैं:

प्रत्येक मशीन पर चलने वाले हमारे HAProxy लोड बैलेंसरों में SYN पैकेट आने में देरी करके, हम HAProxy रीलोड्स के दौरान यातायात को न्यूनतम रूप से प्रभावित करने में सक्षम हैं, जो हमें उपयोगकर्ता ट्रैफ़िक को प्रभावित किए बिना एसओए के भीतर सेवा बैकेंड जोड़ने, हटाने और बदलने की अनुमति देता है।

# 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

Gist: https://gist.github.com/jolynch/97e3505a1e92e35de2b0

ऐसे अद्भुत अंतर्दृष्टि साझा करने के लिए येल्प को चीयर्स।


बहुत बढ़िया लिंक! लिंक लिंक की अवधि समाप्त होने पर शायद आप इसे यहाँ संक्षेप में बताना चाहेंगे। यही कोई कारण नहीं है।
मैट

@Matt ने कुछ अंश और कोड नमूने जोड़े
स्टीव जाॅनसन

8

सच्चा शून्य डाउनटाइम के साथ हैप्रोक्स को फिर से लोड करने का एक और बहुत सरल तरीका है - इसका नाम iptables flipping है (लेख वास्तव में येल्प सॉल्यूशन के लिए अनबॉइंट रिस्पॉन्स है)। यह स्वीकृत उत्तर की तुलना में क्लीनर है क्योंकि किसी भी पैकेट को छोड़ने की आवश्यकता नहीं है जो लंबे समय तक लोड के साथ समस्याएं पैदा कर सकता है।

संक्षेप में, समाधान में निम्नलिखित चरण होते हैं:

  1. आइए एक जोड़ी हाइप्रोक्स उदाहरण हैं - पहला सक्रिय जो एक ट्रैफ़िक प्राप्त करता है और दूसरा स्टैंडबाय में जिसे कोई ट्रैफ़िक प्राप्त नहीं होता है।
  2. आप किसी भी समय स्टैंडबाय आवृत्ति (पुनः लोड) को फिर से कॉन्फ़िगर करें।
  3. एक बार स्टैंडबाय नए कॉन्फ़िगरेशन के साथ तैयार हो जाने के बाद आप सभी नए कनेक्शन को स्टैंडबाय नोड में बदल देते हैं जो नया सक्रिय हो जाता है । Unbounce बैश स्क्रिप्टiptable प्रदान करता है जो कुछ सरल कमांड के साथ फ्लिप करता है
  4. एक पल के लिए आपके पास दो सक्रिय उदाहरण हैं। आपको तब तक प्रतीक्षा करने की आवश्यकता है जब तक कि पुराने सक्रिय कनेक्शन को खोला नहीं जाएगा। समय आपकी सेवा व्यवहार और रख-रखाव सेटिंग्स पर निर्भर करता है।
  5. पुराने सक्रिय स्टॉप पर ट्रैफ़िक जो नए स्टैंडबाय बन जाते हैं - आप चरण 1 में वापस आ गए हैं।

इसके अलावा समाधान को किसी भी प्रकार की सेवा (नगनेक्स, अपाचे आदि) के लिए अपनाया जा सकता है और अधिक दोष सहिष्णु है क्योंकि आप ऑनलाइन जाने से पहले स्टैंडबाय कॉन्फ़िगरेशन का परीक्षण कर सकते हैं।


4

संपादित करें: मेरा जवाब यह धारणा बनाता है कि कर्नेल केवल SO_REUSEPORT के साथ खोले जाने के लिए सबसे हालिया पोर्ट पर ट्रैफ़िक भेजता है, जबकि यह वास्तव में सभी प्रक्रियाओं को ट्रैफ़िक भेजता है जैसा कि टिप्पणियों में से एक में वर्णित है। दूसरे शब्दों में, iptables नृत्य की अभी भी आवश्यकता है। :(

यदि आप एक कर्नेल पर हैं जो SO_REUSEPORT का समर्थन करता है, तो यह समस्या नहीं होनी चाहिए।

प्रक्रिया जो दोबारा शुरू होती है, वह है:

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/haproc.c#L1554-L1577 )

यह पहली बार लिनक्स 3.9 कर्नेल में समर्थित था लेकिन कुछ डिस्ट्रो ने इसे वापस भेज दिया है। उदाहरण के लिए, 2.6.32-417.el6 से EL6 कर्नेल इसका समर्थन करते हैं।


यह SO_REUSEPORTकुछ विशेष परिदृश्यों के साथ होगा - विशेष रूप से भारी यातायात के तहत। जब SYN को पुरानी हैप्रोक्सी प्रक्रिया में भेजा जाता है और उसी क्षण यह सुनने वाले सॉकेट को बंद कर देता है जिसके परिणामस्वरूप RST होता है। ऊपर अन्य उत्तर में उल्लिखित येल्प लेख देखें।
gertas

4
यह बेकार है ... बस इस मुद्दे को संक्षेप में बताने के लिए, लिनक्स एक विशेष पोर्ट पर सुन रहे सभी प्रक्रियाओं के बीच नए कनेक्शन वितरित करता है जब SO_REUSEPORT का उपयोग किया जाता है, इसलिए एक छोटा समय है जहां पुरानी प्रक्रिया अभी भी कनेक्शन को अपनी कतार में डाल देगी।
जेसन स्टब्स

2

मैं अपना सेटअप समझाता हूँ और मैंने कैसे सुंदर रिलोड्स हल किये हैं:

मेरे पास एक विशिष्ट सेटअप है जिसमें 2 नोड्स HAproxy चल रहे हैं और रखवाले हैं। Keepalived पटरियों इंटरफ़ेस dummy0, इसलिए मैं एक "ifconfig dummy0 डाउन" कर सकता हूं ताकि स्विच ओवर किया जा सके।

असली समस्या यह है कि, मुझे नहीं पता कि क्यों, एक "हाइपोक्सिक्‍स रीलोड" अभी भी सभी स्थापित कनेक्शनों को गिरा देता है :( मैंने गार्ट्स द्वारा प्रस्तावित "आईपीप्लेट फ़्लिपिंग" की कोशिश की, लेकिन मुझे कुछ मुद्दे मिले क्योंकि यह गंतव्य पर एक NAT करता है। आईपी ​​पता, जो कुछ परिदृश्यों में एक उपयुक्त समाधान नहीं है।

इसके बजाय, मैंने नए कनेक्शन से संबंधित पैकेटों को चिह्नित करने के लिए एक CONNMARK गंदे हैक का उपयोग करने का फैसला किया, और फिर उन चिह्नित पैकेटों को दूसरे नोड पर पुनर्निर्देशित किया।

यहाँ 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 (192.168.0.2) को भेजता है।

छठे नियम अपने मूल गंतव्य तक पहुंचने से रोकने के लिए नए प्रवाह से संबंधित पैकेटों को गिराता है।

याद रखें कि इंटरफेस या कर्नेल पर rp_filter को निष्क्रिय करना उन मार्शल पैकेट को गिरा देगा।

और अंतिम लेकिन कम से कम, लौटने वाले पैकेट का मन नहीं! मेरे मामले में असममित मार्ग है (अनुरोध ग्राहक के लिए आते हैं -> haproxy1 -> haproxy2 -> वेबसर्वर, और उत्तर वेबसर्वर से जाते हैं -> haproxy1 -> ग्राहक), लेकिन यह प्रभावित नहीं करता है। यह बढ़िया काम करता है।

मुझे पता है कि सबसे सुंदर समाधान divert करने के लिए iproute2 का उपयोग करना होगा, लेकिन यह केवल पहले SYN पैकेट के लिए काम करता था। जब इसे ACK (3-रास्ता हैंडशेक का तीसरा पैकेट) प्राप्त हुआ, तो उसने इसे चिह्नित नहीं किया :( मैं जांच के लिए ज्यादा समय नहीं दे सका, जैसे ही मैंने देखा कि यह टीईई लक्ष्य के साथ काम करता है, इसने इसे वहीं छोड़ दिया। बेशक, यह iproute2 के साथ आज़माने के लिए स्वतंत्र महसूस करें।

मूल रूप से, "सुशोभित रीलोड" इस तरह काम करता है:

  1. मैं iptables नियमों को सक्षम करता हूं और तुरंत नए कनेक्शन को अन्य HAproxy में जा रहा हूं।
  2. मैं "netstat -an | grep ESTABLISHED | wc -l" पर नजर रखता हूं ताकि "ड्रेनिंग" प्रक्रिया की देखरेख कर सकूं।
  3. एक बार बस कुछ (या शून्य) कनेक्शन होते हैं, "ifconfig dummy0 डाउन" जो कि विफलता को रखने के लिए मजबूर किया जाता है, इसलिए सभी ट्रैफ़िक दूसरे HAproxy में जाएंगे।
  4. मैं iptables नियमों को हटा देता हूं
  5. (केवल "नॉन-प्रीमेप्टिंग" कीपैलिव कॉन्फिग "के लिए" 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.