ย้อนกลับพร็อกซี nginx - ลองอัปสตรีม A จากนั้น B จากนั้น A อีกครั้ง


22

ฉันกำลังพยายามตั้งค่า nginx เป็น reverse proxy พร้อมเซิร์ฟเวอร์ backend จำนวนมาก ฉันต้องการเริ่มต้นแบ็กเอนด์ตามคำขอ (ตามคำขอแรกที่เข้ามา) ดังนั้นฉันจึงมีกระบวนการควบคุม (ควบคุมโดยคำขอ HTTP) ซึ่งเริ่มต้นแบ็กเอนด์ขึ้นอยู่กับคำขอที่ได้รับ

ปัญหาของฉันคือการกำหนดค่า nginx ให้ทำ นี่คือสิ่งที่ฉันมี:

server {
    listen 80;
    server_name $DOMAINS;

    location / {
        # redirect to named location
        #error_page 418 = @backend;
        #return 418; # doesn't work - error_page doesn't work after redirect

        try_files /nonexisting-file @backend;
    }

    location @backend {
        proxy_pass http://$BACKEND-IP;
        error_page 502 @handle_502; # Backend server down? Try to start it
    }

    location @handle_502 { # What to do when the backend server is not up
        # Ping our control server to start the backend
        proxy_pass http://127.0.0.1:82;
        # Look at the status codes returned from control server
        proxy_intercept_errors on;
        # Fallback to error page if control server is down
        error_page 502 /fatal_error.html;
        # Fallback to error page if control server ran into an error
        error_page 503 /fatal_error.html;
        # Control server started backend successfully, retry the backend
        # Let's use HTTP 451 to communicate a successful backend startup
        error_page 451 @backend;
    }

    location = /fatal_error.html {
        # Error page shown when control server is down too
        root /home/nginx/www;
        internal;
    }
}

วิธีนี้ใช้ไม่ได้ดูเหมือนว่า nginx จะไม่สนใจรหัสสถานะใด ๆ ที่ส่งคืนจากเซิร์ฟเวอร์ควบคุม ไม่มีerror_pageคำสั่งในการ@handle_502ทำงานในสถานที่และรหัส 451 ได้รับการส่งไปยังลูกค้า

ฉันเลิกพยายามใช้การเปลี่ยนเส้นทาง nginx ภายในสำหรับเรื่องนี้และพยายามแก้ไขเซิร์ฟเวอร์ควบคุมเพื่อปล่อยการเปลี่ยนเส้นทาง 307 ไปยังตำแหน่งเดียวกัน (เพื่อให้ลูกค้าจะลองคำขอเดียวกันอีกครั้ง แต่ตอนนี้เริ่มต้นเซิร์ฟเวอร์แบ็กเอนด์) อย่างไรก็ตามตอนนี้ nginx กำลังเขียนทับรหัสสถานะอย่างโง่เขลาด้วยรหัสที่ได้จากการพยายามแบ็กเอนด์ (502) แม้ว่าเซิร์ฟเวอร์ควบคุมจะส่งส่วนหัว "ตำแหน่ง" ในที่สุดฉันก็ได้ "ทำงาน" โดยเปลี่ยนบรรทัด error_page เป็นerror_page 502 =307 @handle_502;ดังนั้นบังคับให้เซิร์ฟเวอร์ควบคุมทั้งหมดตอบกลับถูกส่งกลับไปยังไคลเอนต์ด้วยรหัส 307 นี่คือแฮ็กและไม่พึงประสงค์เนื่องจาก 1) ไม่มีการควบคุมสิ่งที่ nginx ควรทำต่อไปขึ้นอยู่กับการตอบสนองของเซิร์ฟเวอร์ควบคุม (โดยเฉพาะอย่างยิ่งเราต้องการลองแบ็กเอนด์เฉพาะในกรณีที่เซิร์ฟเวอร์ควบคุมรายงานว่าสำเร็จ) และ 2) ไม่ใช่ HTTP ทั้งหมด ไคลเอนต์รองรับการเปลี่ยนเส้นทาง HTTP (เช่นผู้ใช้ curl และแอปพลิเคชันที่ใช้ libcurl ต้องเปิดใช้งานการเปลี่ยนเส้นทางต่อไปนี้อย่างชัดเจน)

