테마 전환

Nginx 리버스 프록시 완벽 가이드: upstream, 버퍼링, 타임아웃

Easton editorial illustration: one large reverse-proxy gate distributing traffic to three upstream servers

휴대전화가 쉴 새 없이 진동했습니다. 프로덕션 환경에서 경보가 발생한 것입니다.

로그를 열어 보니 온통 502 Bad Gateway였습니다. 백엔드 서비스는 중단되지 않았지만 Nginx 설정의 타임아웃이 너무 짧았습니다. 트래픽이 몰리자 요청 처리가 끝나기도 전에 연결이 강제로 끊겼습니다. 문제는 애초에 별생각 없이 넣었던 proxy_read_timeout 60s 한 줄이었습니다.

그 사고 이후 일주일 동안 Nginx 리버스 프록시의 핵심 모듈 세 가지를 철저히 파고들었습니다. upstream 로드 밸런싱, proxy buffer 버퍼링, timeout 설정입니다. 이 세 모듈을 제대로 설정하면 리버스 프록시가 열 배의 트래픽도 견딜 수 있지만, 잘못 설정하면 그날의 경보처럼 처참한 결과를 맞게 됩니다.

이 글에는 직접 겪은 문제와 디버깅 경험, 이해하게 된 원리를 모두 정리했습니다. 백엔드나 운영 업무를 맡고 있거나 Nginx 설정 매개변수 뒤에 숨은 원리를 이해하고 싶다면 많은 시간을 아낄 수 있을 것입니다.


Upstream 로드 밸런싱: 단순한 ‘요청 분배’가 아닙니다

기본 문법부터 살펴보기

upstream 설정 블록은 Nginx 로드 밸런싱의 핵심입니다. 다음과 같은 기본 형태는 한 번쯤 보셨을 것입니다.

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

보기에는 간단합니다. 백엔드 서버 그룹을 정의하고 proxy_pass로 가리키면 끝입니다. 하지만 솔직히 이 정도만 알아서는 부족합니다. 실제 프로덕션 환경에서는 서버가 중단되면 어떻게 할지, 사양이 좋은 서버에 트래픽을 더 보낼지, 장기 연결을 유지할지까지 고려해야 합니다.

네 가지 로드 밸런싱 알고리즘과 각각의 용도

Nginx는 기본적으로 라운드 로빈(round-robin)을 사용해 요청을 순서대로 분배합니다. 공평하지만 영리하지는 않습니다.

백엔드가 WebSocket이나 데이터베이스 연결 풀처럼 장기 연결을 사용한다면 라운드 로빈 때문에 특정 서버의 연결 수가 갑자기 치솟을 수 있습니다. 이때는 최소 연결(least_conn) 알고리즘이 더 적합합니다.

