배포한 지 3개월 된 서비스에 사용자가 몰리기 시작했습니다. 단일 WAS 서버 하나로는 동시 접속 500명을 버티지 못하고, CPU가 100%를 찍고 응답이 멈춥니다. 인프라팀 슬랙에 "백엔드 서버 2대 더 추가할게요, Nginx에서 분산해 주세요"라는 메시지가 날아옵니다.
Nginx 설정 파일 하나로 이 요청을 해결할 수 있습니다. 그 핵심이 리버스 프록시와 upstream 블록입니다.
- 1포워드 프록시와 리버스 프록시의 차이를 구조적으로 설명할 수 있다
- 2proxy_pass로 단일 백엔드에 트래픽을 전달하고 헤더를 올바르게 설정할 수 있다
- 3upstream 블록으로 서버 그룹을 정의하고 로드밸런싱 알고리즘을 선택할 수 있다
- 4WebSocket 연결을 프록시로 처리하는 설정을 작성할 수 있다
- 5헬스체크와 backup 서버로 장애 대응 구조를 구성할 수 있다
nginx -vsystemctl status nginx --no-pagerls /etc/nginx/conf.d/ /etc/nginx/sites-available/ 2>/dev/nullpython3 -m http.server 8081 --directory /tmp &리버스 프록시 개념
포워드 프록시 vs 리버스 프록시
"프록시"라는 단어가 같아서 혼동하기 쉽지만, 포워드 프록시와 리버스 프록시는 트래픽이 흐르는 방향과 보호하는 대상이 완전히 다릅니다. 실무에서 "Nginx를 프록시로 써라"는 말을 들었을 때, 클라이언트를 대신하는 역할인지 서버를 보호하는 역할인지 구분하지 못하면 설정 방향 자체가 틀려집니다. 리버스 프록시의 핵심은 백엔드 서버를 인터넷에 직접 노출하지 않고, 트래픽 제어를 한 지점에서 처리한다는 것입니다.
확대
왜 "리버스"라는 단어가 붙었을까요? 방향이 반대이기 때문입니다. 포워드 프록시는 클라이언트 앞에 서서 내부 사용자가 외부 인터넷에 나가는 트래픽을 대신 처리합니다 (사내 방화벽, VPN이 대표적). 리버스 프록시는 서버 앞에 서서 외부 클라이언트가 내부 서버로 들어오는 트래픽을 받습니다.
클라이언트 입장에서는 Nginx 뒤에 백엔드 서버가 몇 대 있는지, 어떤 언어로 짜여 있는지 알 수 없습니다. 이 불투명성이 보안, 부하분산, 캐싱, SSL 종단 처리를 한 곳에서 해결하는 구조를 만듭니다.
| 구분 | 포워드 프록시 | 리버스 프록시 |
|---|---|---|
| 위치 | 클라이언트 앞 | 서버 앞 |
| 대신 처리 | 클라이언트의 외부 요청 | 서버로 들어오는 요청 |
| 대표 사용처 | 사내망, VPN, 캐시 | Nginx, HAProxy, AWS ALB |
| 보호 대상 | 클라이언트 신원 | 백엔드 서버 구조 |
# 리버스 프록시 가장 기본 형태
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080; # 백엔드 서버로 전달
}
}
리버스 프록시가 요청을 백엔드로 넘기는 단계
proxy_pass 한 줄 뒤에서 무슨 일이 일어나나 — 요청 수신부터 응답 중계까지 6단계
location / { proxy_pass http://app_servers; } 한 줄이면 트래픽이 백엔드로 넘어갑니다. 하지만 그 한 줄 뒤에서 Nginx는 요청을 받고 → 어느 upstream에 보낼지 고르고 → 헤더를 다시 쓰고 → 백엔드에 연결하고 → 응답을 받아 중계하는 6단계를 밟습니다. 이 단계를 알아야 "왜 백엔드 로그에 클라이언트 IP가 안 남지", "왜 502지", "왜 504지"가 각각 어느 단계의 문제인지 짚을 수 있습니다.
[클라이언트] GET /api/order (Host: shop.example.com)
│
① 요청 수신 Nginx가 클라이언트 연결 종단 (여기서 remote_addr = 클라이언트 IP)
│
② location 매칭 /api/ location → proxy_pass http://app_servers 결정
│
③ upstream 선택 app_servers 풀에서 알고리즘(round-robin·least_conn·ip_hash)으로 1대 선택
│
④ 헤더 재작성 Host·X-Real-IP·X-Forwarded-For 세팅 후 백엔드 전달 요청 구성
│
⑤ 백엔드 연결·전송 선택된 WAS로 TCP 연결(또는 keepalive 재사용) → 요청 송신
│
⑥ 응답 중계·버퍼링 백엔드 응답을 buffer에 받아 클라이언트로 전달 (proxy_buffering)
▼
[클라이언트] 200 / 502 / 504
각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① 요청 수신 | Nginx가 클라이언트 연결을 종단. 이 시점의 $remote_addr이 실제 클라이언트 IP | 앞단 LB가 있으면 $remote_addr이 LB IP → real_ip_module로 복원 필요 |
| ② location 매칭 | URL로 location 결정 → proxy_pass 대상 확정 | 매칭 우선순위 오해로 엉뚱한 proxy_pass 적용 → 다른 백엔드로 감(nginx -T로 확인) |
| ③ upstream 선택 | upstream 풀에서 알고리즘으로 서버 1대 선택. max_fails 초과 서버는 일시 제외 | 모든 서버가 max_fails 초과로 격리 → no live upstreams → 502 |
| ④ 헤더 재작성 | proxy_set_header로 Host·X-Real-IP·X-Forwarded-For 전달 | 이 설정 누락 → 백엔드 로그에 클라이언트 IP 대신 Nginx IP만 남음 / Host 누락 → 백엔드 가상호스트 라우팅 어긋남 |
| ⑤ 백엔드 연결·전송 | 선택된 WAS로 TCP 연결 후 요청 송신. keepalive면 유휴 연결 재사용 | 백엔드 다운·포트 오지정·방화벽 차단 → connect() failed → 502 |
| ⑥ 응답 중계·버퍼링 | 백엔드 응답을 proxy_buffers에 받아 클라이언트로 중계 | 응답이 proxy_read_timeout 초과 → 504 / 응답 헤더가 buffer보다 큼 → upstream sent too big header → 502 |
즉 리버스 프록시의 상태코드는 어느 단계에서 백엔드와의 중계가 깨졌는지를 가리킵니다 — 502는 ③·⑤에서 백엔드에 아예 못 붙은 것(다운·격리·포트), 504는 ⑤·⑥에서 붙었지만 응답이 제 시간에 안 온 것(백엔드 처리 지연), "클라이언트 IP 유실"은 ④에서 헤더를 안 넘긴 것입니다. 그래서 502면 백엔드에 직접 curl로 붙어보고(⑤ 확인), 504면 $upstream_response_time으로 지연을 재고, IP 유실이면 nginx -T로 ④의 proxy_set_header 설정을 확인하는 식으로 단계별로 접근합니다.
proxy_pass와 proxy_set_header — 헤더 전달의 중요성
proxy_pass만 설정하면 트래픽은 넘어가지만 문제가 생깁니다. 백엔드 서버는 요청을 보낸 주체가 클라이언트가 아니라 Nginx(127.0.0.1)라고 인식합니다. 이렇게 되면 접근 로그 분석, IP 기반 차단, 지역 기반 기능이 모두 망가집니다.
proxy_set_header는 이 문제를 해결합니다. Nginx가 실제 클라이언트 정보를 헤더에 담아 백엔드로 전달합니다. 실무에서 이 설정을 빠뜨리면 "왜 모든 접속이 내부 IP로만 찍히죠?"라는 질문이 나옵니다.
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
# 실제 클라이언트 IP를 백엔드에 전달
proxy_set_header X-Real-IP $remote_addr;
# 프록시 체인 전체 IP 목록 (여러 프록시를 거쳤을 때 유용)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 원본 Host 헤더 전달 (백엔드가 가상호스트 기반일 때 필수)
proxy_set_header Host $host;
# 백엔드가 HTTP/1.1을 사용하도록 강제 (기본값은 HTTP/1.0)
proxy_http_version 1.1;
# 프록시 연결 타임아웃
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
자주 쓰는 Nginx 변수:
| 변수 | 의미 |
|---|---|
$remote_addr | 직접 연결한 IP (프록시 앞단이면 프록시 IP) |
$proxy_add_x_forwarded_for | 기존 X-Forwarded-For + $remote_addr |
$host | 요청의 Host 헤더 값 |
$request_uri | 전체 요청 URI (쿼리스트링 포함) |
$scheme | http 또는 https |
확대
upstream 블록과 로드밸런싱 알고리즘
WAS 서버를 3대로 늘렸는데 특정 서버에 요청이 몰려 CPU가 튀는 현상이 있었습니다. 파일 업로드 API라 요청 처리 시간이 일정하지 않았고, 라운드로빈으로는 "처리 중인 요청이 많은 서버"를 고려하지 못했기 때문입니다. 알고리즘을 least_conn으로 바꾸니 부하가 균등해졌습니다. upstream 블록 없이 여러 서버를 관리하면 설정이 복잡해지고 가중치, 헬스체크, 알고리즘 설정도 불가합니다.
Nginx 오픈소스 버전이 지원하는 세 가지 알고리즘은 각각 적합한 상황이 다릅니다.
# round-robin (기본값) — 순서대로 균등 분배
upstream app_backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
# weight — 서버 성능이 다를 때
upstream app_backend_weighted {
server 10.0.0.1:8080 weight=3; # 3배 더 많은 요청
server 10.0.0.2:8080 weight=1;
}
# least_conn — 활성 연결이 적은 서버 우선
upstream app_backend_lc {
least_conn;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
# ip_hash — 같은 클라이언트는 같은 서버로 (세션 고정)
upstream app_backend_session {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
알고리즘 선택 가이드:
| 알고리즘 | 적합한 상황 | 주의사항 |
|---|---|---|
| round-robin | API, 정적 콘텐츠 — 요청이 거의 동일한 무게 | 기본값, 별도 설정 불필요 |
| weight | 서버 사양이 다를 때 | 성능 차이 측정 후 비율 설정 |
| least_conn | 파일 업로드, 스트리밍 — 요청마다 시간이 다름 | 짧은 요청엔 round-robin이 나을 수도 |
| ip_hash | 서버 측 세션 저장, 외부 세션스토어 미사용 | NAT 환경에서 특정 서버 집중 발생 가능 |
실습: 기본 리버스 프록시 설정
아래 내용을 파일에 작성합니다. backup 디렉티브를 붙인 8082는 8081이 다운됐을 때만 사용됩니다. 저장 후 nginx -t로 문법 검사하고 reload합니다.
upstream app_servers {
server 127.0.0.1:8081;
server 127.0.0.1:8082 backup;
}
server {
listen 9090;
server_name _;
access_log /var/log/nginx/proxy-demo-access.log;
error_log /var/log/nginx/proxy-demo-error.log;
location / {
proxy_pass http://app_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
}
}
sudo nginx -t && sudo systemctl reload nginx
sudo nano /etc/nginx/conf.d/proxy-demo.conf- nginx -t 로 문법 확인 먼저, 통과하면 curl -s http://localhost:9090/ -I 로 응답 코드 확인 — nginx -t 없이 reload 시 upstream 블록 문법 오류가 있으면 Nginx 자체가 재시작 실패
- 응답 코드 기준: 200=백엔드 서버가 살아있고 프록시 정상 동작, 502=Nginx는 떠있지만 백엔드 서버가 없거나 연결 불가(설정 테스트 시 502는 정상), 504=백엔드 응답 시간 초과
- curl에서 502가 나오는데 access.log에도 기록이 안 되면 → upstream 블록이 로드되지 않은 것 — nginx -T | grep upstream 으로 설정이 실제로 반영됐는지 확인
아래 내용을 작성합니다. max_fails=3 fail_timeout=30s는 수동 헬스체크 역할을 합니다. keepalive 32는 Nginx와 백엔드 사이 유휴 커넥션을 유지해 오버헤드를 줄입니다.
upstream api_pool {
least_conn;
server 10.0.0.1:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://api_pool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
sudo nginx -t
sudo nano /etc/nginx/conf.d/lb-demo.conf- nginx -T 2>/dev/null | grep -A 10 "upstream api_pool" 로 upstream 설정 반영 확인 먼저, 그 다음 curl 로 실제 요청 테스트 — upstream 블록이 없으면 요청이 백엔드로 전달 안 됨
- least_conn 분산 기준: 10회 요청 후 access.log의 upstream_addr 분포가 weight 2:2:1 비율(40%:40%:20%)에 근사하면 정상 — 한 서버로만 몰리면 upstream 블록이 반영 안 된 것
- 모든 요청이 upstream_addr 중 하나에서만 응답하면 → 다른 서버들이 max_fails 초과로 일시 차단된 것 — error.log에서 "upstream timed out" 또는 "connect() failed" 로 차단 원인 확인
WebSocket 프록시 설정
WebSocket — 프로토콜 업그레이드 처리
채팅 기능을 배포했는데 연결이 5초마다 끊겼습니다. Nginx에 WebSocket 설정이 없어서 연결 유지가 안 됐기 때문입니다. proxy_http_version 1.1과 Upgrade 헤더 설정을 추가하니 연결이 지속됐습니다. WebSocket은 처음에 HTTP로 시작해 프로토콜 전환을 요청하는 구조라, Nginx가 이 헤더를 전달하지 않으면 백엔드가 전환을 거부합니다.
WebSocket은 일반 HTTP와 달리 연결을 끊지 않고 양방향으로 지속합니다. 채팅, 알림, 실시간 대시보드가 대표적입니다. 문제는 Nginx의 기본 프록시 설정이 WebSocket을 인식하지 못한다는 점입니다.
WebSocket 연결은 처음에 HTTP로 시작해 Upgrade: websocket 헤더로 프로토콜 전환을 요청합니다. Nginx가 이 헤더를 그냥 지나치면 백엔드는 프로토콜 전환을 거부하고 연결이 실패합니다. proxy_http_version 1.1과 Connection Upgrade 헤더 설정이 이 문제를 해결합니다.
확대
upstream ws_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name ws.example.com;
# WebSocket 엔드포인트
location /ws/ {
proxy_pass http://ws_backend;
# WebSocket 필수 설정
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket Upgrade 헤더 전달
proxy_set_header Connection "upgrade"; # 커넥션 유지
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket은 연결이 오래 유지되므로 타임아웃 늘리기
proxy_read_timeout 3600s; # 1시간
proxy_send_timeout 3600s;
}
# 일반 HTTP API는 별도 location
location /api/ {
proxy_pass http://ws_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
}
}
트러블슈팅
원인: Nginx가 upstream 서버에 연결하지 못했습니다. 가장 흔한 원인 두 가지입니다.
- 백엔드 서버가 실행 중이지 않거나 포트가 다르다
- 방화벽이 Nginx → 백엔드 연결을 차단한다
# 1. 백엔드 서버가 해당 포트에서 실제로 listening하는지 확인
ss -tlnp | grep ':8080'
# 2. Nginx에서 백엔드 포트로 직접 연결 테스트
curl -v http://127.0.0.1:8080/health
# 3. Nginx 에러 로그 확인 — 실제 에러 메시지 확인
sudo tail -50 /var/log/nginx/error.log | grep upstream
# 4. SELinux가 Nginx의 네트워크 연결을 차단하는지 확인 (CentOS/RHEL)
sudo ausearch -m avc -ts recent | grep nginx
# 허용이 필요하면:
sudo setsebool -P httpd_can_network_connect 1
# 5. upstream 설정 내 IP/포트 오타 여부 확인
sudo nginx -T | grep -A 5 'upstream'
보통 curl http://127.0.0.1:8080이 성공하면 Nginx 설정 문제이고, 실패하면 백엔드 서버 문제입니다.
원인: proxy_set_header X-Real-IP와 X-Forwarded-For 설정이 누락됐거나, 백엔드 애플리케이션이 해당 헤더를 읽지 않고 있습니다.
# 1. Nginx 설정에서 헤더 설정 확인
sudo nginx -T | grep -E 'X-Real-IP|X-Forwarded'
# 설정이 없다면 location 블록에 추가
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 2. 실제로 헤더가 전달되는지 테스트용 엔드포인트로 확인
# Python 백엔드라면 request.headers.get('X-Real-IP') 출력
# 3. 중간에 다른 프록시가 더 있다면 real_ip_header 모듈 필요
# nginx.conf http 블록 또는 server 블록에 추가:
# set_real_ip_from 10.0.0.0/8; # 앞단 로드밸런서 IP 대역
# real_ip_header X-Forwarded-For;
# real_ip_recursive on;
# 4. 설정 변경 후 반드시 reload
sudo nginx -t && sudo systemctl reload nginx
AWS ALB, CloudFlare 등 앞단 프록시가 있을 경우 X-Forwarded-For에 IP가 여러 개 쌓입니다. real_ip_module로 신뢰할 프록시 대역을 설정하면 $remote_addr에서 진짜 클라이언트 IP를 꺼낼 수 있습니다.
심화 — 프록시가 바이트를 실제로 옮기는 법
심화: upstream keepalive와 응답 버퍼링 — proxy_pass가 말해주지 않는 것
proxy_pass는 '어디로 보낼지'만 정합니다. '연결을 재사용할지, 응답을 어떻게 실어 나를지'는 keepalive와 버퍼링 설정이 정하는데, 이걸 모르면 부하가 커질 때 원인 모를 포트 고갈·502·스트리밍 지연을 만납니다.
- 기본은 매 요청마다 새 연결:
proxy_pass만 쓰면 Nginx는 요청마다 백엔드로 TCP 연결을 새로 맺고 응답 후 닫습니다. 트래픽이 크면 Nginx→백엔드 사이에 짧게 살고 죽는 소켓이 폭증해, 닫힌 소켓이TIME_WAIT로 쌓이고 로컬 포트(ephemeral port)가 고갈됩니다. - keepalive는 조건부로만 켜집니다:
upstream블록의keepalive N으로 유휴 연결 풀을 두면 연결을 재사용합니다. 그런데 이게 실제로 켜지려면 그 location에proxy_http_version 1.1과proxy_set_header Connection ""(hop-by-hop Connection 헤더 제거)가 함께 있어야 합니다. 둘 중 하나라도 없으면 Nginx는 백엔드에Connection: close를 보내 매번 연결을 닫아버려,keepalive 32를 적어놨어도 무효입니다. 그리고keepalive는 반드시upstream블록 안에 있어야 하며 server/location에 두면 동작하지 않습니다. - 응답 버퍼링: 기본값
proxy_buffering on에서 Nginx는 백엔드 응답을 버퍼(proxy_buffers)에 받아 두었다가 클라이언트로 내보냅니다. 느린 클라이언트가 다 받기 전에 백엔드 연결을 먼저 놓아줄 수 있어 대개 유리하지만, 두 함정이 있습니다 — ① SSE·스트리밍·실시간 진행률은 버퍼링 탓에 '한꺼번에 몰려' 나올 수 있어 해당 location만proxy_buffering off가 필요. ② 백엔드가 보낸 응답 헤더가proxy_buffer_size보다 크면(거대한 Set-Cookie·토큰 등)upstream sent too big header로 502가 나므로proxy_buffer_size·proxy_buffers를 키워야 합니다.
정리하면 리버스 프록시의 성능·안정성은 proxy_pass 한 줄이 아니라 '연결 재사용(keepalive)'과 '응답 실어나르기(buffering)'라는 두 내부 동작에서 갈립니다.
상황: upstream에 keepalive 32까지 넣었는데도, 트래픽이 오르면 백엔드 서버 자원은 여유가 있는데 Nginx 쪽에서 간헐적 502·connect 실패가 납니다. Nginx 서버에서 ss -s를 보면 TIME_WAIT 소켓이 수만 개고, error.log에는 connect() failed (99: Cannot assign requested address)가 반복됩니다.
원인: Nginx↔백엔드 연결이 재사용되지 않고 매 요청마다 새로 맺혔다 닫히고 있습니다. keepalive 32는 적었지만, 그 location의 proxy_http_version이 1.0(기본)이거나 proxy_set_header Connection ""가 빠져서 Nginx가 백엔드에 Connection: close를 보내 keepalive가 무효가 된 것입니다. 닫힌 연결이 TIME_WAIT로 쌓여 로컬 ephemeral 포트가 고갈되면(에러 99: Cannot assign requested address) 새 연결을 못 맺어 502가 납니다. 백엔드가 죽은 게 아니라, Nginx가 스스로 나갈 포트가 없어진 것입니다.
진단: ss -tan state time-wait | wc -l로 TIME_WAIT 규모를 보고, ss -tan | grep :8080 같은 식으로 백엔드 연결이 소수의 ESTAB로 유지·재사용되는지 아니면 계속 새로 생겼다 사라지는지 확인합니다. nginx -T로 문제 location에 proxy_http_version 1.1과 proxy_set_header Connection ""가 실제로 있는지, keepalive가 upstream 블록 안에 있는지 대조합니다. error.log의 Cannot assign requested address가 결정적 단서입니다.
해결: 해당 location(또는 상속되는 위치)에 proxy_http_version 1.1;과 proxy_set_header Connection "";를 넣어 keepalive가 실제로 동작하게 합니다. 그러면 소켓 재사용으로 TIME_WAIT·포트 소비가 급감합니다. 커널 측 완화(ephemeral 포트 범위 확장·tw_reuse)는 보조 수단일 뿐 근본은 연결 재사용입니다. keepalive가 upstream이 아닌 엉뚱한 블록에 있었다면 그것부터 바로잡습니다(Nginx 설치, 기본 설정, 정적 파일 서빙 실무).
실무 맥락
실제 업무에서 이 설정이 쓰이는 세 가지 상황:
1. 배포 무중단 처리 (Rolling Deploy)
upstream에서 특정 서버를 일시적으로 제외하고 배포한 뒤 다시 추가하는 패턴입니다.
# 배포 전: 10.0.0.2 서버를 down 처리
# upstream 블록에 'server 10.0.0.2:8080 down;' 추가 후 reload
# 배포 완료 후: down 제거 후 reload
sudo nginx -t && sudo systemctl reload nginx
Nginx Plus를 쓰면 API로 동적 처리가 가능하지만, 오픈소스는 설정 수정 + reload가 표준 방법입니다.
2. 경로별 다른 서비스로 라우팅 (마이크로서비스)
server {
listen 80;
server_name api.company.com;
location /auth/ { proxy_pass http://auth_service; }
location /orders/ { proxy_pass http://order_service; }
location /catalog/ { proxy_pass http://catalog_service; }
}
API 게이트웨이 없이 Nginx 설정만으로 마이크로서비스를 하나의 도메인에서 서비스하는 구조입니다. 실제 현장에서 빠르게 쓰는 방법입니다.
3. 헬스체크와 알람 연동
max_fails와 fail_timeout으로 자동 격리가 되지만, 실제로는 모니터링(Prometheus, Datadog)과 연동해 upstream 서버가 제외됐을 때 알람이 오도록 구성합니다. Nginx의 ngx_http_upstream_module 메트릭을 expose하는 nginx-prometheus-exporter를 함께 띄우는 것이 일반적입니다.
명령어·단축키 빠른 참조
이 모듈에서 다룬 리버스 프록시·로드밸런싱 지시어와 진단 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
proxy_pass | 요청을 백엔드/upstream으로 전달 | proxy_pass http://app_servers; |
upstream 블록 | 백엔드 서버 그룹 정의 | 안에 server 10.0.0.1:8080; 나열 |
least_conn / ip_hash | 로드밸런싱 알고리즘 | 기본 round-robin, 세션고정 ip_hash |
server … weight=3 | 서버별 가중치 | 성능 좋은 서버에 더 많은 요청 |
server … max_fails=3 fail_timeout=30s | 수동 헬스체크(자동 격리) | 예비 서버는 server …:8082 backup; |
keepalive 32 | 백엔드 연결 재사용 풀 | proxy_http_version 1.1+Connection "" 필수 |
proxy_http_version 1.1 | HTTP/1.1 강제(기본 1.0) | WebSocket·keepalive 재사용에 필수 |
proxy_set_header | 클라이언트 정보 전달 | X-Real-IP $remote_addr, Host $host |
proxy_set_header Upgrade $http_upgrade | WebSocket 프로토콜 전환 | Connection "upgrade" 와 세트 |
nginx -t && systemctl reload nginx | 검증 후 무중단 반영 | upstream 문법 오류 시 재시작 실패 방지 |
nginx -T | grep -A10 upstream | upstream 반영 여부 확인 | 헤더는 grep -E 'X-Real-IP|X-Forwarded' |
ss -tlnp | grep ':8080' | 백엔드 리스닝 확인(502 진단) | 직접 테스트 curl -v http://127.0.0.1:8080/health |
ss -tan state time-wait | wc -l | TIME_WAIT 폭증 확인 | keepalive 미동작 시 포트 고갈 신호 |
setsebool -P httpd_can_network_connect 1 | SELinux 프록시 연결 허용 | CentOS 502 원인 조회 ausearch -m avc |
관련 모듈로 더 깊이:
- Nginx 설치, 기본 설정, 정적 파일 서빙 실무 — 리버스 프록시 설정의 기반이 되는 Nginx 기본 디렉티브와 구조
- Let's Encrypt 인증서 발급과 Nginx SSL 설정 — 프록시 앞단에서 SSL/TLS를 종료하는 HTTPS 구성
- L4/L7 로드밸런서와 VIP 기반 이중화 구성 — upstream 분산을 VIP·로드밸런서 레벨로 확장하는 이중화 설계
다음 모듈에서는 Nginx SSL/TLS 설정과 Let's Encrypt 인증서 자동 갱신을 다룹니다.