AWS NLB Preserve Client IP와 Nginx 연동 삽질기 (feat. Proxy Protocol)
오늘은 AWS 환경에서 애플리케이션을 운영하며 흔히 만날 수 있는 로드밸런서, 그중에서도 Network Load Balancer(NLB)와 Nginx를 함께 사용할 때 겪었던 Preserve Client IP
옵션 관련 삽질기를 공유해볼까 합니다. 저와 비슷한 문제로 골머리를 앓았던 분들께 조금이나마 도움이 되기를 바랍니다.
문제 상황: NLB Preserve client IP addresses
옵션
일반적인 웹 서비스 구성이었습니다.
Nginx는 리버스 프록시 역할을 수행합니다. Nginx 로그나 애플리케이션 단에서 실제 클라이언트의 IP 주소를 확인해야 할 필요성이 생겼습니다. 가장 먼저 떠오른 것은 NLB의 Preserve client IP addresses
옵션입니다. 이 옵션은 실제 클라이언트 IP로 호출 된것 같이 Nginx로 요청을 보내 줍니다.
하지만 결과는 참담했습니다. NLB의 모니터링 지표에서 해당 타겟 그룹(Nginx 인스턴스들)으로 가는 Request / Response Byte Size가 0/0 으로 찍히며 통신이 완전히 두절된 것입니다. 옵션을 끄면 다시 정상으로 돌아왔고요. 대체 왜 이런 일이 발생했을까요?
원인 분석: 비대칭 라우팅
결론부터 말하자면, Preserve Client IP
옵션을 켰을 때 발생하는 비대칭 라우팅(Asymmetric Routing) 때문이었습니다.
Preserve Client IP
= OFF (기본값, 정상 작동 시):- Client -> NLB 로 요청이 옵니다.
- NLB는 자신의 Private IP를 Source IP로 사용하여 Nginx에 요청을 전달합니다 (Source NAT 발생).
- Nginx는 NLB IP로부터 요청을 받았으므로, 응답도 NLB IP로 보냅니다.
- NLB는 응답을 받아 Client에게 전달합니다.
- 요청과 응답이 NLB를 통해 대칭적으로 이루어집니다. Nginx는 같은 VPC 내의 NLB IP와 통신하므로 문제가 없습니다. 이를 대칭 라우팅(Symmetric Routing)이라고 합니다. 즉, 요청(Request) 패킷이 지나간 경로와 응답(Response) 패킷이 돌아오는 경로가 동일한 네트워크 장비(여기서는 NLB)를 거치는 상태입니다.
Preserve Client IP
= ON (문제 발생 시):- Client -> NLB 로 요청이 옵니다.
- NLB는 Source IP를 변경하지 않고 원본 Client IP를 Source IP로 사용하여 Nginx에 요청을 전달합니다.
- Nginx는 원본 Client IP로부터 요청을 받은 것으로 인지합니다.
- Nginx는 응답을 원본 Client IP로 직접 보내려고 시도합니다!
- 이때, EC2 인스턴스(Nginx)의 기본 라우팅 경로는 보통 인터넷 게이트웨이(IGW)나 NAT 게이트웨이를 향합니다. 따라서 응답 패킷은 NLB를 거치지 않고 바로 인터넷으로 나가버립니다.
- 바로 이 지점에서 비대칭 라우팅(Asymmetric Routing)이 발생합니다. 요청 패킷은
Client -> NLB -> Nginx
경로로 왔지만, 응답 패킷은Nginx -> IGW -> Client
경로로 돌아가게 됩니다. 요청 경로와 응답 경로가 달라진 것입니다. - NLB와 같은 상태 기반(Stateful) 네트워크 장비는 자신이 전달한 요청에 대한 응답이 자신을 통해 돌아오는 것을 기대하며 연결 상태를 추적합니다. 하지만 비대칭 라우팅 상황에서는 NLB가 Nginx로 보낸 요청에 대한 응답을 볼 수 없으므로, 연결이 비정상적으로 종료되었거나 실패한 것으로 간주합니다. 이것이 0/0 카운트의 원인이었습니다.
결국, 이 옵션을 켜려면 Nginx 인스턴스의 운영체제 레벨에서 라우팅 테이블을 수정하여 클라이언트 IP로 가는 응답 트래픽이 NLB를 거치도록 매우 복잡한 설정을 해줘야 했습니다. 이건 너무 번거로웠죠.
대안 1: 그냥 Preserve Client IP
= OFF 쓰고 XFF 헤더 확인하기
가장 간단한 방법입니다. Preserve Client IP
옵션을 끄면, NLB는 자동으로 X-Forwarded-For
(XFF) HTTP 헤더에 원본 클라이언트 IP를 담아서 Nginx로 전달해줍니다. Nginx에서는 이 헤더 값을 읽으면 됩니다.
Nginx에서 XFF 헤더를 신뢰하고 실제 클라이언트 IP로 인식하게 하려면 nginx.conf
등에 다음과 같이 설정합니다.
http {
# ... 다른 설정들 ...
# NLB의 IP 대역을 신뢰하도록 설정 (VPC CIDR 사용 권장)
set_real_ip_from 10.0.0.0/16; # 예시: VPC CIDR
# 어떤 헤더를 실제 IP로 사용할지 지정
real_ip_header X-Forwarded-For;
# XFF 헤더가 여러 IP를 포함할 경우, 가장 오른쪽 IP를 사용 (가장 마지막 프록시 IP 제외)
# real_ip_recursive on; # 필요에 따라 설정
# ... 다른 설정들 ...
}
이 방법은 OS 라우팅 수정 없이 Nginx 설정만으로 해결 가능해서 매우 편리합니다. 대부분의 경우 이 방법으로 충분할 것입니다.
대안 2: Proxy Protocol v2 사용하기
하지만 어떤 이유로든 Preserve Client IP = ON
과 유사한 효과 (TCP 레벨에서 클라이언트 IP 확인 등)가 필요하거나, 혹은 다른 이유로 Preserve Client IP = OFF
상태에서 Proxy Protocol v2를 사용해야 하는 상황이 있을 수 있습니다. (예: NLB -> ALB
구성에서 Client IP 전달 등)
그래서 다음 시도를 해봤습니다.
- NLB 설정:
Preserve Client IP
= OFF, Target Group의Proxy Protocol v2
= ON - Nginx 설정: 원본 IP를 받도록 설정
이론적으로는 Preserve Client IP = OFF
이므로 라우팅은 해결되고, Proxy Protocol v2 헤더를 통해 원본 IP 정보가 TCP 스트림 내에 포함되어 Nginx로 전달될 것으로 기대했습니다.
결과: 또다시 0/0 !!
이번엔 또 무슨 문제일까요? 원인은 Nginx가 Proxy Protocol 헤더를 이해하도록 설정되지 않았기 때문이었습니다.
NLB가 Proxy Protocol v2 헤더를 보내면, Nginx는 기본적으로 이를 해석하지 못하고 비정상적인 요청으로 간주하여 연결을 끊어버립니다. NLB는 Nginx와의 통신(Health Check 포함)이 실패하니 타겟이 비정상이라고 판단하는 것이죠.
해결책: Nginx의 listen
지시어에 proxy_protocol
파라미터를 추가해야 합니다.
server {
# HTTP (80번 포트) + Proxy Protocol
listen 80 proxy_protocol;
# HTTPS (443번 포트) + Proxy Protocol
# listen 443 ssl proxy_protocol;
# Proxy Protocol 헤더를 보내는 주체(NLB)를 신뢰하도록 설정
set_real_ip_from 10.0.0.0/16; # 예시: VPC CIDR
# 실제 IP를 Proxy Protocol 헤더에서 가져오도록 설정
real_ip_header proxy_protocol;
# ... 나머지 설정 ...
}
여기서 중요한 점! set_real_ip_from
지시어는 여전히 필요합니다. 왜냐하면 real_ip_header proxy_protocol;
설정이 동작하려면, 직접 연결을 시도한 IP 주소(Preserve Client IP = OFF
이므로 NLB의 Private IP)가 신뢰할 수 있는 프록시 목록(set_real_ip_from
)에 포함되어 있어야 Nginx가 Proxy Protocol 헤더의 내용을 신뢰하기 때문입니다.
이 설정을 적용하고 Nginx를 재시작하니 드디어 NLB <-> Nginx 통신이 정상화되고, Nginx 로그에도 실제 클라이언트 IP가 찍히는 것을 확인할 수 있었습니다!
결론
AWS NLB의 Preserve Client IP
옵션은 편리해 보이지만, 비대칭 라우팅이라는 숨겨진 복병을 만날 수 있습니다. 요청 경로와 응답 경로가 달라지면서 NLB와 같은 상태 기반 장비의 연결 추적을 방해하는 이 현상은 예상치 못한 통신 장애를 유발합니다.
대부분의 경우, 이 옵션을 끄고 NLB가 자동으로 추가해주는 X-Forwarded-For
헤더를 Nginx에서 활용하는 것이 가장 간단하고 권장되는 방법입니다.
만약 Proxy Protocol v2를 사용해야 한다면, NLB 타겟 그룹 설정뿐만 아니라 Nginx의 listen
지시어에도 proxy_protocol
파라미터를 반드시 추가해야 한다는 점, 그리고 set_real_ip_from
설정으로 NLB를 신뢰하도록 지정해야 한다는 점을 잊지 마시기 바랍니다.
참고 자료
이 글의 내용은 AWS NLB 및 Nginx 설정과 관련된 일반적인 기술 개념과 문제 해결 과정을 바탕으로 작성되었습니다. 더 자세한 기술 정보는 아래 공식 문서를 참고하시면 좋습니다.
- AWS Network Load Balancer - 대상 그룹:
- Nginx Documentation:
- ‘ngx_http_realip_module’ (‘set_real_ip_from’, ‘real_ip_header’): http://nginx.org/en/docs/http/ngx_http_realip_module.html
- ‘listen’ 지시어 (‘proxy_protocol’ 파라미터): http://nginx.org/en/docs/http/ngx_http_core_module.html#listen