EKS – Kubernetes Service

Kubernetes Service 클러스터 내에서 실행 중인 Pod 그룹에 안정적인 네트워크 접근 방법을 제공해주는 추상화된 자원이다. 이는 Pod의 동적인 생성 및 삭제, IP 주소 변경에 유연하게 대응하여 애플리케이션의 지속적인 접근성을 보장한다. Service는 기본적으로 로드 밸런싱과 라우팅 기능을 수행하며, 클러스터 내부의 다른 Pod나 서비스들이 특정 애플리케이션에 일관된 방식으로 접근할 수 있도록 돕는다

Service의 필요성

  • Pod의 동적인 특성: Pod는 생성, 삭제, 재시작될 때 마다 새로운 내부 IP 주소가 할당된다. 사용자가 직접 Pod의 동적인 IP 주소를 추적하여 요청을 보내는 것은 불가능하다.
  • 로드 밸런싱: 여러 개의 동인할 Pod(Replica Pods)가 있을 때, Service는 이 Pod들 간에 트래픽을 효율적으로 분산하여 부하를 나누고 가용성을 높인다. 사용자가 개별 Pod의 포트를 직접 지정하는 방식으로는 부하 분산이 어렵다
  • 단일 진입점: Service는 특정 Pod 그룹에 대한 단일하고 안정적은 접근 엔드포인트(Service Ip, Service Name, Service Port 조합)를 제공하여 클라이언트 (다른 Pod, 외부 사용자 등)가 일관된 주소로 애플리케이션에 접근할 수 있게 한다

Service YAML (nginx_service.yml)

apiVersion: v1
kind: Service
metadata:
  name: my-service # Service의 이름
  namespace: hyeok # Service가 속할 네임스페이스
spec:
  # type: ClusterIP # 서비스 타입 (생략 시 기본값은 ClusterIP)
  ports:
  - port: 80 # Service가 클러스터 내부에서 리스닝하는 포트 (Service Port)
    targetPort: 80 # Service가 트래픽을 전달할 Pod 컨테이너의 포트 (Container Port)
  selector: # 이 Service가 라우팅할 Pod를 선택하는 기준
    app: my-nginx # 'app: my-nginx' 레이블을 가진 Pod를 선택
  • port번호와 targetPort번호는 다르게 설정하는 것이 좋아 보인다
ports:
- port: 80          # Service 자체의 포트 (클라이언트가 접근하는 포트)
  targetPort: 80    # Pod 컨테이너의 실제 포트 (다를 수 있음)
  # 예: port: 8080, targetPort: 80 (8080으로 접근 → 80으로 전달)

Pod YML 정의 (nginx_pod.yml)

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: hyeok
  labels: # Service의 'selector'와 일치해야 함
    app: my-nginx
spec:
  containers:
  - name: my-nginx
    image: nginx
    ports:
    - containerPort: 80 # Pod 컨테이너 내부에서 애플리케이션이 리스닝하는 포트

핵심 연결고리 – selector와 labels

Service의 spec.selector.app 필드에 지정된 값(my-nginx)과 Pod의 metadata.labels.app 필드에 지정된 값이 일치해야 한다. 이 selector를 통해 Service는 자신이 관리할 Pod 그룹을 동적으로 찾아내고 연결한다. Pod가 확장되거나 재시작되어도 동일한 레이블을 가지고 있다면 Service는 자동으로 Pod를 로드 밸런싱 대상에 포함시킨다

주요 구성 요소
  • ports.port: Service의 자체 포트이다. 클러스터 내부의 다른 자원들이 이 Service에 접근할 때 사용하는 포트이다.
  • ports.targetPort: Service가 트래픽을 전달할 대상 Pod 컨테이너의 포트이다. 이는 Pod Yaml의 containerPort와 일치하는 것이 일반적이지만, 반드시 같은 필요는 없다.

서비스 디스커버리 (Service Discovery)

Service는 Kubernetes 클러스터 내에서 서비스 디스커버리 기능을 제공한다. 즉, Pod들은 IP 주소가 아닌 Service의 이름(DNS)을 통해 다른 Service에 접근할 수 있다

  • 같은 네임스페이스 내 통신: http://<service-name>:<service-port>
    • 예: http://my-service:80
  • 다른 네임스페이스 간 통신: http://<service-name>:<namespace-name>.svc.cluster.local:<service-port>

이러한 명명 규칙 덕분에 Pod들은 IP 주소 변경에 신경 쓰지 않고 안정적으로 서로 통신할 수 있다

Service Type

Service는 외부 접근 방식에 따라 여러 type을 가질 수 있다. spec.type 필드를 통해 지정하며, 생략 시 기본값은 ClusterIP 이다