upstream backend {
    least_conn;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

각 서버의 활성 연결 수를 추적해 새 요청을 현재 가장 한가한 서버로 보냅니다. 예전에 WebSocket으로 실시간 메시지를 전송하는 프로젝트에서 라운드 로빈을 사용했다가 특정 서버의 메모리가 바닥난 적이 있습니다. least_conn으로 바꾸자 부하가 훨씬 고르게 분산됐습니다.

사용자가 로그인한 뒤의 요청을 모두 같은 서버로 보내야 하는 상황도 있습니다. 세션을 로컬에 저장하기 때문입니다. IP Hash는 바로 이런 용도입니다.

upstream backend {
    ip_hash;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

같은 클라이언트 IP에서 온 요청은 해시를 통해 고정된 백엔드로 전달됩니다. 다만 이 방식에는 결함이 있습니다. 해당 서버가 중단되면 세션도 사라집니다. 실제로 안정적인 방법은 Redis에 세션을 저장하고 ip_hash는 임시 방안으로만 사용하는 것입니다.

네 번째는 분산 캐시 환경에서 자주 쓰는 일관된 해시(hash)입니다.

upstream backend {
    hash $request_uri consistent;
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

Nginx는 weight unit마다 가상 노드 160개를 만들고 요청 URI를 기준으로 특정 서버에 해시합니다. 같은 URI가 항상 같은 서버로 전달되므로 캐시 적중률이 높다는 장점이 있습니다.

가중치 설정: 서버 사양이 서로 다르다면

백엔드 서버의 사양이 서로 다른 경우는 흔합니다. 어떤 서버는 메모리 32GB와 8코어 CPU를 갖췄지만, 다른 서버는 메모리 16GB와 4코어뿐일 수 있습니다. 공평하게 라운드 로빈으로 나누면 성능이 좋은 서버의 자원이 낭비됩니다.

upstream backend {
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=2;
    server 192.168.1.12:8080 weight=1;
}

weight=3인 서버는 세 배 많은 요청을 받습니다. 사양이 좋은 서버는 더 많이 처리하고, 사양이 낮은 서버는 덜 처리하는 것이 합리적입니다.

예비 서버를 지정하는 backup 매개변수도 있습니다.

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080 backup;
}

backup 서버는 평소 부하 분산에 참여하지 않고 앞의 두 서버가 모두 중단됐을 때만 활성화됩니다. 주전이 빠져야 투입되는 후보 선수와 비슷합니다.

Keepalive 연결 풀: 성능을 두 배로 높이는 비결

많은 사람이 이 부분을 놓치기 쉽습니다. Nginx의 기본 동작은 요청마다 백엔드와 새로운 TCP 연결을 맺고 응답이 끝나면 연결을 닫는 것입니다. 얼핏 문제없어 보이지만 실제로는 그렇지 않습니다.

TCP 연결을 맺으려면 3-way handshake가 필요하고 닫을 때는 4-way handshake가 필요합니다. 동시 요청이 많은 환경에서는 이 비용이 매우 큽니다. keepalive 연결 풀을 사용하면 연결을 재사용해 이 비용을 없앨 수 있습니다.

설정 예시는 다음과 같습니다.

upstream backend {
    server 192.168.1.10:8080;
    keepalive 32;  # worker마다 유휴 연결 32개 유지
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

두 가지에 주의해야 합니다.

  1. keepalive 32는 worker 프로세스마다 유지할 수 있는 최대 유휴 연결 수를 설정합니다.
  2. 반드시 proxy_http_version 1.1Connection ""를 설정해야 합니다. HTTP/1.0은 지속 연결을 지원하지 않습니다.

예전에 API 서비스를 테스트했을 때 keepalive를 쓰지 않으면 QPS가 약 2000이었지만, 적용한 뒤에는 곧바로 4000을 넘었습니다. 두 배라는 말은 과장이 아니라 실제 결과입니다.

2배
QPS 성능 향상
Source: 실측 데이터: keepalive 연결 풀 활성화 후

다만 keepalive 값을 너무 높게 설정해서는 안 됩니다. 테스트 환경에서 keepalive를 100으로 설정한 적이 있는데, 백엔드에 ECS 컨테이너가 하나뿐이라 연결 수를 감당하지 못했습니다. 프로덕션 환경에서는 대략 다음 공식으로 계산할 수 있습니다.

keepalive ≈ 총 QPS ÷ 요청당 평균 소요 시간 ÷ worker 프로세스 수

예상 QPS가 10000이고 평균 응답 시간이 50ms이며 worker가 4개라고 가정해 보겠습니다.

10000 × 0.05 ÷ 4 = 125

keepalive는 약 125로 설정하는 것이 적절합니다.

헬스 체크: 중단된 서버 자동 제외

Nginx 오픈 소스 버전은 수동 헬스 체크만 지원합니다. 요청 실패를 발견한 뒤에야 서버를 비정상 상태로 표시합니다.

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_next_upstream error timeout http_502 http_503 http_504;
        proxy_next_upstream_tries 3;
    }
}

proxy_next_upstream은 연결 오류, 타임아웃 또는 502/503/504 반환 등 어떤 상황에서 다음 서버로 재시도할지를 정의합니다. proxy_next_upstream_tries 3은 최대 3대까지 시도한다는 뜻입니다.

하지만 수동 검사는 지연이 있습니다. 요청이 실패해야 서버가 중단됐다는 사실을 알 수 있기 때문입니다. 서비스 가용성 요구가 높다면 NGINX Plus의 능동 헬스 체크가 더 좋습니다.

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    location / {
        proxy_pass http://backend;
        health_check interval=5s fails=3 passes=2;
    }
}