วิธีที่ถูกต้องในการทำให้ nginx พยายามพร็อกซีไปยังเซิร์ฟเวอร์ upstream A คือ B และจากนั้น A อีกครั้ง (โดยเฉพาะเมื่อ B ส่งคืนรหัสสถานะเฉพาะ)

คำตอบ:


20

ประเด็นสำคัญ:

  • ไม่ต้องกังวลกับการupstreamบล็อกล้มเหลวหากการส่ง Ping ไปยังเซิร์ฟเวอร์หนึ่งจะทำให้เกิดเซิร์ฟเวอร์ใหม่ขึ้น - ไม่มีทางที่จะบอก nginx (อย่างน้อยไม่ใช่รุ่น FOSS) ว่าเซิร์ฟเวอร์เครื่องแรกขึ้นอีกครั้ง Nginx จะพยายามเซิร์ฟเวอร์ในการสั่งซื้อในคำขอแรก แต่ไม่ได้ติดตามการร้องขอแม้ใด ๆbackup, weightหรือfail_timeoutการตั้งค่า
  • คุณต้องเปิดใช้recursive_error_pagesงานเมื่อใช้งาน failover โดยใช้error_pageและระบุตำแหน่ง
  • เปิดใช้งานproxy_intercept_errorsเพื่อจัดการรหัสข้อผิดพลาดที่ส่งจากเซิร์ฟเวอร์ต้นน้ำ
  • ต้อง=ใช้ไวยากรณ์ (เช่นerror_page 502 = @handle_502;) เพื่อจัดการรหัสข้อผิดพลาดอย่างถูกต้องในตำแหน่งที่ระบุ หาก=ไม่ได้ใช้ nginx จะใช้รหัสข้อผิดพลาดจากบล็อกก่อนหน้า

คำตอบเดิม / บันทึกการวิจัยมีดังนี้:


นี่เป็นวิธีแก้ปัญหาที่ดีกว่าที่ฉันพบซึ่งเป็นการปรับปรุงเนื่องจากไม่ต้องการการเปลี่ยนเส้นทางไคลเอ็นต์:

upstream aba {
    server $BACKEND-IP;
    server 127.0.0.1:82 backup;
    server $BACKEND-IP  backup;
}

...

location / {
    proxy_pass http://aba;
    proxy_next_upstream error http_502;
}

จากนั้นให้เซิร์ฟเวอร์ควบคุมส่งคืน 502 ใน "ความสำเร็จ" และหวังว่าโค้ดจะไม่ถูกส่งคืนโดยแบ็กเอนด์


อัปเดต: nginx ทำเครื่องหมายรายการแรกในupstreamบล็อกเหมือนเดิมดังนั้นจึงไม่ลองเซิร์ฟเวอร์ตามลำดับการร้องขอที่ต่อเนื่อง ฉันพยายามเพิ่มweight=1000000000 fail_timeout=1ในรายการแรกโดยไม่มีผลกระทบ จนถึงตอนนี้ฉันยังไม่พบวิธีแก้ไขปัญหาใด ๆ ที่ไม่เกี่ยวข้องกับการเปลี่ยนเส้นทางไคลเอ็นต์


แก้ไข: อีกสิ่งหนึ่งที่ฉันอยากรู้ - เพื่อรับสถานะข้อผิดพลาดจากerror_pageตัวจัดการใช้ไวยากรณ์นี้: error_page 502 = @handle_502;- นั่นเท่ากับเครื่องหมายจะทำให้ nginx เพื่อรับสถานะข้อผิดพลาดจากตัวจัดการ


