504 Gateway Time-out 문제의 원인과 해결 방법

HYEONG HWAN, MUN/ 9월 16, 2019/ 미분류/ 14 comments

https://blog.lael.be/post/9251

검색해봤는데, 마음에 드는 설명글이 없어서, 제가 하나 작성해봅니다.

504 Gateway Time-out 오류는 리버스 프록시 프로그램에서 < == > 해당 프록시(upstream)와의 통신이 오래걸렸고, 리버스 프록시 프로그램에서 지정한 시간 제한을 초과해서 발생한 오류입니다.
리버스 프록시 프로그램은 거의 99% nginx 를 사용하므로, 이 글에서는 nginx 기준으로 설명하겠습니다.

504 Gateway Time-out 오류는 아래와 같은 형태로 나타납니다.

< 그림 : nginx 에서 504 오류 화면 >

504 Gateway Time-out
504 Gateway Timeout
HTTP 504
504 ERROR
Gateway Timeout (504)
HTTP Error 504 - Gateway Timeout
Gateway Timeout Error
504 Gateway Time-out The server didn't respond in time

 

게이트웨이란? Gateway

게이트웨이는 (통신 분야에서) 서로 다른 네트워크 연결을 위한 출입구를 의미합니다.
단어 자체는 관문, 출입구라고 번역됩니다.
한 지역과 다른 지역을 연결하는 톨게이트나, 빌딩 건물과 외부를 연결하는 중앙현관문을 비유로 들 수 있습니다.

 

일반적인 nginx 네트워크 구성은 아래와 같습니다.

< 화백 라엘님의 Gateway 설명. 굵은점이 Gateway 입니다. >

nginx 는 reverse proxy server 이며, 뒷부분 처리하는 곳은 upstream 이라고 하며, 그 연결 부분은 (위 그림에서 동그라미 부분) Gateway 가 됩니다.
upstream 은 대부분 php, nodejs, python, tomcat 중 하나를 사용합니다.

NGINX 와 PHP-FPM 의 통신을 위한 저 굵은점이 (NGINX 입장에서) Gateway 가 됩니다.

 

게이트웨이 타임아웃이란? Gateway Timeout?

NGINX 는 외부프로그램과 통신을 하여 결과를 출력하는 프로그램입니다. 이때 외부프로그램과 통신이 오래 지연된다면 시간 초과(timeout)가 일어나게 됩니다.

 

nginx 와 upstream 이 통신을 할 때, 어느 부분이 nginx 에서 설정한 시간 제한보다 오래걸리면 504 timeout 이 발생하게 됩니다. (timeout : 연결을 끊어버리고 오류 메세지 표시)
gateway timeout 의 조건은 다음과 같습니다.

 

Gateway Timeout 의 종류

connect_timeout (default : 60s) : upstream 연결이 지연되는 경우 발생한다. 대부분의 upstream 은 내부망이나 가까운 곳에 위치해 있기 때문에, connect timeout 이 발생할 확률은 거의 없다. (upstream 연결이 proxy 방식이고, 이 proxy 연결이 방화벽으로 막혀있을때 일어날 수도 있다.)
TCP/IP 에서 연결은 3 way handshake 방식으로 동작하는데, SYN 을 보낸 후 SYN+ACK 을 시간 내에 받지 못하면 connect_timeout 이 발생한다. 더 알아보기 : TCP-3-WayHandshake-4-WayHandshake

send_timeout (default : 60s) : 프록시 연결 후 데이터 전송이 지연되는 경우 발생한다. 사용자가 파일을 업로드 하는 중이고, 업로드 속도가 매우 느려서 send_timeout 을 초과하면 발생한다.
일반적으로 대용량 파일은 관리자나 지정된 사용자만 업로드하며, 이들은 좋은/안정적인 인터넷 환경을 사용할 확률이 크기 때문에 이 문제는 거의 발생하지 않는다. (일반적인 인터넷 전송 속도와, upload_max_filesize 값을 비교해서, 60초 이상을 초과할 확률이 있는지 확인해 보도록 하자.)

read_timeout (default : 60s) : 거의 모든 504 Gateway Time-out 의 원인은 이것이다. upstream 에 정상적으로 연결했고, 응답 요청도 정상적으로 보냈으나, 응답이 지연되는 경우이다.
백엔드 서버가 느리거나, 복잡한 작업을 하거나, 기타 이유로 지연되어 발생할 수 있다.