5초마다 능동적으로 헬스 체크 요청을 보내고, 3회 연속 실패하면 unhealthy로 표시하며 2회 연속 성공해야 복구됩니다.


Proxy Buffer: 버퍼링은 도우미일까요, 방해꾼일까요?

버퍼링 메커니즘은 무엇을 하나요?

먼저 개념부터 살펴보겠습니다. Nginx는 백엔드에서 응답을 받으면 클라이언트에 곧바로 보내지 않고 먼저 버퍼에 저장합니다.

그 이유는 클라이언트의 네트워크 속도를 예측할 수 없기 때문입니다. 백엔드는 데이터를 빠르게 내보내더라도 클라이언트의 네트워크가 느리면 Nginx가 그 자리에 막힌 채 클라이언트가 천천히 받을 때까지 기다려야 합니다. 버퍼가 있으면 Nginx가 응답을 한 번에 저장한 뒤 클라이언트에 천천히 전송할 수 있습니다. 백엔드는 기다리지 않고 다음 요청을 더 빨리 처리할 수 있습니다.

물론 버퍼링에도 대가가 있습니다. 메모리를 차지합니다. 응답 본문이 크고 동시 요청이 많으면 메모리 사용량도 상당해집니다.

핵심 매개변수 세 가지의 관계 이해하기

proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;

처음에는 이름이 비슷하고 의미도 복잡해 보여 이 세 매개변수를 이해하기 어려웠습니다. 그림으로 정리한 뒤에야 차이가 보였습니다.

  • proxy_buffer_size: 응답 헤더를 저장하는 버퍼이며 요청마다 하나씩 있습니다.
  • proxy_buffers: 응답 본문을 저장하는 버퍼 배열이며 형식은 개수 각 버퍼 크기입니다.
  • proxy_busy_buffers_size: 클라이언트에 전송 중인 버퍼 부분이며 전체 buffers 크기의 절반을 넘을 수 없습니다.

예를 들어 proxy_buffers 8 32k의 전체 크기는 8×32k = 256k입니다. proxy_busy_buffers_size 64k는 정확히 4분의 1이므로 규칙에 맞습니다.

이 매개변수들은 언제 조정해야 할까요?

백엔드 응답 헤더가 특히 크다면(예: Cookie가 아주 많은 경우) “upstream sent too big header” 오류가 발생할 수 있습니다. 해결 방법은 proxy_buffer_size를 늘리는 것입니다.

proxy_buffer_size 16k;

응답 본문이 자주 크다면(예: API가 긴 JSON을 반환하는 경우) buffers를 더 크게 조정할 수 있습니다.

proxy_buffers 16 64k;

특수한 상황: 버퍼링을 꺼야 할 때

어떤 상황에서는 버퍼링이 오히려 방해가 됩니다.

Server-Sent Events(SSE): 백엔드가 이벤트 스트림을 계속 푸시할 때 Nginx가 버퍼에 저장만 하고 전송하지 않으면 클라이언트는 메시지를 늦게 받습니다. 다음과 같이 버퍼링을 꺼야 합니다.

location /events {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 86400s;
}

SSE는 장기 연결이므로 타임아웃 때문에 끊기지 않도록 proxy_read_timeout 86400s(하루)로 설정합니다.

WebSocket: 양방향 실시간 통신이므로 마찬가지입니다.

location /ws {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400s;
}

대용량 파일 업로드: 클라이언트가 1GB 파일을 업로드할 때 Nginx가 파일 전체를 받은 뒤 백엔드에 전달한다면 메모리가 곧바로 고갈됩니다. 이때는 요청 버퍼링을 꺼야 합니다.

location /upload {
    proxy_pass http://backend;
    proxy_request_buffering off;
    client_max_body_size 1G;
}

proxy_request_buffering off를 설정하면 Nginx가 받은 데이터를 곧바로 스트리밍 방식으로 전달합니다.


Timeout: 타임아웃 설정 뒤에 숨은 원리

각자 역할이 다른 세 가지 타임아웃 매개변수

proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;

