8.2.3 서비스와 셀렉터 (Selector)

쿠버네티스 서비스(Service)가 파드 집합에 대한 안정적인 접근 지점을 제공하고, 클러스터 IP라는 고정된 가상 IP를 통해 이 파드들에게 트래픽을 전달한다는 것은 이제 잘 이해하셨을 겁니다. 그렇다면 서비스는 어떤 파드들이 자신에게 속한 백엔드 그룹인지 어떻게 알고, 이 변화무쌍한 파드들의 목록을 어떻게 최신 상태로 유지하는 걸까요? 마치 식당의 매니저가 어떤 요리사들이 오늘 출근했고, 누가 현재 주문을 받을 수 있는 상태인지 실시간으로 파악해야 하는 것과 같습니다.

이 질문에 대한 답의 핵심에 바로 쿠버네티스의 강력한 리소스 관리 메커니즘인 레이블(Label)과 셀렉터(Selector)가 있습니다. 서비스는 이 레이블과 셀렉터를 사용하여 자신이 라우팅해야 할 대상 파드 그룹을 동적으로 식별하고 연결합니다. 이 느슨한 결합(loose coupling) 방식은 쿠버네티스의 유연성과 확장성을 뒷받침하는 매우 중요한 특징입니다.

이번 절에서는 서비스가 어떻게 셀렉터를 활용하여 파드 그룹과 연결되는지, 그리고 특별한 경우 셀렉터 없이 서비스를 사용하는 방법은 무엇인지 자세히 살펴보겠습니다.

8.2.3.1 레이블 셀렉터를 이용한 파드 그룹 연결

쿠버네티스에서 레이블(Label)은 파드(Pod)를 비롯한 모든 오브젝트에 사용자가 임의로 부여할 수 있는 ‘키-값’ 쌍의 메타데이터입니다. 마치 물건에 태그를 붙이는 것처럼, 이 레이블은 오브젝트의 특징이나 용도를 나타내는 데 사용됩니다. 예를 들어, app: my-backend, tier: api, environment: production 과 같이 특정 애플리케이션, 계층, 환경 등을 나타내는 레이블을 파드에 부여할 수 있습니다.

  • 셀렉터(Selector)는 이러한 레이블을 기반으로 특정 조건을 만족하는 오브젝트들을 찾아내고 그룹화하는 메커니즘입니다. 서비스는 바로 이 셀렉터를 사용하여 자신이 트래픽을 전달해야 할 대상 파드들의 집합을 결정합니다.

서비스 명세(spec) 내의 selector 필드에 특정 레이블 조건을 정의하면, 쿠버네티스 컨트롤 플레인(특히 엔드포인트 컨트롤러 – Endpoint Controller 또는 최신 버전에서는 엔드포인트 슬라이스 컨트롤러 – EndpointSlice Controller)은 지속적으로 클러스터 내의 모든 파드들을 감시합니다. 그리고 서비스의 selector와 일치하는 레이블을 가진 파드들을 찾아내어, 이 파드들의 실제 IP 주소와 포트 정보를 담은 엔드포인트(Endpoints) 오브젝트 (또는 엔드포인트 슬라이스(EndpointSlice) 오브젝트)를 자동으로 생성하고 관리합니다.

