nginx proxy ngược - thử ngược dòng A, rồi B, rồi lại A


22

Tôi đang cố gắng thiết lập nginx như một proxy ngược, với một số lượng lớn máy chủ phụ trợ. Tôi muốn khởi động các phụ trợ theo yêu cầu (theo yêu cầu đầu tiên xuất hiện), vì vậy tôi có một quy trình kiểm soát (được kiểm soát bởi các yêu cầu HTTP) khởi động phụ trợ tùy theo yêu cầu mà nó nhận được.

Vấn đề của tôi là cấu hình nginx để làm điều đó. Đây là những gì tôi có cho đến nay:

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;
    }
}

Điều này không hoạt động - nginx dường như bỏ qua bất kỳ mã trạng thái nào được trả về từ máy chủ điều khiển. Không có error_pagechỉ thị nào trong @handle_502vị trí hoạt động và mã 451 được gửi nguyên trạng cho máy khách.

Tôi đã từ bỏ việc cố gắng sử dụng chuyển hướng nginx nội bộ cho việc này và đã thử sửa đổi máy chủ điều khiển để phát ra một chuyển hướng 307 đến cùng một vị trí (để máy khách sẽ thử lại cùng một yêu cầu, nhưng bây giờ với máy chủ phụ trợ đã khởi động). Tuy nhiên, bây giờ nginx đang ngu ngốc ghi đè mã trạng thái với mã trạng thái nhận được từ lần thử yêu cầu phụ trợ (502), mặc dù máy chủ điều khiển đang gửi tiêu đề "Vị trí". Cuối cùng tôi đã làm cho nó "hoạt động" bằng cách thay đổi dòng error_page thànherror_page 502 =307 @handle_502;, do đó buộc tất cả các phản hồi của máy chủ điều khiển phải được gửi lại cho máy khách với mã 307. Điều này rất khó khăn và không mong muốn, bởi vì 1) không có quyền kiểm soát nginx nên làm gì tiếp theo tùy thuộc vào phản hồi của máy chủ điều khiển (lý tưởng là chúng tôi chỉ muốn thử lại phần phụ trợ chỉ khi máy chủ điều khiển báo cáo thành công) và 2) không phải tất cả HTTP khách hàng hỗ trợ chuyển hướng HTTP (ví dụ: người dùng curl và các ứng dụng sử dụng libcurl cần phải bật các chuyển hướng sau một cách rõ ràng).

Cách thích hợp để khiến nginx thử proxy vào máy chủ ngược dòng A, rồi B, rồi lại A (lý tưởng nhất là khi B trả về một mã trạng thái cụ thể)?

Câu trả lời:


20

Những điểm chính:

  • Đừng bận tâm với upstreamcác khối để chuyển đổi dự phòng, nếu ping một máy chủ sẽ đưa một máy chủ khác lên - không có cách nào để nói với nginx (ít nhất, không phải là phiên bản FOSS) rằng máy chủ đầu tiên sẽ hoạt động trở lại. nginx sẽ cố các máy chủ theo thứ tự trên các yêu cầu đầu tiên, nhưng không phải theo dõi các yêu cầu, dù bất kỳ backup, weighthoặc fail_timeoutcài đặt.
  • Bạn phải bật recursive_error_pageskhi triển khai chuyển đổi dự phòng bằng cách sử dụng error_pagevà đặt tên vị trí.
  • Cho phép proxy_intercept_errorsxử lý mã lỗi được gửi từ máy chủ ngược dòng.
  • Các =cú pháp (ví dụ error_page 502 = @handle_502;) là bắt buộc để xử lý một cách chính xác mã lỗi ở vị trí được đặt tên. Nếu =không được sử dụng, nginx sẽ sử dụng mã lỗi từ khối trước đó.

Câu trả lời gốc / nhật ký nghiên cứu theo:


Đây là một cách giải quyết tốt hơn mà tôi đã tìm thấy, đây là một cải tiến vì nó không yêu cầu chuyển hướng máy khách:

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;
}

Sau đó, chỉ cần yêu cầu máy chủ điều khiển trả về 502 khi "thành công" và hy vọng rằng mã không bao giờ được trả về bởi phụ trợ.


Cập nhật: nginx tiếp tục đánh dấu mục đầu tiên trong upstreamkhối là xuống, vì vậy nó không thử các máy chủ theo thứ tự liên tiếp. Tôi đã thử thêm weight=1000000000 fail_timeout=1vào mục đầu tiên mà không có hiệu quả. Cho đến nay tôi không tìm thấy bất kỳ giải pháp nào không liên quan đến chuyển hướng khách hàng.


Chỉnh sửa: Một điều nữa tôi muốn tôi biết - để có được trạng thái lỗi từ error_pagetrình xử lý, hãy sử dụng cú pháp này: error_page 502 = @handle_502;- dấu bằng đó sẽ khiến nginx nhận trạng thái lỗi từ trình xử lý.


Chỉnh sửa: Và tôi đã làm cho nó hoạt động! Ngoài cách error_pagekhắc phục ở trên, tất cả những gì cần thiết là cho phép recursive_error_pages!


1
Đối với tôi, proxy_next_upstreamthủ thuật này (kịch bản của tôi không phức tạp như của bạn), tôi chỉ muốn nginx dùng thử máy chủ tiếp theo nếu xảy ra lỗi, do đó tôi phải thêm proxy_next_upstream error timeout invalid_header non_idempotent;( non_idempotentvì tôi muốn chuyển tiếp POSTyêu cầu).
Philipp

1

Bạn có thể thử một cái gì đó như sau

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 sẽ không thử lại a.example.netsau khi nó thất bại một lần theo cùng một yêu cầu. Nó sẽ gửi cho khách hàng lỗi gặp phải khi cố gắng kết nối b.example.net, đây sẽ không phải là điều họ mong đợi trừ khi tôi cũng thực hiện ủy quyền trong máy chủ điều khiển.
Vladimir Panteleev

Và điều gì sẽ xảy ra với cấu hình của bạn trong tình huống tiếp theo: yêu cầu ngược dòng A trả về thất bại, trả về B ngược dòng không thành công, sau đó chúng ta lại thử ngược dòng A và cũng bị lỗi (502)?
ALex_hha

Thượng nguồn B là máy chủ điều khiển. Mục đích của nó là đảm bảo rằng yêu cầu tiếp theo ngược dòng A sẽ thành công. Mục tiêu là thử ngược dòng A, nếu thất bại hãy thử ngược dòng B, nếu nó "thành công" (sử dụng quy ước nội bộ của chúng tôi về "thành công"), hãy thử ngược dòng A một lần nữa. Nếu câu hỏi của tôi không đủ rõ ràng, hãy cho tôi biết làm thế nào tôi có thể cải thiện nó.
Vladimir Panteleev

Hmm, giả sử ngược dòng A không hoạt động, ví dụ như một số vấn đề về phần cứng. Điều gì sẽ làm cho thượng nguồn B? Có khả năng trả lại phản hồi cho yêu cầu từ khách hàng không?
ALex_hha

Vấn đề này không phải là về failover cho lỗi phần cứng. Vấn đề này là về việc bắt đầu phụ trợ ngược dòng theo yêu cầu. Nếu máy chủ điều khiển (ngược dòng B) không thể kích hoạt lại phụ trợ (ngược dòng A), thì lý tưởng nhất là người dùng sẽ nhận được thông báo lỗi thích hợp, nhưng đó không phải là vấn đề tôi đang cố gắng giải quyết - vấn đề là khiến nginx phải thử lại A sau B một lần nữa, trong cùng một yêu cầu.
Vladimir Panteleev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.