Kubernetes(k8s)에서 서비스를 외부로 노출해야 할 때 가장 먼저 떠오르는 방법 중 하나는 NodePort 타입입니다. 설정이 간편하고 직관적이어서 개발 환경이나 간단한 테스트 용도로 널리 사용되죠. 하지만 NodePort의 동작 방식을 자세히 들여다보지 않으면 운영 환경에서 예상치 못한 성능 저하와 비효율을 겪을 수 있습니다.
NodePort 서비스를 생성하면, 해당 서비스를 위한 파드(Pod)가 특정 노드에만 실행되고 있더라도 클러스터의 모든 노드에 지정된 포트가 열리게 됩니다. 그리고 어떤 노드의 포트로 트래픽이 들어오든, 쿠버네티스는 이 트래픽을 실제 파드가 있는 곳으로 알아서 전달해 줍니다. 정말 편리해 보이지만, 바로 이 지점에서 문제가 시작됩니다.
이번 글에서는 NodePort의 기본 동작 방식이 야기하는 문제점들을 상세히 짚어보고, 이를 최소화할 수 있는 효과적인 방안들을 소개하겠습니다.
NodePort의 숨겨진 비효율성: 모든 노드에 포트가 열리는 이유
이 현상을 이해하려면 쿠버네티스 네트워킹의 핵심 컴포넌트인 kube-proxy의 역할을 알아야 합니다. kube-proxy는 클러스터의 모든 노드에서 실행되는 데몬으로, Service와 Endpoint의 변경 사항을 감시하며 네트워크 규칙(기본적으로 iptables 또는 IPVS)을 설정하는 역할을 합니다.
NodePort 서비스가 생성되면 kube-proxy는 다음과 같이 동작합니다.
- 클러스터의 모든 노드에 지정된 NodePort를 개방하도록 네트워크 규칙을 설정합니다.
- 어떤 노드로든 해당 포트의 트래픽이 들어오면, kube-proxy는 이 요청을 가로챕니다.
- kube-proxy는 해당 Service에 연결된 실제 파드들의 IP 목록을 알고 있으므로, 그중 하나를 선택하여 트래픽을 전달(DNAT, Destination Network Address Translation)합니다.
이것이 바로 NodePort 서비스의 기본 정책인 externalTrafficPolicy: Cluster의 동작 방식입니다. 파드가 있든 없든 모든 노드가 트래픽을 받을 수 있는 관문이 되고, 클러스터 전체(Cluster-wide)로 트래픽을 분산시켜주는 것이죠.

그림 1: externalTrafficPolicy: Cluster 동작 방식
위 그림처럼 외부 트래픽이 파드가 없는 Node A로 들어와도, kube-proxy가 파드가 있는 Node B로 트래픽을 전달해줍니다.
이로 인해 발생하는 문제점들
이러한 Cluster 정책의 동작 방식은 편리함의 대가로 몇 가지 심각한 문제점을 야기합니다.
1. 불필요한 네트워크 홉(Hop) 발생 및 지연 시간 증가 🐢
가장 큰 문제입니다. 위 그림의 예시처럼, 트래픽이 파드가 없는 노드(Node A)로 먼저 도달한 뒤 실제 파드가 있는 노드(Node B)로 다시 전달되어야 합니다. 이 과정에서 불필요한 네트워크 홉이 추가됩니다.
- 외부 클라이언트 → Node A → Node B → 파드
이 추가 홉은 패킷이 노드 간 네트워크를 한 번 더 거치게 만들어 약간의 네트워크 지연 시간(Latency)을 추가합니다. 마이크로서비스 환경에서 이러한 작은 지연들이 쌓이면 전체 애플리케이션의 응답성에 영향을 줄 수 있습니다.
2. 소스 IP 주소 손실 (Source IP Address Preservation 문제) 🕵️
매우 중요한 문제입니다. Node A가 Node B로 트래픽을 전달할 때, 패킷의 출발지 IP 주소(Source IP)가 **원본 클라이언트의 IP가 아닌 Node A의 내부 IP로 변경(SNAT, Source Network Address Translation)**됩니다.
결과적으로 최종 목적지인 파드 입장에서는 모든 트래픽이 클러스터 내부 노드로부터 오는 것처럼 보이게 됩니다. 이로 인해 다음과 같은 문제가 발생합니다.
- 로그 분석의 어려움: 실제 클라이언트의 IP를 알 수 없어 정확한 접속 통계나 사용자 추적이 불가능합니다.
- 보안 정책 적용 불가: 특정 IP 대역을 차단하는 등의 보안 정책을 애플리케이션 레벨에서 적용할 수 없습니다.
- 지역 기반 서비스 제약: 클라이언트의 위치에 따라 다른 콘텐츠를 제공하는 서비스 구현이 어려워집니다.
3. 네트워크 대역폭 낭비 💸
노드 간 트래픽 전달은 클러스터 내부 네트워크의 대역폭을 소모합니다. 대규모 트래픽이 발생하는 서비스라면 이러한 불필요한 트래픽이 네트워크에 부담을 주어 다른 정상적인 통신까지 방해하는 네트워크 병목 현상을 유발할 수 있습니다.
문제점을 최소화하는 방법
다행히 쿠버네티스는 이러한 문제들을 해결할 수 있는 옵션을 제공합니다.
1. externalTrafficPolicy: Local 설정 (가장 간단한 해결책)
가장 직접적이고 간단한 해결책은 Service의 externalTrafficPolicy를 Cluster에서 Local로 변경하는 것입니다.
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30007
externalTrafficPolicy: Local # 이 부분을 추가 또는 변경합니다.
externalTrafficPolicy: Local로 설정하면 kube-proxy는 더 이상 트래픽을 다른 노드로 전달하지 않습니다. 오직 자신이 실행 중인 노드에 해당 파드가 있을 경우에만 트래픽을 받아서 전달합니다. 만약 파드가 없는 노드로 트래픽이 들어오면 해당 패킷은 그냥 버려집니다(Dropped).

