Published on

504 Gateway Time-out 에러 해결을 통한 개인적인 정리

Authors
  • avatar
    Name
    김민석
    Twitter

Introduction

상황

배포한 파일 업로드 기능에서 오류가 발생했다.

파일을 업로드하면 서버에서 예상한 응답을 받지 못하고 504 Gateway Timeout 오류가 나타났다 .

이 문제는 Spring Boot 애플리케이션과 Nginx 리버스 프록시를 이용한 배포 환경에서 발생했고,

특히 요청 처리에 시간이 오래 걸리는 상황에서 타임아웃 문제가 빈번히 나타난 것이다 .

문제

image.png

이 문제는 사용자가 파일을 업로드하는 중에 발생했기 때문에 큰 영향을 미칠 수 있었다.

504 Gateway Timeout 오류는 서버가 요청을 처리하는 데 너무 오래 걸릴 때 발생하는 오류로,

클라이언트가 오랜 시간 동안 기다리다가 서버와의 연결이 끊어진다는 것을 의미한다.

따라서 이 문제를 해결하지 않으면 애플리케이션이 제대로 동작을 하지 않는다는 문제점이 생긴다.

원인

배포 후 파일을 업로드하자마자 에러 메시지를 확인했다.

또한, 개발자 모드에서 로그를 통해 타임아웃 문제가 발생했음을 확인할 수 있었다.

특히, 504 Gateway Timeout 메시지와 함께 Nginx 로그에는 프로세스가 계속 재시작되는 현상이

나타났고, Spring Boot 로그에서는 요청이 완료되지 못하는 현상을 감지했다.

Nginx와 springboot 로그를 확인하여 이쪽은 문제가 없다는 걸 알 수 있었다.

image.png
  • Nginx 에러 로그.
image.png
  • springboot 로그.

또 다른 에러, HTTP method names must be tokens

image.png

HTTP 요청의 메서드 이름에 유효하지 않은 문자가 포함되어 있을 때 발생하는 에러였다.

로그에 나온 내용처럼 Invalid character found in method name이라는 메시지가 뜨는 이유는

클라이언트에서 잘못된 HTTP 요청이 서버로 전달된 경우일 가능성이 크다.

이런 문제는 일반적으로 아래와 같은 상황에서 발생할 수 있었다.

  1. 클라이언트가 잘못된 형식의 요청을 보낼 때.
  2. 프록시나 로드 밸런서가 잘못된 요청을 전달하는 경우.
  3. 네트워크 연결 문제로 인해 데이터가 손상되었을 때.

나의 해결 방법

  1. 클라이언트 코드에서 올바른 HTTP 메서드를 사용하고 있는지 확인했다.
  • 예를 들어, POST, GET과 같은 HTTP 메서드 이름이 정확히 지정되었는지 체크하였다.
location / {
    # GET, POST, PUT 등 정상적인 HTTP 메소드가 아닌 경우 차단
    if ($request_method !~ ^(GET|POST|HEAD|PUT|DELETE|OPTIONS)$) {
        return 444;
    }

    // ... 생략

  1. 서버의 보안 그룹에서의 인바운드 규칙 확인했다.
  • 이 문제를 해결하기 위해, 서버의 보안 그룹에서 8080 포트에 대한 인바운드 규칙을 제거하여 외부에서 직접 8080 포트로 접근할 수 없도록 설정했다.
  • 이를 통해, 8080 포트를 통한 불필요한 외부 접근을 차단하고, 보안성을 높이기 위한 조치를 취했다.
image.png

해결

컨트롤러에서 Thread.sleep(35초) 테스트

문제를 재현하고자 컨트롤러에 테스트 API를 추가하여 Thread.sleep(35 * 1000);으로 지연을 주고 로컬과 Nginx 환경에서 각각 테스트를 진행했다.

  • 이를 통해 지연이 발생할 때 타임아웃이 발생하는 것을 확인할 수 있었다.
@RestController
public class TimeoutTestController {

    @GetMapping("/test-timeout")
    public ResponseEntity<String> testTimeout() {
        try {
            // 35초간 대기
            Thread.sleep(35000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ResponseEntity.ok("Response after 35 seconds");
    }
}

Nginx 및 Spring Boot 타임아웃 설정

proxy_read_timeout 설정을 통해 타임아웃 문제를 해결 할 수 있었다.

504 Gateway Timeout 오류를 해결하려면 주로 proxy_read_timeout 시간을 늘려서 Nginx가 백엔드 서버로부터의 응답을 더 오래 기다리도록 설정할 수 있다.

http {
    server {
        ...
        location / {
            proxy_pass http://backend-server;
            proxy_connect_timeout 60s; # 서버 연결 대기 시간
            proxy_read_timeout 120s;   # 응답 대기 시간, 120초로 설정
            proxy_send_timeout 60s;    # 요청 전송 시간
        }
    }
}

proxy_connect_timeout이나 proxy_send_timeout 값도 필요에 따라 조정할 수 있지만, 응답 지연 문제 해결에는 proxy_read_timeout이 가장 중요한 설정이다.

각각에 대한 추가 설명

  • proxy_connect_timeout
  • proxy_read_timeout
  • proxy_send_timeout

proxy_connect_timeout

  • Nginx가 서버에 처음 연결을 시도할 때까지 기다리는 최대 시간이다.
  • 작동 방식: Nginx는 클라이언트 요청을 처리할 서버(예: Spring Boot 애플리케이션)로 연결을 시도한다.
    • 이 과정에서 Nginx는 proxy_connect_timeout에 설정된 시간 동안 연결이 수립될 때까지 기다린다.
  • 용도: 서버의 응답이 느리거나 서버 연결이 불안정할 때 사용된다. 이 값이 지나치게 짧으면 연결이 수립되기 전에 요청이 타임아웃될 수 있다.
  • ex): proxy_connect_timeout 60s;로 설정되었다면, Nginx는 60초 동안 서버로의 연결을 시도한다. 60초 이내에 연결이 수립되지 않으면 Nginx는 연결을 포기하고 오류를 반환하게 되는 것이다.

proxy_read_timeout

  • Nginx가 서버의 첫 응답을 기다릴 최대 시간을 설정해, 응답 지연 시 오류를 방지하는 설정이다.
  • 작동 방식: Nginx는 서버로 요청을 전송한 후, 응답의 첫 번째가 도착하기까지 proxy_read_timeout에 설정된 시간만큼 기다린다.
  • 용도: 서버에서 작업 처리에 시간이 오래 걸리는 경우에 필요하다. 예를 들어, 서버에서 긴 시간 동안 데이터를 처리할때 시간이 길어질 수 있다.
    • 설정된 시간 내에 응답이 도착하지 않으면 Nginx는 타임아웃으로 요청을 종료하고 504 Gateway Timeout 오류를 반환한다.
  • ex): proxy_read_timeout 60s;로 설정되어 있으면, Nginx는 서버가 60초 이내에 첫 번째 응답을 보내기를 기다린다. 그렇지 않으면 요청이 타임아웃 되는 것이다.

proxy_send_timeout

  • Nginx가 서버로 데이터를 전송할 때, 데이터 전송이 완료될 때까지 기다리는 시간이다.
  • 작동 방식: 클라이언트가 Nginx를 통해 서버로 큰 양의 데이터를 전송할 때, 전송이 완료되기까지 proxy_send_timeout에 설정된 시간만큼 대기한다.
  • 용도: Nginx가 클라이언트로부터 큰 파일을 받아 서버로 보내야 하는 경우 이 설정이 유용하다. 지정된 시간 안에 데이터가 전송되지 않으면 타임아웃이 발생하고, Nginx는 연결을 끊는다.
  • ex): proxy_send_timeout 60s;로 설정되어 있다면, Nginx는 60초 동안 데이터를 서버로 전송을 시도한다. 전송이 완료되지 않으면 요청이 중단된다.

로컬 환경에서 테스트하기 (성공)

  • 144초 동안 대기한 후에 응답이 오는지 확인했다.
image.png

로컬에서 144초 후에 응답이 정상적으로 도착하면서,

로컬 서버는 이 시간 동안 문제 없이 응답을 제공할 수 있다는 것을 의미했다.

그렇게 하면 왜 해결되는지

  • 이 문제는 로컬 환경에서는 해결되었지만, 서버 환경에서는 해결되지 않았다. 로컬에서는 예상한 대로 동작했으나, 서버에서는 여전히 동일한 에러가 발생하여 추가적인 조치가 필요했다.
  • 로컬에서는 정상적으로 동작했지만, 서버 환경에서 동일한 문제를 해결하기 위해 서버와 클라이언트 간의 응답 대기 시간인 proxy_read_timeout 설정을 조정했다.
  • 이를 통해 Nginx가 서버로부터 충분한 시간이 지나더라도 응답을 기다리게 함으로써 서버로부터 응답이 올 때까지 요청이 끊기지 않도록 설정 했다.
  • 최종적으로, 서버 환경에서도 문제가 해결되어 정상적인 파일 업로드와 요청 처리가 가능해졌다는 것이다.

결말

배포 된 서버 환경 (성공)

image.png

얼마나 개선되었는지

  • proxy_read_timeout 설정을 조정한 후에는 타임아웃 오류가 더 이상 발생하지 않았으며, 업로드된 파일이 정상적으로 처리되었다.
  • 이를 통해 사용자가 파일을 업로드하는 과정에서 발생하던 오류가 크게 줄어들었다는 것이다.

배우는 것은 무엇인지

  • 이번 문제 해결을 통해 서버 타임아웃과 관련된 설정의 중요성을 배우게 되었다.
  • NginxSpring Boot의 타임아웃 설정을 적절히 조정하는 것만으로도 시스템을 개선할 수 있음을 깨달았다.
  • 또한, 배포 환경에서 발생하는 문제를 해결하기 위해 로깅을 활용하는 방법과, 설정을 하나씩 변경하며 테스트하는 방법의 중요성을 다시 한 번 실감했다.

다른 방법은 없었는지

  • 다른 방법으로는 파일 업로드 처리를 백그라운드에서 진행하는 비동기 방식을 생각해볼 수 있었다.
  • 이 방법을 사용하면 서버가 파일 처리를 뒤에서 진행하고, 클라이언트는 바로 응답을 받아 타임아웃 문제가 줄어들 수 있게 할 수 있지 않을까 생각해봤다.
  • 하지만, 이 방법은 코드 구조를 많이 변경해야 해서, 이번에는 간단한 타임아웃 시간 조정 방법을 선택했다.