nginx 역방향 프록시 - 업스트림 A, B, A를 다시 시도합니다.

nginx 역방향 프록시 - 업스트림 A, B, A를 다시 시도합니다.

많은 수의 백엔드 서버를 사용하여 nginx를 역방향 프록시로 설정하려고 합니다. 요청 시(들어오는 첫 번째 요청에 대해) 백엔드를 시작하고 싶기 때문에 수신되는 요청에 따라 백엔드를 시작하는 제어 프로세스(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_502451 코드는 있는 그대로 클라이언트에 전송됩니다.

나는 이를 위해 내부 nginx 리디렉션을 사용하려는 시도를 포기하고 동일한 위치로 307 리디렉션을 내보내도록 제어 서버를 수정하려고 시도했습니다(클라이언트가 동일한 요청을 다시 시도하지만 이제 백엔드 서버가 시작됨). 그러나 이제 nginx는 제어 서버가 "Location" 헤더를 보내고 있음에도 불구하고 백엔드 요청 시도(502)에서 얻은 상태 코드로 어리석게도 상태 코드를 덮어쓰고 있습니다. error_page 줄을 로 변경하여 마침내 "작동"하게 되었고 error_page 502 =307 @handle_502;, 모든 제어 서버 응답이 307 코드와 함께 클라이언트로 다시 전송되도록 했습니다. 이는 매우 해킹적이고 바람직하지 않습니다. 1) 제어 서버의 응답에 따라 nginx가 다음에 수행해야 하는 작업을 제어할 수 없으며(이상적으로는 제어 서버가 성공을 보고하는 경우에만 백엔드를 다시 시도하고 싶음) 2) 모든 HTTP가 아니기 때문입니다. 클라이언트는 HTTP 리디렉션을 지원합니다(예: 컬 사용자 및 libcurl을 사용하는 응용 프로그램은 다음 리디렉션을 명시적으로 활성화해야 합니다).

nginx가 업스트림 서버 A, B, 다시 A로 프록시를 시도하도록 하는 적절한 방법은 무엇입니까(이상적으로는 B가 특정 상태 코드를 반환하는 경우에만)?

답변1

키 포인트:

  • 하나의 서버를 핑하면 다른 서버가 작동하는 경우 장애 조치를 위한 블록을 신경쓰지 마세요 upstream. 첫 번째 서버가 다시 작동 중임을 nginx(적어도 FOSS 버전은 아님)에 알릴 방법이 없습니다. nginx는 첫 번째 요청에서 순서대로 서버를 시도하지만 , 또는 backup설정 에도 불구하고 후속 요청을 시도하지 않습니다 .weightfail_timeout
  • ~ 해야 하다recursive_error_pages및 명명된 위치를 사용하여 장애 조치를 구현할 때 활성화합니다 error_page.
  • proxy_intercept_errors업스트림 서버에서 전송된 오류 코드를 처리하도록 활성화합니다 .
  • 명명된 위치의 오류 코드를 올바르게 처리하려면 구문(예: )이 필요 합니다 =. 사용되지 error_page 502 = @handle_502;않으면 =nginx는 이전 블록의 오류 코드를 사용합니다.

요약은 다음과 같습니다.

server {
    listen ...;
    server_name $DOMAINS;

    recursive_error_pages on;

    # First, try "Upstream A"
    location / {
        error_page 418 = @backend;
        return 418;
    }

    # Define "Upstream A"
    location @backend {
        proxy_pass http://$IP:81;
        proxy_set_header  X-Real-IP     $remote_addr;
        # Add your proxy_* options here
    }

    # On error, go to "Upstream B"
    error_page 502 @handle_502;

    # Fallback static error page, in case "Upstream B" fails
    root /home/nginx/www;
    location = /_static_error.html {
        internal;
    }

    # Define "Upstream B"
    location @handle_502 { # What to do when the backend server is not up
        proxy_pass ...;
        # Add your proxy_* options here
        proxy_intercept_errors on;          # Look at the error codes returned from "Upstream B"
        error_page 502 /_static_error.html; # Fallback to error page if "Upstream B" is down
        error_page 451 = @backend;          # Try "Upstream A" again
    }
}

원래 답변/연구 로그는 다음과 같습니다.


내가 찾은 더 나은 해결 방법은 다음과 같습니다 . 이는 클라이언트 리디렉션이 필요하지 않기 때문에 개선된 것입니다.

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!

답변2

다음과 같은 것을 시도해 볼 수 있습니다.

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

}

관련 정보