이름은 비슷해 보이지만 역할은 명확히 나뉩니다.

  • proxy_connect_timeout: Nginx가 TCP 연결이 맺어지기를 기다리는 시간입니다. 네트워크 혼잡이나 방화벽 차단 등으로 백엔드 서버의 응답이 느리고 이 시간을 넘기면 연결을 포기합니다.
  • proxy_read_timeout: 연결이 맺어진 뒤 Nginx가 백엔드 데이터를 기다리는 시간입니다. 두 읽기 작업 사이의 간격이 이 값을 넘으면 타임아웃으로 처리합니다.
  • proxy_send_timeout: Nginx가 요청 본문을 백엔드로 전송할 때의 시간 제한입니다.

여기서 혼동하기 쉬운 부분이 있습니다. proxy_read_timeout은 전체 타임아웃이 아니라 두 읽기 작업 사이의 간격입니다. 백엔드가 응답하기까지 5분이 걸리더라도 처리 중에 하트비트 패킷처럼 데이터를 계속 보낸다면 proxy_read_timeout 60s로 충분합니다. 하지만 백엔드가 5분 동안 완전히 응답하지 않는다면 proxy_read_timeout 300s로 설정해야 합니다.

타임아웃과 502/504의 관계

새벽 3시의 그 경보에서 중요한 교훈 하나를 얻었습니다.

  • 502 Bad Gateway: Nginx가 백엔드에 아예 연결하지 못했습니다. 백엔드가 중단됐거나, 포트가 열려 있지 않거나, 방화벽이 차단했을 수 있습니다.
  • 504 Gateway Timeout: Nginx가 백엔드에 연결했지만 백엔드가 너무 오래 데이터를 반환하지 않았습니다.

예를 들어 proxy_connect_timeout 10s로 설정했는데 백엔드가 연결 요청에 15초 뒤 응답하면 Nginx는 502를 반환합니다. 연결은 빠르게 맺어졌지만 백엔드가 2분 동안 처리한 뒤 데이터를 보내고 proxy_read_timeout 60s로 설정돼 있다면 504를 반환합니다.

상황별 타임아웃 전략

API 서비스: 보통 30~60초면 충분합니다. API 응답은 빨라야 하므로 타임아웃을 짧게 설정하면 느린 요청을 빠르게 발견할 수 있습니다.

proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;

파일 처리: 보고서 내보내기나 PDF 생성 같은 작업은 몇 분이 걸릴 수 있으므로 타임아웃을 늘려야 합니다.

proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;

스트리밍 서비스: 라이브 영상, WebSocket, SSE는 장기 연결이므로 하루로 설정해도 이상하지 않습니다.

proxy_read_timeout 86400s;

502/504 문제 해결 실전

원인 분석

제가 겪었던 사례는 다음과 같습니다.

  1. upstream 서비스가 실제로 중단됨: 프로세스 충돌, 포트 점유, 메모리 부족
  2. 연결 수 고갈: 백엔드 서버의 연결 풀이 가득 차 Nginx가 연결할 수 없음
  3. 타임아웃 설정이 너무 짧음: 새벽 경보 때처럼 proxy_read_timeout은 60s인데 백엔드 처리에는 2분이 걸림
  4. 방화벽/네트워크 문제: 보안 그룹 규칙이 설정되지 않았거나 iptables가 요청을 차단함

로그 진단 방법

첫 단계는 언제나 error_log 확인입니다.

error_log /var/log/nginx/error.log warn;

흔히 볼 수 있는 오류 메시지는 다음과 같습니다.

upstream timed out (110: Connection timed out) while reading response header from upstream

읽기 시간이 초과된 504 오류입니다.

connect() failed (111: Connection refused) while connecting to upstream

연결이 거부된 502 오류입니다. 백엔드가 포트를 수신하고 있지 않습니다.

한 단계 더 나아가려면 로그 형식을 직접 정의해 upstream 상태를 출력할 수 있습니다.

log_format upstream_status '$status $upstream_status $upstream_response_time';

access_log /var/log/nginx/access.log upstream_status;

그러면 200 200, 200, 502 0.5, 1.2, 3.0과 같은 출력이 나타납니다. 각 백엔드가 어떤 상태 코드를 반환했고 얼마나 걸렸는지 명확하게 보여 줍니다.

대표적인 해결 방법

상황 1: 백엔드 처리가 느려 504가 자주 발생함

해결 방법: proxy_read_timeout을 늘리는 동시에 백엔드가 실제로 작업을 끝낼 수 있는지 확인합니다. Nginx만 조정하지 말고 백엔드 타임아웃도 함께 조정해야 합니다.

