apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web # 이 라벨을 가진 파드로 보낸다
ports:
- port: 80 # 서비스 포트
targetPort: 8080 # 파드 포트
파드 IP 는 계속 바뀐다. 서비스는 '바뀌지 않는 주소' 를 준다 파드가 죽고 새로 떠도 라벨만 맞으면 자동으로 대상에 들어간다
두 객체가 함께 움직인다
Service 가상 IP 와 포트
EndpointSlice 실제로 지금 Ready 인 파드 IP 목록
트래픽이 안 가면 엔드포인트부터
kubectl get endpointslices -l kubernetes.io/service-name=web
비어 있으면
- selector 라벨이 파드 라벨과 안 맞는다
- 파드가 Ready 가 아니다 (readiness 실패)
서비스 설정 문제가 아니라 이 둘 중 하나인 경우가 압도적으로 많다
kubectl get pods --show-labels kubectl get svc web -o jsonpath='{.spec.selector}'
- 눈으로 대조하는 것이 가장 빠르다
타입 네 가지
-
ClusterIP — 클러스터 내부 전용 (기본값)
-
NodePort — 모든 노드의 특정 포트를 연다 (30000-32767)
- ClusterIP 를 포함한다
-
LoadBalancer — 클라우드 로드밸런서를 만든다
- NodePort 를 포함한다 (세 겹으로 쌓인 구조다)
-
ExternalName — 외부 DNS 이름으로 CNAME 을 준다 (프록시하지 않는다)
-
헤드리스 (clusterIP: None)
- 가상 IP 없이 DNS 가 파드 IP 들을 직접 돌려준다
- StatefulSet 개별 접근 · 클라이언트 측 로드밸런싱에 쓴다
트래픽 정책
spec:
externalTrafficPolicy: Local
Local 의 효과
얻는 것 클라이언트 원본 IP 가 보존된다 (SNAT 를 안 한다)
불필요한 노드 간 홉이 사라진다
잃는 것 그 노드에 파드가 없으면 트래픽이 버려진다
→ 파드가 고르게 퍼져 있어야 한다
원본 IP 가 필요한 경우(레이트 리밋·접근 제한·로깅)에 쓴다
포트 이름을 붙인다
ports:
- name: http
port: 80
targetPort: http # 컨테이너 포트 이름을 참조할 수 있다
targetPort 에 숫자 대신 이름을 쓰면 컨테이너 포트가 바뀌어도 서비스를 안 고쳐도 된다
containers:
- ports: [{ name: http, containerPort: 8080 }]
여러 포트를 노출할 때는 name 이 필수다 그리고 Gateway API·모니터링이 이 이름을 참조한다
서비스 없이 파드를 부를 수도 있다
헤드리스 서비스 + 파드 이름
- web-0.web.default.svc.cluster.local
또는 파드 IP 를 직접 (권장하지 않는다 — 바뀐다)
그리고 셀렉터 없는 서비스도 만들 수 있다
- EndpointSlice 를 손으로 만들어 클러스터 밖 주소를 가리킨다
- 외부 DB 를 클러스터 안 이름으로 부르고 싶을 때 쓴다
면접 함정
- ❌ "서비스가 로드밸런싱을 한다" → kube-proxy의 커널 규칙이 한다. L4다.
- ❌ "LoadBalancer가 ClusterIP를 대체한다" → 포함한다. 세 타입이 겹겹이 쌓인 구조다.