그림 2: externalTrafficPolicy: Local 동작 방식
장점:
- 불필요한 홉 제거: 트래픽이 파드가 있는 노드로 직접 전달되어 네트워크 효율성이 극대화되고 지연 시간이 줄어듭니다.
- 소스 IP 보존: SNAT가 발생하지 않아 파드에서 원본 클라이언트의 IP 주소를 확인할 수 있습니다.
주의할 점:
- 불균등한 트래픽 분산: 만약 Node A에 3개의 파드가 있고 Node B에 1개의 파드가 있다면, 외부 로드밸런서가 Node A와 Node B에 트래픽을 50:50으로 보낼 경우 Node B의 파드가 Node A의 파드들보다 3배 더 많은 트래픽을 받게 됩니다. 파드를 노드에 고르게 분산시키는 전략(예: podAntiAffinity)이 중요해집니다.
- 로드밸런서 Health Check: 클라우드 환경에서 Type: LoadBalancer와 함께 사용할 때, 클라우드 로드밸런서는 모든 노드의 NodePort로 Health Check를 보냅니다. Local 정책에서는 파드가 없는 노드가 Health Check에 응답하지 않으므로, 로드밸런서는 해당 노드를 비정상으로 판단하고 트래픽 전달 대상에서 제외할 수 있습니다. 이는 의도된 동작이지만, 반드시 인지하고 있어야 합니다.
2. Ingress Controller 사용 (더 나은 대안)
사실상 대부분의 프로덕션 환경에서는 NodePort를 직접 노출하기보다는 Ingress Controller를 사용하는 것이 표준적인 방식입니다.

Ingress는 HTTP/HTTPS 트래픽을 위한 L7 로드밸런싱 규칙을 정의하는 쿠버네티스 오브젝트입니다. Ingress Controller(예: NGINX Ingress Controller, Traefik)는 이 규칙에 따라 실제로 트래픽을 처리하는 파드입니다.
- Ingress Controller 파드를 Deployment나 DaemonSet으로 클러스터에 배포합니다.
- 이 Ingress Controller를 외부로 노출하기 위해 NodePort 또는 LoadBalancer 타입의 서비스를 사용합니다. (이때 보통 externalTrafficPolicy: Local을 적용합니다.)
- 외부 트래픽은 먼저 Ingress Controller로 들어온 뒤, 정의된 Ingress 규칙(호스트명, 경로 등)에 따라 적절한 내부 서비스로 라우팅됩니다.
이 방식은 트래픽 진입점을 Ingress Controller로 중앙화하여 NodePort의 문제를 근본적으로 해결하고, SSL/TLS 종료, 경로 기반 라우팅 등 훨씬 더 풍부한 기능을 제공합니다.
결론
NodePort는 쿠버네티스를 시작할 때 서비스를 외부에 노출하는 간편한 방법을 제공하지만, 그 기본 동작 방식(externalTrafficPolicy: Cluster)은 네트워크 비효율성과 소스 IP 손실이라는 명백한 단점을 가지고 있습니다.
- 간단한 성능 개선과 소스 IP 보존이 필요하다면 **externalTrafficPolicy: Local**을 적용하는 것이 가장 빠른 해결책입니다. 다만 이로 인한 트래픽 분산과 Health Check 동작의 변화를 반드시 이해해야 합니다.
- 안정적이고 확장성 있는 프로덕션 환경을 구축한다면, NodePort를 직접 사용하는 대신 Ingress Controller를 도입하는 것을 강력히 권장합니다. 이는 쿠버네티스 네트워킹의 모범 사례(Best Practice)이며, 트래픽 관리를 훨씬 더 유연하고 효율적으로 만들어줍니다.
쿠버네티스의 편리함 뒤에 숨겨진 동작 원리를 이해하고 최적의 설정을 찾아가는 과정은 더 견고하고 효율적인 시스템을 만드는 첫걸음이 될 것입니다.
'일반IT' 카테고리의 다른 글
| 🚀 Argo CD와 함께 알아보는 가장 인기 있는 CD 프로젝트 TOP 5 (3) | 2025.08.01 |
|---|---|
| Deployment vs. StatefulSet: 내 애플리케이션에 맞는 컨트롤러는? (1) | 2025.07.31 |
| 👋 kube-proxy와 작별하고 Cilium을 맞이해야 하는 이유 (3) | 2025.07.30 |
| Kubernetes 무중단 배포의 비밀: 안정적인 롤링 업데이트를 위한 3가지 핵심 기능 (4) | 2025.07.30 |
| SSD를 바꿨는데 윈도우가 저절로 정품 인증? 마법 같은 디지털 라이선스의 비밀 (1) | 2025.07.30 |