상황 2: 연결이 거부돼 502가 발생함

해결 방법: 백엔드 프로세스가 실행 중인지, 포트가 수신 중인지, 방화벽 규칙이 허용하는지 확인합니다.

netstat -tlnp | grep 8080
ps aux | grep your_app

상황 3: 동시 요청이 많을 때 연결 수가 고갈됨

해결 방법: 백엔드 연결 풀 제한을 늘리거나 Nginx upstream keepalive를 활성화해 연결 생성 비용을 줄입니다.


성능 최적화 모범 사례

Worker 설정

Nginx는 다중 프로세스 모델을 사용합니다. worker_processes는 프로세스 수를 설정하며 보통 CPU 코어 수와 같게 둡니다.

worker_processes auto;

auto는 CPU 코어 수를 자동 감지합니다. 서버가 8코어라면 worker 프로세스도 8개가 됩니다.

worker_connections는 worker 하나가 처리할 수 있는 최대 연결 수입니다.

events {
    worker_connections 4096;
}

이론상 최대 동시 연결 수는 worker_processes × worker_connections입니다. 8코어 × 4096 = 32768입니다. 다만 실제 값은 시스템 파일 디스크립터 제한의 영향을 받습니다.

TCP 최적화의 세 가지 핵심

sendfile on;
tcp_nopush on;
tcp_nodelay on;

이 세 매개변수를 함께 사용하면 성능을 크게 높일 수 있습니다.

  • sendfile on: 커널 수준 파일 전송을 활성화해 사용자 공간 버퍼를 거치지 않습니다.
  • tcp_nopush on: sendfile과 함께 사용하며 패킷을 하나씩 보내는 대신 데이터를 묶어 전송합니다.
  • tcp_nodelay on: 버퍼가 가득 찰 때까지 기다리지 않고 작은 패킷을 즉시 전송합니다.

정적 파일 서비스를 직접 측정했을 때 이 세 설정을 켜자 처리량이 30% 이상 향상됐습니다.

30%+
처리량 향상
Source: 실측 데이터: sendfile + tcp_nopush + tcp_nodelay 활성화

기타 최적화

gzip 압축: 텍스트 응답을 압축해 전송하면 대역폭을 절약할 수 있습니다.

gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;

파일 디스크립터 제한: 동시 요청이 많을 때 부족할 수 있습니다. 다음 명령으로 시스템 제한을 확인합니다.

ulimit -n

값이 1024에 불과하다면 높여야 합니다. /etc/security/limits.conf를 편집합니다.

* soft nofile 65535
* hard nofile 65535

전체 설정 예시

프로덕션 환경에 권장하는 설정 템플릿입니다.

# 기본 설정
worker_processes auto;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    # TCP 최적화
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # Keepalive
    keepalive_timeout 30;
    keepalive_requests 100;

    # 버퍼 설정
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 32k;
    proxy_busy_buffers_size 64k;

    # 타임아웃 설정
    proxy_connect_timeout 10s;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;

    # gzip
    gzip on;
    gzip_types text/plain text/css application/json;

    upstream backend {
        least_conn;
        server 192.168.1.10:8080 weight=3;
        server 192.168.1.11:8080 weight=2;
        server 192.168.1.12:8080 backup;
        keepalive 32;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;

            proxy_next_upstream error timeout http_502 http_503 http_504;
            proxy_next_upstream_tries 3;
        }

        # SSE 전용 설정
        location /events {
            proxy_pass http://backend;
            proxy_buffering off;
            proxy_read_timeout 86400s;
        }
    }
}

마무리

지금까지 살펴본 내용을 세 가지로 정리할 수 있습니다.

  1. upstream 설정: 적절한 로드 밸런싱 알고리즘을 선택하고 keepalive 연결 풀과 헬스 체크를 활성화합니다.
  2. 버퍼 설정: 세 매개변수의 관계를 이해하고 특수한 상황에서는 버퍼링을 끕니다.
  3. 타임아웃 설정: 세 매개변수가 각각 무엇을 제어하는지 이해하고 상황에 맞는 전략을 사용합니다.

