쿠버네티스에서 외부 트래픽을 앱으로 들여보내려고 검색하면 Service, NodePort, LoadBalancer, Ingress가 한꺼번에 쏟아집니다. 다 비슷해 보여서 "그래서 뭘 써야 하지?"에서 막힙니다. 사실 이들은 경쟁 관계가 아니라 층층이 쌓이는 단계입니다. Service가 토대이고, 그 위에 외부 노출 방식이 얹히는 구조라고 보면 정리가 됩니다.
먼저 Service의 역할
Pod는 죽고 살아나며 IP가 계속 바뀝니다. Service는 그 변하는 Pod 묶음 앞에 고정된 가상 주소를 하나 세워 줍니다. 클라이언트는 Service만 바라보고, Service가 살아있는 Pod로 부하를 분산합니다. Service의 type이 바로 "어디까지 노출할까"를 결정합니다.
| type | 노출 범위 | 쓰임 |
|---|---|---|
| ClusterIP | 클러스터 내부만 | 서비스 간 내부 통신(기본값) |
| NodePort | 각 노드의 고정 포트 | 개발·테스트용 외부 접근 |
| LoadBalancer | 클라우드 외부 IP | 단일 서비스 인터넷 노출 |
NodePort와 LoadBalancer
NodePort는 모든 노드에 30000~32767 범위의 포트를 열어 노드IP:포트로 접근하게 합니다. 포트 번호가 지저분하고 노드 IP를 직접 알아야 해서 실서비스보다는 테스트용입니다.
LoadBalancer는 클라우드(AWS·GCP 등)에 진짜 로드밸런서를 하나 띄워 외부 IP를 받아 줍니다. 깔끔하지만 서비스 하나당 로드밸런서 하나라 서비스가 늘면 비용과 IP가 그만큼 늘어납니다.
kubectl expose deploy web --type=LoadBalancer --port=80
kubectl get svc web # EXTERNAL-IP 확인
그래서 Ingress가 필요하다
서비스가 10개라고 LoadBalancer를 10개 만들 순 없습니다. Ingress는 하나의 진입점에서 경로·도메인 기준으로 트래픽을 여러 Service로 갈라 주는 L7 라우터입니다. shop.com/api는 api로, shop.com/은 web으로 보내는 식이죠. TLS 종료(HTTPS)도 여기서 한 번에 처리합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
spec:
rules:
- host: shop.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service: { name: api, port: { number: 80 } }
- path: /
pathType: Prefix
backend:
service: { name: web, port: { number: 80 } }
주의할 점은 Ingress 리소스만 만든다고 동작하지 않는다는 것입니다. 실제로 트래픽을 처리하는 Ingress Controller(nginx-ingress 등)를 클러스터에 설치해야 규칙이 살아납니다.
언제 무엇을 쓰나
내부 통신만 필요하면 ClusterIP로 끝입니다. 외부에 노출할 서비스가 < 2개로 단순하면 LoadBalancer 하나로 충분합니다. HTTP 서비스가 여럿이고 도메인·경로로 라우팅하거나 HTTPS를 한곳에서 관리하고 싶으면 Ingress가 정답입니다. 실제 운영 클러스터는 보통 "여러 ClusterIP Service + 그 앞 Ingress 하나" 조합으로 굴러갑니다.
요점 정리
- Service는 변하는 Pod 앞의 고정 주소이고,
type이 노출 범위를 정한다. - ClusterIP=내부, NodePort=테스트용 외부, LoadBalancer=서비스당 외부 IP 하나.
- Ingress는 단일 진입점에서 경로·도메인으로 여러 Service에 라우팅하는 L7 계층이다.
- Ingress는 Controller 설치가 전제다 — 리소스만 만들면 동작하지 않는다.
ClusterIP·NodePort·LoadBalancer·Ingress를 직접 띄워 트래픽이 어떻게 흐르는지 확인하는 실습은 쿠버네티스 트랙에서 할 수 있습니다 — 회원가입 없이 무료로.