구체적인 동작 흐름은 다음과 같습니다.

  1. 파드에 레이블 부여: 먼저, 서비스를 제공할 파드들을 생성할 때 (보통 디플로이먼트나 스테이트풀셋의 파드 템플릿을 통해) 해당 파드들을 식별할 수 있는 적절한 레이블을 부여합니다. 예를 들어, app: my-web-server 라는 레이블을 웹 서버 파드들에 부여합니다.
  2. 서비스에 셀렉터 정의: 서비스를 생성할 때, spec.selector 필드에 위에서 파드에 부여한 레이블과 일치하는 조건을 명시합니다. 예를 들어, selector: {app: my-web-server} 와 같이 정의합니다.
  3. 엔드포인트(슬라이스) 자동 관리:
    • 쿠버네티스의 엔드포인트(슬라이스) 컨트롤러는 이 서비스의 selector를 보고, 클러스터 내에서 app: my-web-server 레이블을 가진 모든 파드들을 찾아냅니다.
    • 이때, 찾아낸 파드들 중에서 실제로 요청을 받을 준비가 된(Ready 상태이고, 종료 중이 아닌) 파드들의 IP 주소와 (서비스에 정의된 targetPort에 해당하는) 컨테이너 포트 정보만을 모아 해당 서비스와 이름이 같은 엔드포인트(슬라이스) 오브젝트에 기록합니다.
    • 만약 새로운 파드가 생성되어 app: my-web-server 레이블을 가지게 되거나, 기존 파드가 삭제되거나, 또는 파드의 준비 상태가 변경되면, 엔드포인트(슬라이스) 컨트롤러는 이 변화를 즉시 감지하여 엔드포인트(슬라이스) 오브젝트를 최신 상태로 업데이트합니다.
  4. kube-proxy의 역할: 각 노드에서 실행되는 kube-proxy는 이 엔드포인트(슬라이스) 오브젝트의 변경 사항을 감시하고 있다가, 서비스의 클러스터 IP로 들어오는 트래픽을 엔드포인트(슬라이스)에 등록된 실제 파드 IP와 포트로 전달하기 위한 네트워킹 규칙(iptables, IPVS 등)을 해당 노드에 업데이트합니다.

이러한 레이블 셀렉터 기반의 동적 연결 방식은 다음과 같은 강력한 이점을 제공합니다.

  • 느슨한 결합 (Loose Coupling): 서비스는 특정 파드의 IP 주소나 이름에 직접 의존하지 않고, 오직 레이블을 통해 논리적으로 연결됩니다. 따라서 파드가 재생성되어 IP 주소가 변경되거나, 파드의 개수가 스케일 아웃/인 되어도 서비스는 자동으로 새로운 파드들을 감지하고 트래픽을 전달할 수 있습니다.
  • 자동화된 서비스 디스커버리: 개발자나 운영자는 어떤 파드가 어떤 서비스에 속하는지를 레이블을 통해 명확하게 정의하고 관리할 수 있으며, 쿠버네티스는 이를 기반으로 서비스와 파드 간의 연결을 자동으로 유지합니다.
  • 롤링 업데이트 및 블루/그린 배포 지원: 디플로이먼트를 사용하여 애플리케이션을 업데이트할 때, 새로운 버전의 파드는 새로운 레이블을 가질 수도 있고 (이 경우 서비스 셀렉터도 변경 필요), 또는 기존 레이블을 그대로 유지하면서 점진적으로 교체될 수도 있습니다. 서비스는 이러한 변화에 맞춰 엔드포인트 목록을 자동으로 업데이트하므로, 무중단 업데이트가 가능해집니다.

예를 들어, 다음과 같은 서비스와 디플로이먼트 YAML이 있다고 가정해 봅시다.

서비스 YAML (my-service.yaml):

클립보드에 복사

디플로이먼트 YAML (my-deployment.yaml):

클립보드에 복사

이 경우, my-app-service는 app: my-app 레이블을 가진 모든 파드들을 자신의 백엔드로 인식하고, 해당 파드들의 8080번 포트로 트래픽을 전달하게 됩니다. my-app-deployment에 의해 생성되는 3개의 파드들은 모두 app: my-app 레이블을 가지므로, 이 서비스의 대상이 됩니다.

이처럼 레이블과 셀렉터는 쿠버네티스 서비스가 동적이고 확장 가능한 방식으로 파드 그룹과 상호작용할 수 있도록 하는 핵심적인 접착제 역할을 합니다.

8.2.3.2 셀렉터 없는 서비스 (직접 Endpoints 관리)

대부분의 경우, 쿠버네티스 서비스는 레이블 셀렉터를 사용하여 클러스터 내의 파드들을 동적으로 찾아내고 엔드포인트(Endpoints) 오브젝트를 자동으로 관리합니다. 하지만 특별한 상황에서는 서비스에 selector 필드를 정의하지 않고, 대신 엔드포인트 오브젝트를 수동으로 직접 생성하고 관리하는 방식을 사용할 수도 있습니다. 이를 “셀렉터 없는 서비스(Service without selectors)”라고 부릅니다.