예를 들어 아래와 같은 코드를 작성하면, PHP-FPM로 부터 응답이 70초 후에 오게 되며, read_timeout 기본값 60초를 초과하기 때문에, NGINX는 default read_timeout 60초가 되는 순간 연결을 끊고 504 에러를 표시하게 된다.


// make delay
sleep(70);

echo "HELLO";

 

NGINX 에서 read_timeout 의 경우, 요청(send)는 전송된 상태이기 때문에, upstream 과 연결을 강제로 끊더라도 upstream 프로세스의 작업은 계속 동작한다. (PHP 의 경우 max_execution_time 시간까지 실행된다. FPM 에서 request_terminate_timeout 을 설정한 경우 이 값의 영향도 받음.)

 

Upstream 과 연결 방법

FastCGI 연결 : 프로그램 자체와 연결. 프로그램에서는 FastCGI 라는 프로토콜을 지원하며, 이것으로 NGINX 와 통신 가능. (fastcgi:// 또는 unix:/blabla/sample.sock 형태로 나타남)

Proxy 연결 : 프로그램에서 HTTP 프로토콜을 지원하고 이것으로 NGINX 와 통신하는 경우. (http://some-ip-addr:9000 형태로 나타남)


NGINX 에서 timeout 을 다루는 방법 (504 Gateway Timeout 해결방법)

먼저 자신의 서버가 upstream 과 어떤 형태로 통신하는지 알아야 한다. 이것은 nginx 에서 사이트환경설정 conf 파일을 보면 알 수 있다.

< 환경 설정 코드 예제 : NGINX 공식 위키를 참조했습니다. (링크) >

fastcgi_pass 라고 쓰여 있으면 fastcgi 연결이고, proxy_pass 라고 쓰여 있으면 proxy 연결이다.

 

아래와 같이 변경하면 504 에러가 해결된다.
둘 다 하는게 아니라 둘 중 하나, proxy 이면 proxy 설정을, fastcgi 이면 fastcgi 설정을 하면 된다.
connect_timeout 과 send_timeout 은 기본값인 60초를 넘길 일이 거의 없고, upstream 에게 정상적인 응답 요청(request)을 보내기 전이므로 시스템적으로 큰 문제가 발생하지 않는다. (따라서 무시하는 편임.)
되도록 read_timeout 만 설정하도록 하자.


# proxy example
location / {
    proxy_pass      http://127.0.0.1:8080;

    #proxy_connect_timeout   90; # default 60
    #proxy_send_timeout      90; # default 60
    proxy_read_timeout      300;
    proxy_buffers           64 16k;  # default 8 4k
}

# fastcgi example
location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

    #fastcgi_connect_timeout 90; # default 60
    #fastcgi_send_timeout 90; # default 60
    fastcgi_read_timeout 300;

    fastcgi_pass unix:/run/php/myuser1.sock;
    fastcgi_index index.php;
    fastcgi_buffers 64 16k; # default 8 4k

    include fastcgi_params;
}


 

문제가 해결됨!

끝.

 

참고로 프로덕션 환경에서는 짧게 해두는게 좋습니다.
cjmall 쇼핑중에 사이트가 다운되서 테스트 몇개 해봤는데 여기는 timeout 이 5초로 되어있네요.

 


참고로 서비스 앞단에 AWS ELBCloudFlare 를 둔 경우, 이것들의 timeout 도 고려해 주어야 합니다.

CloudFlare 는 timeout 값을 변경할 수 없습니다.
CloudFlare 의 경우 connection_timeout15 second 이며, 504 대신 Error 522: connection timed out 를 표시합니다.
CloudFlare 의 경우 read_timeout100 second(Enterprise Plan 은 최대 600 second) 이며, 504 대신 Error 524: a timeout occurred 를 표시합니다.

AWS ELB 의 경우 send_timeout 과 read_timeout 은 60 second 이며, 이 값은 ELB 설정에서 Idle timeout 항목에서 수정할 수 있습니다.

참조 : https://support.cloudflare.com/hc/en-us/articles/115003011431

참조 : https://docs.aws.amazon.com/ko_kr/elasticloadbalancing/latest/classic/config-idle-timeout.html


알아두면 좋은 참고 사항
timeout 이 되었을 때 실행중이던 작업은 어떻게 처리될까?

이것은 upstream 의 환경설정에 따라 달려있다. 대부분 request 가 오래 걸리더라도 중간에 끊지 않고 다 처리하는 편이다.

외부 호출(인터럽트/system/mysql query)등이 일어나면 카운트가 중지함. 즉 max_execution_time 이 10초로 설정되어 있어도, 1시간 동안 동작하는 mysql query 를 실행할 수 있다.
request_terminate_timeout 값은 기본값이 0(사용안함) 인데, 이 값을 사용해서 절대적인 실행시간 제한을 둘 수 있다. system 의 명령어는 request_terminate_timeout 제한에 걸렷을때 강제 종료가 되지만 mysql query 는 종료되지 않는다.

 

14 Comments

  1. 좋은 글 감사합니다.
    라즈베리파이를 이용해서 로드밸런싱 서버를 구축하고 운영중인데 종종 504에러가 발행하더군요.
    이런 저런 글 확인하면서 오류를 잘 해결했지만 어떤 원인으로 발생하는지는 잘 몰랐었습니다.
    그런데 작성하신 포스트 덕분에 504 Gateway Time-out에 대해서 자세히 알게되었습니다.

    다음에 같은 증상이 일어나면 자세히 살펴봐야겠어요.
    항상 라엘님 포스트 보면서 도움 많이 받고있는데 감사합니다: D

    1. 도움이 되셨다니 다행입니다!
      아 그리고 gravatar.com 에 사진 등록해보세요. 많은 사이트들이 여기 사진을 참조하거든요.

  2. 안녕하세요.
    덕분에 504 Gateway 관련 오류 문제를 해결했습니다.
    정말 다양한 부분들이 서로 얽혀서 하나씩 찾아서 해결했네요!
    저 같은 경우에는
    php.ini (default_socket_timeout),
    nginx.conf ( keepalive_timeout, fastcgi_read_timeout, fastcgi_connect_timeout )
    aws LBS Idle timeout
    총 3가지 값을 수정 해서 해결 했습니다..
    다른 분들도 혹시나 위와 같이 안되시면 추가적으로 더 확인 해보시길 바라는 마음에
    댓글 남겨봅니다.

    1. 도움이 되셨다니 다행입니다.
      제 개인적인 추측인데 fastcgi_read_timeout, ELB Idle timeout 두가지만 수정해도 동작했을 것 같네요.
      감사합니다.

  3. 좋은 글이네요. 블로그에 퍼가도 될까요?

  4. upstream 이 두개인데 #1에 요청하고 read_timeout이 발생하였고,
    nginx에서 read_timeout 후 #2 upstream으로 동일한 request를 날리는 failover를 진행합니다.
    동일 request를 날리지 않도록 막는 설정이 어느 부분인지 혹시 아시나요?

    1. read_timeout 이 발생하여도 job 은 처리중이기 때문에 중복되어 실행될 것입니다.
      질문을 잘 이해하지 못하였지만, 해당 방법을 제가 모르는듯 합니다.

  5. 504 Gateway Time-out 이거 떠서 찾아보다가 봤는데 뭐라그러는지 모르겠다…

  6. 안녕하세요. 좋은글 감사합니다. 덕분에 문제를 해결할 수 있었습니다. 🙂

  7. 안녕하세요.
    최근들어 서버 공부하면서 라엘님의 글에 많은 도움을 받고있습니다.
    워드프레스 플러그인 contact form7 사용시 메일 보내는 시간이 꽤 오래걸리더군요
    fast-cgi 상황이었고 504에러가 떴었는데 (그래도 메일은 발송되더군요 ^^)
    read-timeout을 300으로 충분히 주니 해결되었습니다.
    항상 감사드립니다.

    1. 도움이 되었다니 다행입니다.
      해당 경우는 nginx 는 timeout 처리되어 504 에러를 반환하는 것이고, 요청을 처리하는 php fastcgi 는 계속 실행중인 것입니다.
      서버의 네임서버를 바꿔보세요. 메일이 더 빨리 발송될 수도 있습니다.

      1. 호스팅 받을때는 메일전송이 빨랐는데 홈서버로 만들어보니 늦더군요. 말씀대로 네임서버 문제인듯 합니다. 하지만 cloudflare에 ddns 서비스로 네임서버로 등록해놓은 상황이어서 어쩔수 없을것 같습니다.^^
        감사합니다. 참고하겠습니다.

나무의서에 답글 남기기 응답 취소

작성하신 댓글은 관리자의 수동 승인 후 게시됩니다.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
*
*