이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Nginx timeout, buffering, body size 설정은 각각 어떤 장애를 만들거나 줄이는가?
- 413, 499, 504를 설정 관점에서 어떻게 구분하는가?
- 업로드, streaming, 일반 API에 같은 proxy 설정을 쓰면 왜 위험한가?
개요
Nginx의 timeout, buffering, body size 설정은 요청이 어느 크기와 시간까지 허용되는지 정하는 운영 계약이다.
값이 너무 작으면 정상 요청도 413이나 504로 실패한다.
값이 너무 크면 느린 upstream을 오래 붙잡아 Spring thread와 client connection을 소모한다.
이 설정은 Nginx만의 문제가 아니라 Spring timeout, multipart limit, DB 대기 시간, client timeout과 함께 맞춰야 한다.
주요 설정
location /api/ {
proxy_pass http://127.0.0.1:8080;
client_max_body_size 10m;
client_body_timeout 10s;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_request_buffering on;
proxy_buffering on;
}client_max_body_size는 client가 보낼 수 있는 request body 최대 크기다.
이보다 큰 업로드는 Nginx에서 413으로 거절될 수 있다.
proxy_connect_timeout은 Nginx가 upstream에 connection을 맺는 시간 제한이다.
proxy_read_timeout은 upstream 응답을 기다리는 시간 제한이다.
proxy_send_timeout은 Nginx가 upstream으로 request를 보내는 시간 제한이다.
413 Body Size
413은 request body가 Nginx 허용 크기를 넘었을 때 나타난다.
파일 업로드 API에서 자주 발생한다.
Spring의 multipart limit이 20MB인데 Nginx가 10MB로 막고 있으면 요청은 Spring까지 도달하지 않는다.
반대로 Nginx는 100MB를 허용하지만 Spring이 10MB로 막으면 Spring에서 실패한다.
따라서 Nginx, Spring, frontend validation, CDN 또는 load balancer limit을 같은 표로 맞춰야 한다.
504 Timeout
504는 Nginx가 upstream 응답을 제한 시간 안에 받지 못했을 때 나타난다.
proxy_read_timeout을 늘리면 504는 줄어들 수 있다.
하지만 Spring이 DB lock, 외부 API 대기, thread starvation에 빠져 있다면 timeout 증가가 장애를 숨긴다.
느린 API는 먼저 처리 시간을 측정하고, 필요하다면 pagination, async 처리, job 분리, query 개선을 검토한다.
499 Client Closed Request
499는 Nginx 고유 신호로, client가 응답 전에 연결을 닫았다는 뜻이다.
client timeout이 서버 처리 시간보다 짧거나 사용자가 페이지를 떠났을 수 있다.
499가 늘면 frontend timeout, mobile network, API latency, response size를 함께 본다.
서버가 결국 성공 응답을 만들었더라도 client는 이미 떠났을 수 있다.
Buffering
proxy_request_buffering은 Nginx가 request body를 먼저 받아 둔 뒤 upstream으로 보낼지 결정한다.
일반 upload에는 도움이 될 수 있지만, 큰 body는 disk temporary file을 많이 쓸 수 있다.
proxy_buffering은 upstream response를 Nginx가 buffer할지 결정한다.
일반 API에는 효율적일 수 있지만, SSE나 streaming response에서는 지연을 만들 수 있다.
Streaming endpoint는 별도 location으로 분리해 buffering 정책을 다르게 둔다.
Endpoint별 분리
모든 API에 같은 timeout과 body size를 적용하면 운영 판단이 흐려진다.
일반 JSON API, 파일 업로드, 다운로드, streaming, long polling은 기대 시간이 다르다.
location /api/ {
proxy_read_timeout 30s;
client_max_body_size 10m;
}
location /api/uploads/ {
proxy_read_timeout 120s;
client_max_body_size 100m;
}긴 timeout은 필요한 endpoint에만 둔다.
전체 server block에 크게 잡으면 느린 요청이 장애를 오래 끌고 간다.
운영 체크리스트
- Nginx body size와 Spring multipart limit이 일치하는가?
- CDN 또는 load balancer의 body size limit도 확인했는가?
- 일반 API와 upload, streaming endpoint 설정이 분리되어 있는가?
- timeout을 늘리기 전에 upstream 처리 지연 원인을 확인하는가?
- access log에 request time과 upstream response time이 남는가?
- buffering이 streaming 응답을 지연시키지 않는가?
- temporary file이 disk를 압박하지 않는가?
장애 신호
- upload API에서 413이 발생하고 Spring log에는 요청이 없다.
- 504가 늘고 Nginx error log에
upstream timed out이 보인다. - 499가 늘고 frontend timeout 설정과 API p95가 비슷하다.
- SSE나 streaming endpoint가 한참 뒤에 몰아서 응답한다.
/var/lib/nginx또는 temporary path disk 사용량이 급증한다.- timeout을 크게 늘린 뒤 active connection이 오래 유지된다.
안전한 완화 조치
413은 먼저 실패가 Nginx에서 난 것인지 Spring에서 난 것인지 구분한다.
504는 proxy_read_timeout을 늘리기 전에 Spring 처리 시간과 DB 상태를 본다.
Streaming 문제는 해당 endpoint만 buffering을 조정한다.
큰 업로드는 body size뿐 아니라 disk temporary path와 virus scan, storage write 시간도 고려한다.
설정 변경 후에는 error log와 access log의 status 분포를 비교한다.
인프라 담당자와 공유할 자료
증상: 50MB 파일 업로드 413
Nginx client_max_body_size: 10m
Spring multipart max-file-size: 100MB
Spring log: 요청 없음
판단: Nginx에서 body size 차단
완화: /api/uploads/ location만 100m로 조정body size와 timeout 문제는 endpoint, 파일 크기, 처리 시간, 어느 계층에서 실패했는지를 같이 적어야 한다.
실전 팁
- timeout은 전체 서비스 기본값보다 endpoint 성격에 맞춘다.
- 업로드 limit은 Nginx와 Spring, 앞단 gateway를 한 번에 확인한다.
- 504를 없애려고 timeout만 늘리면 장애 시간이 길어질 수 있다.
- streaming endpoint는 buffering 정책을 별도로 검토한다.
- access log에 request time과 upstream response time이 없으면 분석이 느려진다.
위험 신호!
- 413을 Spring multipart 설정만 보고 해결하려 한다.
- 모든 API에 긴
proxy_read_timeout을 적용한다. - buffering 때문에 streaming이 깨졌는데 애플리케이션 코드만 의심한다.
- 큰 업로드 허용 후 disk temporary path를 감시하지 않는다.
- 499를 모두 client 문제로만 본다.
확인 질문
확인 질문
- 413이 Spring log 없이 발생하면 무엇을 의심해야 하는가?
- Nginx나 앞단 gateway의 body size 제한에서 request가 차단되었을 가능성을 먼저 봐야 한다.
- 504가 났을 때 timeout만 늘리면 위험한 이유는 무엇인가?
- upstream 지연 원인을 숨기고 Spring thread와 client connection을 더 오래 점유하게 만들 수 있기 때문이다.
- streaming endpoint에서 buffering을 따로 봐야 하는 이유는 무엇인가?
- Nginx가 response를 모아서 보내면 client가 실시간으로 받아야 할 이벤트가 지연될 수 있기 때문이다.