셀렉터 없는 서비스는 다음과 같은 경우에 유용하게 사용될 수 있습니다.

  1. 클러스터 외부의 서비스를 쿠버네티스 내부 서비스처럼 참조:만약 쿠버네티스 클러스터 외부에서 실행 중인 기존의 레거시 서비스나 데이터베이스가 있고, 클러스터 내부의 애플리케이션들이 이 외부 서비스를 마치 클러스터 내부의 다른 서비스처럼 동일한 서비스 디스커버리 메커니즘(예: DNS 이름)을 통해 접근하게 하고 싶을 때 사용할 수 있습니다.이 경우, 먼저 서비스 YAML 파일에서 selector 필드를 생략하거나 주석 처리합니다. 그리고 이 서비스와 동일한 이름 및 네임스페이스를 가진 엔드포인트 오브젝트를 수동으로 생성하고, 이 엔드포인트 오브젝트의 subsets 필드에 실제 외부 서비스의 IP 주소와 포트 정보를 직접 명시합니다.
    클립보드에 복사
    이렇게 구성하면, 클러스터 내부의 파드가 external-db라는 서비스 이름으로 DNS 조회를 할 때, 쿠버네티스 DNS는 이 서비스의 클러스터 IP를 반환하고, 해당 클러스터 IP로 향하는 트래픽은 kube-proxy에 의해 수동으로 정의된 엔드포인트 (192.168.100.10:5432)로 전달됩니다. (주의: ExternalName 타입의 서비스와는 목적이 유사하지만 동작 방식이 다릅니다. ExternalName은 CNAME 리다이렉션을 수행하는 반면, 셀렉터 없는 서비스와 수동 엔드포인트는 여전히 클러스터 IP를 통해 프록시됩니다.)
  1. 다른 네임스페이스에 있는 서비스를 현재 네임스페이스에서 다른 이름으로 참조:어떤 경우에는 특정 네임스페이스에 있는 서비스를 다른 네임스페이스에서 마치 현재 네임스페이스에 있는 것처럼 다른 이름으로 참조하고 싶을 수 있습니다. 이때도 셀렉터 없는 서비스를 생성하고, 해당 서비스에 대한 엔드포인트를 수동으로 구성하여 원래 서비스의 클러스터 IP를 가리키도록 할 수 있습니다.
  2. 매우 특수한 서비스 라우팅 또는 마이그레이션 시나리오:표준적인 셀렉터 기반 방식으로는 구현하기 어려운 복잡한 서비스 라우팅 규칙을 적용하거나, 서비스를 점진적으로 다른 백엔드로 마이그레이션하는 과정에서 일시적으로 엔드포인트를 수동 제어해야 하는 매우 특수한 상황에서 사용될 수 있습니다.

고려 사항:

  • 셀렉터 없는 서비스를 사용하고 엔드포인트를 수동으로 관리하는 것은 쿠버네티스의 자동화된 동적 관리 이점을 활용하지 못하게 됩니다. 외부 서비스의 IP 주소나 포트가 변경되면 엔드포인트 오브젝트를 수동으로 업데이트해야 하는 부담이 있습니다.
  • 엔드포인트 오브젝트를 직접 관리하는 것은 오류가 발생하기 쉽고, 관리 오버헤드가 크므로 정말 필요한 경우에만 신중하게 사용해야 합니다.
  • 대부분의 경우, 클러스터 외부 서비스를 참조할 때는 ExternalName 타입의 서비스를 사용하는 것이 더 간결하고 권장되는 방법일 수 있습니다.

결론적으로, 서비스와 셀렉터는 쿠버네티스에서 파드 그룹에 대한 안정적인 접근성을 제공하는 핵심적인 메커니즘입니다. 레이블 셀렉터를 통한 동적 엔드포인트 관리는 대부분의 사용 사례에 적합하며 쿠버네티스의 강력함을 잘 보여줍니다. 셀렉터 없는 서비스와 수동 엔드포인트 관리는 특수한 상황에서 유용할 수 있지만, 그에 따른 관리 부담을 충분히 인지하고 사용해야 합니다. 이들의 동작 원리를 정확히 이해하는 것은 쿠버네티스 기반의 애플리케이션을 설계하고 운영하는 데 있어 매우 중요합니다.