Pod IP는 재시작될 때마다 바뀌는데, 다른 서비스는 어떻게 안정적으로 Pod에 접근할 수 있을까요?

Kubernetes의 Service는 변하지 않는 엔드포인트를 제공해서 이 문제를 해결합니다. 하지만 "가상 IP"라는 것이 실제로 어떻게 트래픽을 Pod까지 전달하는지, ClusterIP/NodePort/LoadBalancer가 각각 어떤 계층에서 동작하는지를 알아야 네트워크 문제를 제대로 디버깅할 수 있습니다.

Service의 기본 동작

Service는 레이블 셀렉터 로 대상 Pod을 선택하고, 해당 Pod들의 IP를 Endpoints 오브젝트로 관리합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app    # 이 레이블을 가진 Pod들이 대상
  ports:
    - port: 80          # Service 포트
      targetPort: 8080  # Pod의 컨테이너 포트
  type: ClusterIP       # 기본값
SHELL
# Endpoints 확인 — Service가 트래픽을 보내는 Pod IP 목록
kubectl get endpoints my-service
# NAME         ENDPOINTS                                   AGE
# my-service   10.244.1.5:8080,10.244.2.3:8080             5m

Pod이 추가되거나 삭제되면 Endpoints가 자동으로 업데이트됩니다. readinessProbe가 실패한 Pod은 Endpoints에서 제외됩니다.

ClusterIP — 클러스터 내부 통신

가장 기본적인 Service 타입입니다. 클러스터 내부에서만 접근 가능한 가상 IP를 할당합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP  # 기본값이라 생략 가능
  selector:
    app: backend
  ports:
    - port: 80
      targetPort: 3000

클러스터 내부의 다른 Pod에서 backend-service:80이나 backend-service.default.svc.cluster.local:80으로 접근할 수 있습니다.

NodePort — 노드 IP로 외부 접근

각 노드에 고정 포트를 열어서 외부에서 접근할 수 있게 합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 31000  # 30000-32767 범위, 생략 시 자동 할당

<노드IP>:31000으로 접근하면 Service를 거쳐 Pod에 도달합니다. 어떤 노드로 접근하든 동일하게 동작합니다 (해당 노드에 Pod이 없어도).

PLAINTEXT
외부 클라이언트 → 노드IP:31000 → kube-proxy → Pod:8080

LoadBalancer — 클라우드 로드밸런서 연동

클라우드 환경에서 외부 로드밸런서를 자동 생성합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: web-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"  # AWS NLB 사용
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
SHELL
# 외부 IP 확인
kubectl get svc web-service
# NAME          TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)
# web-service   LoadBalancer   10.96.45.12    a1b2c3.elb...    80:31234/TCP

LoadBalancer는 내부적으로 NodePort를 포함합니다. 구조는 다음과 같습니다.

PLAINTEXT
클라이언트 → 클라우드 LB → 노드IP:NodePort → kube-proxy → Pod

Service 타입 비교

타입접근 범위사용 사례
ClusterIP클러스터 내부만마이크로서비스 간 통신
NodePort노드 IP + 포트개발/테스트 환경
LoadBalancer외부 LB IP프로덕션 외부 서비스
ExternalNameDNS CNAME외부 서비스 추상화

kube-proxy — 실제 트래픽 라우팅의 주역

Service의 가상 IP는 실제로 어떤 네트워크 인터페이스에도 바인딩되지 않습니다. kube-proxy가 각 노드에서 트래픽 규칙을 관리하여 가상 IP로 향하는 트래픽을 실제 Pod IP로 전달합니다.

iptables 모드 (기본)

SHELL
# iptables 규칙 확인
sudo iptables -t nat -L KUBE-SERVICES -n

# Service IP(10.96.45.12)로 향하는 트래픽을
# Pod IP(10.244.1.5, 10.244.2.3) 중 하나로 DNAT

iptables 모드는 커널 레벨에서 동작해서 빠르지만, Service와 Endpoint가 많아지면 규칙 수가 증가하여 성능이 저하될 수 있습니다.

IPVS 모드

SHELL
# IPVS 모드 활성화 (kube-proxy 설정)
# --proxy-mode=ipvs

# IPVS 가상 서버 확인
sudo ipvsadm -Ln

IPVS는 해시 테이블 기반이라 Service 수가 수천 개로 늘어나도 성능이 안정적입니다. 추가로 다양한 로드밸런싱 알고리즘을 지원합니다.

알고리즘설명
rr (Round Robin)순서대로 분배
lc (Least Connection)연결 수가 적은 Pod에 분배
sh (Source Hash)같은 클라이언트는 같은 Pod에 분배

Headless Service

가상 IP 없이 DNS로 직접 Pod IP를 반환하는 Service입니다. StatefulSet과 함께 사용하여 각 Pod에 개별적으로 접근할 때 사용합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None  # Headless Service
  selector:
    app: database
  ports:
    - port: 5432
SHELL
# DNS 조회 시 Pod IP들이 직접 반환됨
nslookup db-headless.default.svc.cluster.local
# Address: 10.244.1.5
# Address: 10.244.2.3

# StatefulSet과 함께 사용 시 개별 Pod DNS
# db-0.db-headless.default.svc.cluster.local
# db-1.db-headless.default.svc.cluster.local

ExternalName Service

클러스터 외부 서비스를 내부 DNS 이름으로 추상화합니다.

YAML
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.production.example.com

Pod에서 external-db로 접근하면 db.production.example.com으로 CNAME 리다이렉트됩니다. 나중에 외부 DB를 클러스터 내부로 마이그레이션할 때 Service만 수정하면 됩니다.

sessionAffinity

같은 클라이언트의 요청을 같은 Pod에 보내고 싶을 때 사용합니다.

YAML
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800  # 3시간

실무에서 자주 만나는 문제

  • **Service에 Endpoints가 없음 **: 레이블 셀렉터가 Pod과 일치하지 않거나, readinessProbe 실패
  • **ClusterIP로 접근 불가 **: kube-proxy가 정상 동작하는지, Pod 네트워크가 정상인지 확인
  • LoadBalancer EXTERNAL-IP가 Pending: 클라우드 컨트롤러가 설치되지 않았거나 권한 부족
SHELL
# 디버깅 순서
kubectl get svc my-service                    # Service 확인
kubectl get endpoints my-service              # Endpoints 확인
kubectl get pods -l app=my-app -o wide        # Pod 상태/IP 확인
kubectl describe svc my-service               # 이벤트 확인

정리

Service는 "변하지 않는 엔드포인트"를 제공하는 추상화 계층입니다. kube-proxy가 iptables 또는 IPVS 규칙으로 가상 IP를 실제 Pod IP에 매핑하고, Service 타입에 따라 접근 범위가 결정됩니다. Headless Service로 직접 Pod에 접근하거나, ExternalName으로 외부 서비스를 추상화하는 패턴도 알아두면 실무에서 유용합니다.

댓글 로딩 중...