nginx 리버스 프록시-업스트림 A, B, A를 다시 시도하십시오


22

많은 백엔드 서버와 함께 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_502작동하지 않으며 451 코드는 그대로 클라이언트로 전송됩니다.

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

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

답변:


20

키 포인트:

  • upstream한 서버를 핑 (ping)하면 다른 서버가 작동 할 경우 장애 조치 (failover)를위한 블록을 신경 쓰지 마십시오 . 첫 번째 서버가 다시 작동한다고 nginx (최소한 FOSS 버전이 아님)에게 알릴 방법이 없습니다. nginx는 첫 번째 요청에서 서버를 순서대로 시도하지만 backup, weight또는 fail_timeout설정 에도 불구하고 후속 요청은하지 않습니다 .
  • 당신은 있어야 가능 recursive_error_pages하여 장애 복구 구현할 때 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;.


편집 : 그리고 나는 그것을 작동시켰다! 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

업스트림 B는 제어 서버입니다. 그 목적은 업스트림 A에 대한 다음 요청이 성공하도록하는 것입니다. 목표는 업스트림 A를 시도하는 것이며, 업스트림 B를 실패하면 "성공"(내부 "성공"이라는 규칙을 사용하여) 된 경우 업스트림 A를 다시 시도하십시오. 내 질문이 명확하지 않은 경우 어떻게 개선 할 수 있는지 알려주십시오.
Vladimir Panteleev

흠, 예를 들어 하드웨어 문제와 같이 업스트림 A가 다운되었다고 가정 해 봅시다. 업스트림 B는 무엇입니까? 클라이언트의 요청에 대한 응답을 반환 할 수 있습니까?
ALex_hha

이 문제는 하드웨어 장애에 대한 장애 조치가 아닙니다. 이 문제는 주문형 업스트림 백엔드 시작에 관한 것입니다. 제어 서버 (업스트림 B)가 백엔드 (업스트림 A)를 다시 활성화 할 수 없다면 이상적으로 사용자에게 적절한 오류 메시지가 표시되지만 해결하려는 문제는 아닙니다. 문제는 nginx가 재 시도하는 것입니다 같은 요청 내에서 다시 B 다음에.
Vladimir Panteleev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.