
앞서 다룬 ClusterIP 가 클러스터 내부 통신을 위한 것이었다면, 이번에는 클러스터 외부의 트래픽을 파드로 연결하는 방법들에 대해 알아본다. 쿠버네티스에서 서비스를 외부로 노출하는 방식은 크게 NodePort, ExternalIP, LoadBalancer가 있다.
NodePort 서비스는 외부에서 접근할 수 있는 가장 기본적인 방식이다. 이름 그대로 모든 노드(Node)의 특정 포트(Port)를 개방하여 트래픽을 수신한다.
동작 방식: 서비스 생성 시 포트를 할당하면, 클러스터의 모든 노드가 해당 포트(예: 30080)를 개방한다. 외부에서 어떤_노드의_IP:30080 으로 접근하든, 해당 트래픽은 서비스로 연결된 파드로 라우팅된다.
내부 구조: 내부적으로는 0.0.0.0:<nodePort> 형태로 바인딩되어 모든 네트워크 인터페이스의 트래픽을 수신한다.

type: NodePort로 설정한다.
spec.ports :
port: 서비스(ClusterIP) 내부에서 사용할 포트
targetPort: 실제 컨테이너가 사용하는 포트
nodePort: 외부 노드에서 개방할 포트 (생략 시 자동 할당)
포트 범위: 기본적으로 30000 ~ 32767 범위 내에서 할당된다.

sample-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: sample-nodeport
spec:
type: NodePort
ports:
- name: "http-port"
protocol: "TCP"
port: 8080 # ClusterIP 내부 포트
targetPort: 80 # 파드 내부 포트
nodePort: 30080 # 외부 노출 포트 (30000-32767)
selector:
app: sample-app
# 리소스 생성
kubectl apply -f sample-nodeport.yaml
# 리소스 확인
kubectl get service sample-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
sample-nodeport NodePort 10.96.160.113 <none> 8080:30080/TCP 10s
이제 클러스터 내의 아무 노드 IP나 잡고 30080 포트로 요청을 보내면 파드에 접속할 수 있다. NodePort 역시 내부 통신용 ClusterIP 를 자동으로 가지므로 내부 DNS 조회도 가능하다.
내부 DNS 주소 확인
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-nodeport.default.svc.cluster.local
ExternalIP 는 별도의 서비스 타입(Type)이라기보다는, ClusterIP 등의 타입에서 사용할 수 있는 설정에 가깝다. 지정된 특정 노드의 IP 주소로 들어오는 트래픽만 허용하여 파드로 전달한다.
특징: NodePort 가 모든 노드의 포트를 여는 것과 달리, 특정 IP(주로 특정 노드의 인터페이스)를 리스닝한다.
권장 사항: 최근에는 관리의 복잡성과 가용성 문제로 인해 잘 사용되지 않으며, NodePort 나 LoadBalancer 사용이 권장되는 추세이다.

type 은 기본값인 ClusterIP로 두고, externalIPs 필드를 추가한다.
sample-externalIP.yaml
apiVersion: v1
kind: Service
metadata:
name: sample-externalip
spec:
type: ClusterIP
externalIPs:
- 192.168.200.11 # 외부로 노출할 특정 노드의 IP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
selector:
app: sample-app
외부에서는 externalIPs에 지정한 노드 IP(192.168.200.11)와 서비스 포트(8080)를 통해 파드에 접근할 수 있다.
이 경우 192.168.200.11:8080 으로 들어오는 트래픽만 처리가 가능하며, 해당 노드가 다운되면 서비스 접속이 불가능해진다.

LoadBalancer 타입은 클라우드 환경(AWS, GCP, Azure 등)에서 가장 표준적으로 사용되는 외부 노출 방식이다.
자동 프로비저닝: 서비스를 생성하면 클라우드 공급자(CSP)의 로드 밸런서(L4)가 자동으로 생성된다.
동작 구조: 외부 로드 밸런서 -> 모든 노드의 NodePort -> 파드(Pod) 순서로 트래픽이 전달된다.
고가용성: NodePort 나 ExternalIP 는 특정 노드에 장애가 생기면 접근이 제한될 수 있지만(클라이언트가 죽은 노드 IP를 보고 있을 경우), LoadBalancer 는 헬스 체크를 통해 살아있는 노드로만 트래픽을 보내므로 단일 장애점(SPOF) 문제를 해결한다.


비용: 퍼블릭 클라우드에서 LoadBalancer 서비스를 생성할 때마다 별도의 로드 밸런서 리소스가 생성되므로 추가 비용이 과금된다.
IP 할당: 클라우드 환경이 아닌 경우(온프레미스 등), EXTERNAL-IP가 영원히 <pending> 상태로 남을 수 있다. 이를 해결하기 위해 MetalLB 같은 솔루션이 필요하다.
apiVersion: v1
kind: Service
metadata:
name: sample-lb
spec:
type: LoadBalancer
ports:
- name: "http-port"
protocol: "TCP"
port: 8080 # 로드밸런서가 수신할 포트
targetPort: 80 # 컨테이너의 포트
nodePort: 30082 # 노드에서 열릴 포트
selector:
app: sample-app
가상 IP 정적 지정: 일부 환경에서는 spec.loadBalancerIP 속성을 통해 외부 IP를 정적으로 지정할 수 있지만, GKE(Google Kubernetes Engine) 등에서는 지원하지 않는다.
방화벽 정책: spec.loadBalancerSourceRanges 필드에 CIDR 형식으로 IP 대역을 지정하여 접속을 허용할 소스 IP를 제한할 수 있다. 기본값은 0.0.0.0/0(전체 허용)이다. 더 세밀한 제어가 필요하면 NetworkPolicy 리소스를 사용한다.
앞서 언급했듯, AWS나 GCP 같은 클라우드 환경이 아닌 베어메탈(Bare-metal)이나 온프레미스 환경에서는 LoadBalancer 타입의 서비스를 만들어도 외부 IP를 할당해 줄 로드 밸런서가 없다. 이 문제를 해결해 주는 오픈소스 프로젝트가 MetalLB이다.
MetalLB는 쿠버네티스 클러스터 내에서 소프트웨어적으로 로드 밸런서 역할을 수행한다.
L2 모드 (ARP): 동일한 서브넷 내에서 ARP(Address Resolution Protocol)를 사용하여 "이 IP는 내가 처리하겠다"고 알리는 방식. 소규모 클러스터나 홈랩에 적합하다.
BGP 모드: 라우터와 BGP 프로토콜로 경로 정보를 교환하는 방식. 대규모 네트워크에 적합하다.
External IP 전파: 데몬셋(DaemonSet)으로 speaker라는 파드를 각 노드에 생성하여 External IP를 전파한다.
지원 환경: VMware, VirtualBox 등 가상 환경이나 Ubuntu + kubeadm으로 구성된 클러스터에 매우 적합하다. 단, AWS(EKS), GCP(GKE) 등 대부분의 퍼블릭 클라우드 환경은 자체 로드밸런서를 사용하므로 MetalLB를 지원하지 않는다.
매니페스트를 이용해 MetalLB 컨트롤러와 스피커(Speaker) 파드를 설치한다.
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml
설치 후 metallb-system 네임스페이스의 파드들이 모두 Running 상태인지 확인한다.

LoadBalancer 가 사용할 수 있는 IP 대역을 지정한다. (자신의 네트워크 환경에 맞는 유휴 IP 대역이어야 한다.)
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ippool
namespace: metallb-system
spec:
addresses:
- 192.168.11.100-192.168.11.102 # 할당 가능한 IP 범위
위에서 설정한 IP 풀을 네트워크에 알리기(Advertise) 위한 설정이다.
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-advertise
namespace: metallb-system
spec:
ipAddressPools:
- my-ippool
MetalLB 설정이 완료된 상태에서 type: LoadBalancer 서비스를 생성하면, pending 상태였던 EXTERNAL-IP 가 지정한 풀 내의 IP(예: 192.168.11.100)로 즉시 할당된다.
$ kubectl get svc sample-lb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
sample-lb LoadBalancer 10.107.243.177 192.168.11.100 8080:30082/TCP
이제 외부(같은 네트워크 대역)에서 curl 192.168.11.100:8080 명령어로 접속하면, 트래픽이 로드 밸런싱되어 파드로 전달되는 것을 확인할 수 있다.