brunch

You can make anything
by writing

C.S.Lewis

by 강진우 Jan 16. 2020

nginx no live upstream 에러 이해하기

Linux OpenSource

글 발행 후 새롭게 확인된 사실이 있어 업데이트 합니다!!


nginx를 리버스 프록시로 운영하다 보면 다양한 이슈를 만나게 됩니다. 오늘은 그중에서 no live upstream 에러에 대해서 이야기하려고 합니다. no live upstream 에러가 왜 발생하고 어떻게 해결할 수 있는지에 대해 살펴보겠습니다.


reverse proxy 로서의 nginx


먼저 reverse proxy 로서의 nginx에 대해서 살펴보겠습니다. 

reverse proxy 로서의 nginx

nginx worker는 클라이언트의 요청을 백엔드에 있는 upstream 서버로 넘겨줍니다. 아래는 흔하게 사용되는 nginx upstream 환경 설정 중 하나입니다.

upstream backend.domain.com {
    server backend.domain.com:80;
    keepalive 1024;
    keepalive_requests 10240;
    keepalive_timeout 120;
  }

  server {
    listen                  80;
    server_name             www.domain.com;
    location / {
      proxy_pass http://backend.domain.com;
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header   X-Real-IP $remote_addr;
      proxy_set_header   X-Forwarded-For $http_x_forwarded_for;
      proxy_set_header Connection "";
    }

www.domain.com의 호스트 헤더를 가지고 들어온 클라이언트의 요청을 nginx worker는 backend.domain.com 서버로 넘겨줍니다. 그리고 backend.domain.com의 응답을 다시 클라이언트에게 돌려줍니다. 여기까지는 아주 기본적인 리버스 프록시의 역할입니다. 하지만 위 설정으로 서비스를 하게 되면 한 가지 이슈를 만날 수 있습니다. 그리고 그 이슈가 바로 오늘의 주제인 no live upstream 에러입니다.


upstream 서버와 문제가 발생할 경우


만약 upstream 서버와의 통신 과정에서 문제가 발생할 경우 어떻게 될까요? nginx worker는 기본적으로 몇 번의 에러가 발생했는지를 세고, 그 횟수가 넘어갈 경우 해당 upstream 서버를 upstream 서버 목록에서 제거합니다. 

upstream 서버에 문제가 생겼을 경우

평상시에는 upstream에 지정한 서버들에게 균등하게 요청을 보내다가 한 서버와의 통신에서 이상을 감지하면 해당 서버를 upstream 서버 목록에서 제거하고 남은 서버에게로 요청을 보내게 됩니다. 

그럼 잠시 위 예제 설정으로 돌아가 보겠습니다. 위 예제 설정에는 서비스의 안정성에 문제를 줄 수 있는 위험성이 숨어 있습니다. 

바로 upstream 지시자 안에 있는 server 지시자가 한 개 밖에 없다는 것입니다. 

backend.domain.com 서버와의 통신에 문제가 생기면 더 이상 upstream 역할을 할 수 있는 서버가 없어지게 되고 그로 인해 no live upstream 에러가 발생하게 되는 것입니다. 그래서 upstream 지시자 안에 여러 개의 server 지시자를 넣는 것을 권고하지만 여의치 않을 경우도 있습니다. server 지시자를 한 개 밖에 넣지 못할 때는 어떤 방법으로 이 문제를 회피할 수 있을까요? 바로 max_failfail_timeout 옵션을 통해서 해결할 수 있습니다.


---- 덧 붙이는 내용

nginx의 upstream 관련된 공식 문서 (http://nginx.org/en/docs/http/ngx_http_upstream_module.html) 에 아래와 같은 구절이 있습니다.

If there is only a single server in a group,  max_fails,  fail_timeout and  slow_start parameters are ignored, and such a server will never be considered unavailable.

혹시 제 글에 오류가 있진 않을까 걱정해 주신 강대명님께서 알려 주셨습니다. 너무 감사 드립니다!

하지만 공식 문서의 내용과는 다르게 저희는 server 지시자에 도메인을 한개만 사용한 상태에서 간헐적인 no live upstream 에러를 경험 했습니다. 그리고 max_fails와 fail_timeout 설정을 한 이후로는 no live upstream 에러는 발생하지 않았습니다. 그래서 조금 더 확인을 해보게 되었고, 공식 문서에서 언급하고 있는 single server in a group 이라는 것은 server 지시자가 한 개인 것을 의미하지 않고 server 지시자가 한 개 이면서 해당 지시자에 명시된 도메인의 IP 질의 결과가 한 개 일 경우 인 것으로 확인 되었습니다. 

즉, server 지시자는 한 개이지만 지시자에 명시된 도메인의 IP 질의 결과가 여러개일 경우에는 single server in a group에 해당하지 않습니다. 

저희는 server 지시자의 도메인이 AWS ALB를 가리켰기 때문에 항상 N개의 IP 질의 결과가 나왔습니다. 

이에 대해서도 여력이 생기면 다른 글을 통해 upstream 소스 코드에 대한 설명을 해보려고 합니다. 

혹시라도 제가 또 잘못 알고 있는 내용이 있으면 편하게 말씀해 주세요~


max_fail과 fail_timeout 옵션


max_failfail_timeout의 기본값은 각각 110입니다. 즉 한 번 실패하게 되면 해당 upstream 서버는 10초 동안 사용이 불가능해지는 것입니다. (참고 : http://nginx.org/en/docs/http/ngx_http_upstream_module.html#server)

max_fail과 fail_timeout을 설정하지 않았을 경우

따라서 위 값들을 아래와 같이 수정하는 것이 서비스 안정성을 높이는데 도움이 됩니다.

max_fail : 3회
fail_timeout : 3초

이 값들을 바탕으로 위에서 보인 예제 설정은 아래와 같이 수정될 수 있습니다.

upstream backend.domain.com {
    server backend.domain.com:80 max_fails=3 fail_timeout=3s;
    keepalive 1024;
    keepalive_requests 10240;
    keepalive_timeout 120;
  }

이 설정을 통해 upstream 서버를 한 대만 사용하더라도 3회까지는 재시도를 하게 되고 upstream 서버를 사용하지 못하게 되더라도 사용하지 못하는 시간이 3초로 짧아집니다. 


마치며


TCP 통신은 다양한 이유로 실패할 수 있습니다. HTTP 역시 TCP에서 동작하는 통신이기 때문에 upstream으로 지정한 서버와의 통신 실패는 다양한 이유로 발생할 수 있습니다. 

이럴 경우 통신 실패 원인을 분석하고 해결하는 것이 가장 중요하겠으나, 때로는 적합한 설정을 통해서 간헐적인 통신 실패가 서비스 장애로 이어지지 않도록 하는 것도 중요합니다. 

이 글이 같은 이슈를 겪고 있는 다른 분들에게 도움이 되었으면 좋겠습니다. 읽어주셔서 감사합니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari