Service 타입 정리 (ClusterIP · NodePort · LoadBalancer)
Pod는 재시작될 때마다 IP가 바뀝니다. 특정 Pod의 IP를 직접 기록해두는 방법은 실전에서 통하지 않습니다. Service는 이 문제를 해결합니다. 라벨 셀렉터로 Pod 집합을 선택하고, 항상 같은 주소로 트래픽을 전달하는 안정적인 엔드포인트를 제공합니다.
Service 타입은 네 가지입니다. 각 타입은 "어디서 접근할 수 있는가"를 결정합니다.
Service 타입 비교
| 타입 | 접근 범위 | 클라우드 비용 | 주요 용도 |
|---|---|---|---|
| ClusterIP | 클러스터 내부만 | 없음 | Pod 간 통신, 내부 서비스 |
| NodePort | 노드 IP:포트 | 없음 | 개발/테스트, 간단한 외부 노출 |
| LoadBalancer | 외부 IP (클라우드) | 로드밸런서 1개당 과금 | 프로덕션 단일 서비스 노출 |
| ExternalName | 외부 DNS 이름 | 없음 | 클러스터 외부 서비스 추상화 |
ClusterIP
가장 기본적인 타입입니다. 클러스터 내부에서만 사용할 수 있는 가상 IP를 할당합니다. 타입을 명시하지 않으면 ClusterIP가 기본값입니다.
apiVersion: v1kind: Servicemetadata: name: api-service namespace: defaultspec: type: ClusterIP # 생략해도 동일 selector: app: api-server # 이 라벨을 가진 Pod에게 트래픽 전달 ports: - name: http port: 80 # Service가 수신하는 포트 targetPort: 8080 # Pod 컨테이너의 포트
# ClusterIP 확인kubectl get svc api-service# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# api-service ClusterIP 10.96.45.123 <none> 80/TCP 5m# 클러스터 내부 다른 Pod에서 접근curl http://api-service.default.svc.cluster.local/healthcurl http://api-service/health # 같은 네임스페이스라면 단축 가능
spec.clusterIP: None을 지정하면 Headless Service가 됩니다. 가상 IP 없이 Pod IP를 직접 DNS로 반환합니다. StatefulSet에서 각 Pod에 고정 DNS를 부여할 때 사용합니다.
apiVersion: v1kind: Servicemetadata: name: mysql-headlessspec: clusterIP: None # Headless selector: app: mysql ports: - port: 3306 targetPort: 3306
NodePort
각 노드의 특정 포트(30000~32767)를 열어 외부에서 노드IP:포트로 접근할 수 있게 합니다. ClusterIP를 포함하므로 내부 통신도 됩니다.
apiVersion: v1kind: Servicemetadata: name: web-nodeportspec: type: NodePort selector: app: web-frontend ports: - name: http port: 80 # 클러스터 내부 접근 포트 targetPort: 3000 # Pod 포트 nodePort: 30080 # 외부 접근 포트 (30000~32767, 생략 시 자동 할당)
# NodePort 서비스 생성 및 확인kubectl apply -f web-nodeport.yamlkubectl get svc web-nodeport# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# web-nodeport NodePort 10.96.77.200 <none> 80:30080/TCP 2m# kind 환경에서 노드 IP 확인kubectl get nodes -o wide# 접근: curl http://<NODE_IP>:30080# minikube 환경에서 바로 접근minikube service web-nodeport --url
NodePort는 포트 범위가 제한적이고, 노드 IP가 바뀌면 접근 주소도 바뀝니다. 프로덕션보다는 개발·테스트 환경에 적합합니다.
LoadBalancer
클라우드 공급자(AWS, GCP, Azure)의 로드밸런서를 프로비저닝해서 고정 외부 IP를 부여합니다. NodePort를 포함하므로 내부/외부 통신을 모두 지원합니다.
apiVersion: v1kind: Servicemetadata: name: api-lb annotations: # AWS EKS에서 NLB 사용 service.beta.kubernetes.io/aws-load-balancer-type: "nlb" # 내부 로드밸런서로 설정 (선택) service.beta.kubernetes.io/aws-load-balancer-internal: "true"spec: type: LoadBalancer selector: app: api-server ports: - name: http port: 80 targetPort: 8080 - name: https port: 443 targetPort: 8443 # 특정 IP에서만 접근 허용 (선택) loadBalancerSourceRanges: - "203.0.113.0/24"
# LoadBalancer 프로비저닝 대기 (클라우드에서 수 분 소요)kubectl get svc api-lb --watch# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# api-lb LoadBalancer 10.96.10.200 203.0.113.45 80:31000/TCP 3m# EXTERNAL-IP로 직접 접근curl http://203.0.113.45/health
로컬 환경(kind, minikube)에서는 클라우드 프로바이더가 없어 EXTERNAL-IP가 <pending> 상태로 남습니다. 로컬에서 LoadBalancer를 테스트하려면 MetalLB를 설치하거나, Ingress를 사용하는 것이 현실적입니다.
# MetalLB 설치 (kind/bare-metal 환경)kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml# IP 주소 풀 설정cat <<EOF | kubectl apply -f -apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata: name: first-pool namespace: metallb-systemspec: addresses: - 172.20.0.200-172.20.0.250---apiVersion: metallb.io/v1beta1kind: L2Advertisementmetadata: name: l2adv namespace: metallb-systemEOF
ExternalName
클러스터 내부의 Service 이름을 외부 DNS 이름으로 매핑합니다. 실제로 트래픽을 중계하지 않고, DNS CNAME 레코드를 반환합니다.
apiVersion: v1kind: Servicemetadata: name: external-db namespace: productionspec: type: ExternalName externalName: prod-db.us-east-1.rds.amazonaws.com
# 클러스터 내부에서 external-db로 접근하면 RDS 엔드포인트로 해석됨# Pod 내부에서:# mysql -h external-db.production.svc.cluster.local -u admin -p
데이터베이스나 외부 API처럼 클러스터 외부에 있는 서비스를 클러스터 내부 이름으로 추상화할 때 유용합니다. 나중에 DB를 클러스터 안으로 이전해도 애플리케이션 코드를 수정할 필요가 없습니다.
언제 어떤 타입을 선택하는가
서비스 타입 선택 기준을 정리하면 다음과 같습니다.
ClusterIP를 선택합니다 - Pod끼리만 통신하는 백엔드 서비스(API 서버, DB, 캐시)에 사용합니다. 외부 노출이 필요 없고, 보안 측면에서 가장 안전합니다.
NodePort를 선택합니다 - 클라우드 없는 로컬이나 온프레미스 환경에서 간단하게 외부 접근이 필요할 때 사용합니다. 포트가 고정적이고 노드 수가 적은 환경에 적합합니다.
LoadBalancer를 선택합니다 - 클라우드 환경에서 단일 서비스를 외부에 안정적으로 노출할 때 사용합니다. 단, 서비스마다 로드밸런서 하나씩 과금되므로, 여러 서비스를 노출해야 한다면 Ingress가 경제적입니다.
ExternalName을 선택합니다 - 클러스터 외부 엔드포인트(RDS, 외부 API)를 클러스터 내부 이름으로 추상화할 때 사용합니다.
# 서비스 상태 한눈에 보기kubectl get svc -Akubectl describe svc <service-name># Endpoints 확인 (셀렉터가 매칭되는 Pod IP 목록)kubectl get endpoints <service-name># 특정 서비스로 트래픽이 실제로 가는지 확인kubectl run debug --image=curlimages/curl --rm -it --restart=Never -- \ curl http://api-service.default.svc.cluster.local/health