ClusterIP(기본값)
  • 특징: 클러스터 내부에서만 접근 가능한 가상 IP를 할당한다. 가장 일반적인 타입이다.
  • 사용자 접근: 외부 사용자는 ClusterIP 타입의 Service에 직접 접근할 수 없다. 클러스터 내부의 다른 Pod나 Service에서만 접근이 가능하다. 외부 접근을 위해서는 NodePort, LoadBalancer 타입 또는 Ingress와 같은 추가적인 자원이 필요하다
  • 장점: 내부 통신에 최적화되어 있으며, 비용이 발생하지 않는다
  • 단점: 외부 노출을 위해서는 추가 설정이 필요하다
  • 외부에서 접근할 수 있는 몇 가지 방법이 실제로는 있다
NodePort
  • 특징: ClusterIP 기능을 포함하며, 클러스터의 모든 Node에 동일한 포트(Node Port)를 개방하여 외부에서 접근 가능하게 한다
  • 사용자 접근: http://<Node-IP>:<Node-Port> 형태로 접근이 가능하다
  • 장점: 비교적 간단하게 외부 노출이 가능하다
  • 단점: 고정된 포트 범위(기본 30000-32767)를 사용해야 하며, Node의 IP 주소를 알아야 한다. 모든 Node에 동일한 포트가 열리므로 포트 충돌 가능성이 있고 Node 장애 시 접근이 불가능해진다
LoadBalancer
  • 특징: NodePort 기능을 포함하며, 클라우드 제공업체의 외부 로드 밸런서(예: AWS ELB, GCP Load Balancer)를 프로비저닝하여 Service에 외부 IP 주소를 할당한다
  • 사용자 접근: 외부 로드 밸런서의 IP 주소(또는 DNS 이름)를 통해 접근 가능하다
  • 장점: 가장 간단하고 안정적인 외부 노출 방법이다. 클라우드 로드 밸런서가 트래픽 분산 및 Node 장애를 자동으로 처리한다
  • 단점: 클라우드 로드 밸런서 생성에 따른 비용이 발생한다. Service 하나당 하나의 로드 밸런서가 생성되므로, Microservices 아키텍처에서 Service가 많아질수록 비용 부담이 커질 수 있다. 또한, 각 Service마다 다른 엔드포인트가 할당된다

Service 테스트 (Pod 내부에서 Service 호출)

ClusterIP 타입의 Service는 클러스터 외부에서 직접 접근할 수 없으므로, Pod 내부에서 Service를 호출하여 정상 동작 여부 테스트를 할 수 있다

Pod 및 Service 생성
kubectl apply -f nginx_pod.yml
kubectl apply -f nginx_service.yml
  • Pod와 Service 생성 순서는 상관없다. Service가 먼저 생성되어도 나중에 일치하는 레이블을 가진 Pod가 생성되면 자동으로 연결된다
Service 확인
kubectl get service -n hyeok
Pod 내 컨테이너 접속
kubectl exec -it my-nginx -n hyeok -- /bin/sh
Service 호출
  • 컨테이너 내에서 curl을 사용하여 Service 이름으로 Nginx에 요청을 보낸다
kubectl exec -it my-nginx -n hyeok -- /bin/sh

결과: Nginx의 기본 HTML 페이지가 출력되면 Service가 Pod로 트래픽을 성공적으로 라우팅한 것이다

Service와 Pod 연결 확인 방법
# Service가 어떤 Pod들을 선택하고 있는지 확인
kubectl describe service my-service -n hyeok

# Service의 엔드포인트 확인
kubectl get endpoints my-service -n hyeok

# Service의 상세 정보와 연결된 Pod들 확인
kubectl get pods -n hyeok -l app=my-nginx --show-labels
Service 삭제 명령어
# Service 삭제
kubectl delete -f nginx_service.yml

# 또는
kubectl delete service my-service -n hyeok

Ingress와의 관계

대규모 시스템에서 LoadBanalcer 타입 Service를 여러 개 사용하여 비용이 증가하는 문제를 해결하고 단일 진입점 및 고급 라우팅 기능을 제공하기 위해 Ingress 자원을 활용한다

  • Ingress는 클러스터 외부의 HTTP/S 트래픽을 클러스터 내부의 Service로 라우팅하는 규칙들의 집합이다
  • 일반적으로 하나의 Ingress Controller가 단일 클라우드 로드 밸런서와 연동되며, 이 Ingress Controller가 여러 Ingress 규칙들을 관리하여 다양한 Service로 트래픽을 분산한다
  • 이를 통해 하나의 외부 로드 밸런서로 여러 Service에 접근할 수 있게 되어 비용을 절감하고 URL 경로 기반 라우팅, 호스트 기반 라우팅, SSL/TSL 종료 등의 고급 기능을 활용할 수 있다

출처 – eks를 활용한 spring 운영서버 배포(feat. devops의 모든것)