แก้ไข: และฉันก็ทำงานได้! นอกเหนือจากการerror_pageแก้ไขข้างต้นสิ่งที่จำเป็นต้องมีก็คือการเปิดใช้recursive_error_pages!


1
สำหรับฉันproxy_next_upstreamแล้วการหลอกลวง (สถานการณ์ของฉันไม่ซับซ้อนเหมือนของคุณ) ฉันแค่อยากให้ nginx ลองเซิร์ฟเวอร์ถัดไปหากมีข้อผิดพลาดเกิดขึ้นดังนั้นฉันจึงต้องเพิ่มproxy_next_upstream error timeout invalid_header non_idempotent;( non_idempotentเพราะฉันต้องการส่งต่อPOSTการร้องขอเป็นหลัก)
Philipp

1

คุณสามารถลองสิ่งต่อไปนี้

upstream backend {
    server a.example.net;
    server b.example.net backup;
}

server {
    listen   80;
    server_name www.example.net;

    proxy_next_upstream error timeout http_502;

    location / {
        proxy_pass http://backend;
        proxy_redirect      off;
        proxy_set_header    Host              $host;
        proxy_set_header    X-Real-IP         $remote_addr;
        proxy_set_header    X-Forwarded-for   $remote_addr;
    }

}

nginx จะไม่ลองอีกครั้งa.example.netหลังจากล้มเหลวหนึ่งครั้งในคำขอเดียวกัน มันจะส่งข้อผิดพลาดที่พบเมื่อพยายามเชื่อมต่อไปยังไคลเอนต์b.example.netซึ่งจะไม่เป็นสิ่งที่พวกเขาคาดหวังนอกจากว่าฉันจะใช้พร็อกซีในเซิร์ฟเวอร์ควบคุมเช่นกัน
Vladimir Panteleev

และสิ่งที่จะเกิดขึ้นกับการกำหนดค่าของคุณในสถานการณ์ต่อไป: ขอให้ส่งคืนค่าอัปสตรีม A, อัปเดต B ย้อนกลับล้มเหลวจากนั้นเราลองอัปสตรีม A อีกครั้งและรับอีกครั้ง (502)
ALex_hha

Upstream B เป็นเซิร์ฟเวอร์ควบคุม โดยมีวัตถุประสงค์คือเพื่อให้แน่ใจว่าคำขอถัดไปที่ upstream A จะประสบความสำเร็จ เป้าหมายคือการลองอัปสตรีม A หากล้มเหลวลองอัปสตรีม B หาก "ประสบความสำเร็จ" (โดยใช้แบบแผน "ความสำเร็จ" ภายในของเรา) ให้ลองอัปสตรีม A อีกครั้ง หากคำถามของฉันยังไม่ชัดเจนโปรดแจ้งให้ฉันทราบว่าฉันสามารถปรับปรุงได้อย่างไร
Vladimir Panteleev

อืมสมมติว่า upstream A ไม่ทำงานเช่นปัญหาฮาร์ดแวร์บางอย่าง อะไรจะทำให้ B ต้นน้ำ สามารถตอบกลับคำขอจากลูกค้าได้หรือไม่
ALex_hha

ปัญหานี้ไม่ได้เกี่ยวกับความล้มเหลวสำหรับความล้มเหลวของฮาร์ดแวร์ ปัญหานี้เกี่ยวกับการเริ่มต้นแบ็กเอนด์ตามคำขอ หากเซิร์ฟเวอร์ควบคุม (อัปสตรีม B) ไม่สามารถเปิดใช้งานแบ็กเอนด์ (อัปสตรีม A) อีกครั้งดังนั้นผู้ใช้ควรได้รับข้อความแสดงข้อผิดพลาดที่เหมาะสม แต่ไม่ใช่ปัญหาที่ฉันพยายามแก้ไข - ปัญหากำลังทำให้ nginx ลองอีกครั้ง A หลังจาก B อีกครั้งภายในคำขอเดียวกัน
Vladimir Panteleev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.