새벽 3시의 그 사고를 통해 Nginx 설정은 매개변수 몇 개를 채워 넣는 것으로 끝나지 않는다는 사실을 배웠습니다. 각 매개변수 뒤에는 설계 원리가 있으며, 그 원리를 이해해야 문제를 피할 수 있습니다.

Nginx를 처음 접한다면 기본 설정으로 시작한 뒤 문제가 생길 때 필요한 부분을 조정하기를 권합니다. 예전의 저처럼 proxy_read_timeout 60s를 대충 적어 곧바로 프로덕션에 적용하지 마세요. 이미 문제를 겪었다면 이 글이 흩어진 경험을 체계적으로 정리하는 데 도움이 될 것입니다.

이 글을 마치며 현재 프로덕션 환경 설정을 다시 확인해 봤습니다. keepalive 32, proxy_read_timeout 120s, least_conn 로드 밸런싱입니다. 새벽 3시의 경보는 다시 울리지 않았습니다.


FAQ

proxy_read_timeout은 전체 타임아웃인가요, 아니면 두 읽기 사이의 간격인가요?
두 읽기 작업 사이의 간격입니다. 백엔드가 처리 중에도 계속 데이터를 전송한다면(예: 하트비트 패킷), 전체 처리 시간이 5분이어도 proxy_read_timeout 60s면 충분합니다. 하지만 백엔드가 5분 동안 완전히 응답하지 않는다면 300s로 설정해야 합니다.
proxy_buffering은 언제 꺼야 하나요?
다음 세 가지 상황에서는 반드시 꺼야 합니다.

• Server-Sent Events(SSE): 실시간 푸시이므로 버퍼링하면 메시지가 지연됩니다.
• WebSocket: 양방향 실시간 통신에는 스트리밍 전송이 필요합니다.
• 대용량 파일 업로드: 메모리 고갈을 막기 위해 받는 즉시 전송해야 합니다.
keepalive 값은 어느 정도가 적절한가요?
계산식은 keepalive ≈ 총 QPS × 요청당 평균 소요 시간 ÷ worker 프로세스 수입니다. 예를 들어 QPS가 10000, 응답 시간이 50ms, worker가 4개라면 keepalive는 약 125로 설정합니다. 너무 높게 설정하지 마세요. 예전에 100으로 설정했다가 백엔드가 감당하지 못한 적이 있습니다.
502와 504는 무엇이 다른가요?
502 Bad Gateway는 Nginx가 백엔드에 연결하지 못했다는 뜻입니다(서비스 중단, 포트 불통, 방화벽 차단). 504 Gateway Timeout은 연결은 됐지만 응답 시간이 초과됐다는 뜻입니다(백엔드 처리 지연). 진단 방법은 완전히 다릅니다. 502는 프로세스와 포트를 확인하고, 504는 타임아웃 설정과 백엔드 처리 시간을 확인합니다.
어떤 로드 밸런싱 알고리즘을 선택해야 하나요?
상황에 맞게 선택합니다.

• 라운드 로빈(기본값): 상태 비저장 서비스에 요청을 고르게 분배합니다.
• least_conn: 장기 연결 환경(WebSocket, 데이터베이스 연결 풀)에 적합합니다.
• ip_hash: 세션 고정이 필요할 때 쓰는 임시 방안으로, Redis보다 안정적이지 않습니다.
• hash: 분산 캐시의 적중률을 높입니다.
upstream sent too big header 오류는 어떻게 해결하나요?
proxy_buffer_size를 늘리면 됩니다. 백엔드 응답 헤더가 너무 크면(예: Cookie가 아주 많은 경우) 기본 4k 버퍼를 초과합니다. proxy_buffer_size를 16k로 바꾸면 대개 해결됩니다.
sendfile + tcp_nopush + tcp_nodelay 조합은 왜 성능을 높이나요?
sendfile은 사용자 공간을 거치지 않고 커널에서 직접 전송하고, tcp_nopush는 데이터를 묶어 전송해 패킷 수를 줄이며, tcp_nodelay는 작은 데이터를 기다리지 않고 즉시 전송합니다. 세 설정을 조합하면 정적 파일 처리량이 실제 측정에서 30% 이상 향상됩니다.

3분 읽기 · 게시일: 2026년 3월 30일 · 수정일: 2026년 9월 4일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog