본문 바로가기
Cloud Native/Kubernetes

1. Service - Headless, Endpoint, ExternalName

by jys275 2026. 1. 27.

이전까지 서비스의 겉모습을 배웠다면, 이번에는 서비스의 속살(Headless, Endpoint, ExternalName)을 파헤치는 시간이다. 인프라를 설계할 때 IP는 언제든 바뀔 수 있는 가변적인 존재다.

 

엔지니어가 되려면 IP가 아닌 '이름(DNS)'으로 통신하는 법을 완벽히 마스터해야 한다.

 


 

1. [복습] 서비스의 기본 3요소와 한계

쿠버네티스 클러스터는 내부망(예: 192.168.x.x) 위에 파드 전용 IP(20.x.x.x)와 서비스 전용 IP(10.x.x.x) 대역을 별도로 가진다.

  • ClusterIP: 클러스터 내부에서만 접근 가능하다. 내부망의 다른 서버들은 직접 호출할 수 없다.
  • NodePort: 클러스터 노드들에 3만 번대 포트를 열어주어 내부망 사용자가 접근하게 해준다.
    • 내부망에 IP를 할당받은 여러 기기들이 있다고 해도 서비스의 IP를 호출할 수 없기 때문에, NodePort를 사용한다.
  • LoadBalancer: 클라우드 프로바이더(AWS, GCP 등)의 로드밸런서를 연결해 외부망 사용자에게 서비스를 노출한다.
    • 동일하게 NodePort가 생성이되어 포트가 열리고, 외부망 사용자들이 1.23.58.1 IP로 접근이 가능하게 되는 원리이다.

핵심 문제: 사용자는 서비스 IP를 확인하고 접근하면 되지만, 파드 간의 통신은 자원이 동시에 배포되거나 파드가 죽고 재생성되면서 IP가 계속 바뀌기 때문에 IP를 미리 알고 코딩하는 것이 불가능하다.


즉, 인프라의 핵심은 '변하는 것(IP)들 사이에서 변하지 않는 이름(DNS)을 찾는 것'이다.

 

2. 서비스 심화: 파드 관점의 통신 및 외부 연결 전략

1. 관점의 전환: 사용자 vs 파드

  • 사용자 관점: 위에서 알아봤던, 서비스 IP(ClusterIP, NodePort, LoadBalancer)를 확인하고 이를 통해 파드에 접근하는 방식이다.
  • 파드 관점: 파드끼리 서로를 호출하거나, 외부 서비스에 안정적으로 연결하는 '내부적 소통'이 핵심이다.

2. IP 기반 통신의 한계 (왜 DNS가 필요한가?)

  • 동적 할당: 파드와 서비스의 IP는 생성 시점에 동적으로 할당되므로, 다른 파드가 이를 미리 알고 설정에 넣는 것이 불가능하다.
  • 가변성: 파드에 장애가 발생하여 오토 힐링으로 재생성되면 IP가 변경된다.
  • 결론: 파드에 직접 IP를 기입하여 통신하는 방식은 유지보수가 불가능한 구조다.

3. 통신 이슈 해결책

  • 내부 통신 (Pod to Pod/Service): DNS와 헤드리스(Headless) 서비스를 통해 IP가 아닌 '이름'으로 서로를 찾는다.
  • 외부 통신 (Pod to External): 파드가 외부 사이트에서 데이터를 가져올 때 주소가 바뀌더라도, 파드를 수정하거나 재배포할 필요가 없도록 익스터널 네임(ExternalName) 서비스를 활용한다.

즉, 서비스 통신의 핵심은 '이름(DNS)'을 얼마나 잘 활용하느냐에 달려 있다. 쿠버네티스 내부 DNS가 어떻게 파드와 서비스를 연결하고, 외부망까지 확장되는지에 대한 핵심 로직을 알아보자.

 

 

3. 쿠버네티스 DNS 및 서비스 심화 로직 정리

 

1. DNS 서버의 역할과 질의 메커니즘

  • 내부 질의: 쿠버네티스 클러스터 안에는 자체 DNS 서버가 존재하여 서비스의 도메인 이름과 IP를 저장하고 관리한다. 파드가 서비스 이름으로 질의를 던지면 DNS가 해당 IP를 즉시 응답한다.
  • 계층적 질의: 만약 내부 DNS에 찾는 이름이 없다면, 상위 DNS(내부망 DNS나 공용 DNS)로 질의를 넘긴다. 이를 통해 파드는 IP 주소를 몰라도 구글 같은 외부 사이트의 IP까지 알아내어 연결할 수 있다.

2. Headless 서비스: 파드 개별 식별

  • 연결 방식: 특정 파드(pod1, pod2 등 선택해서)에 직접 접근하고 싶을 때 헤드리스 서비스를 사용한다.
  • DNS 등록: headless 서비스를 연결하면 DNS 서버에 '파드 이름 + 서비스 이름'이 결합된 도메인 이름이 등록된다.
  • 장점: 파드 입장에서 상대방의 IP 주소를 매번 확인할 필요 없이, 미리 정의된 도메인 이름만으로 특정 파드와 직접 통신할 수 있게 된다.

3. ExternalName 서비스: 운영 유연성 확보

  • 동작 원리: 서비스 내부에 특정 외부 도메인 주소(github)를 지정하여 생성한다.
  • 장점: 파드는 외부 주소를 직접 알 필요 없이 이 서비스를 통해서 데이터를 가져온다.
  • 운영 효율: 만약 데이터를 가져오는 대상을 깃허브에서 다른 곳으로 변경해야 할 때, 파드 소스 코드를 수정하거나 재배포할 필요 없이 서비스의 ExternalName 값만 변경하면 즉시 반영된다.

 


 

 

0. DNS와 FQDN: 통신의 표준 규약

쿠버네티스에는 자체 DNS 서버가 있어 서비스와 파드의 이름을 관리한다.

  • FQDN (Fully Qualified Domain Name): 전체 도메인 이름을 의미하며 규칙이 정해져 있다.
    • 서비스: 서비스이름.네임스페이스.svc.cluster.local
    • 파드: IP주소(대시 변환).네임스페이스.pod.cluster.local (하지만 IP가 포함되어 실효성이 낮고 안쓴다고 보면 된다).
  • 작동 원리: 파드가 서비스 이름을 질의하면 DNS가 IP를 알려준다. 만약 내부 DNS에 없다면 상위 DNS(외부 DNS)를 찾아가 결국 구글 같은 외부 IP까지 알아낸다.
    • 즉, 서비스의 이름만 알아도 해당 서비스에 접근할 수 있게 되는 것이다.

1. Headless Service: 특정 파드를 저격하는 법

때로는 서비스의 대표 IP가 아니라, 특정 파드 하나하나, 또는 서로서로 직접 연결해야 할 때가 있다.

  • 설정 방법: 서비스 스펙의 clusterIP 속성에 None을 입력한다.
  • 특징: 서비스 자체 IP는 생성되지 않는다. DNS에 서비스 이름을 질의하면 연결된 모든 파드의 IP 목록을 반환한다.
  • 파드 개별 접근을 위한 필수 설정:
    1. 파드 스펙에 hostname을 정의한다.
    2. subdomain에 헤드리스 서비스의 이름을 넣는다.
  • 결과: 호스트이름.서비스이름.네임스페이스.svc.cluster.local과 같이 
    • pod4.headless1.default.svc.cluster.local이라는 고유한 도메인이 파드마다 생겨나며,
      • 서비스를 찾는데 파드(.pod) 주소를 쓰면 안되겠지?
    • IP 없이도 특정 파드와 직접 통신이 가능해진다.

2. Endpoint: 라벨 뒤에 숨겨진 실체

우리는 라벨 셀렉터로 서비스와 파드를 묶지만, 쿠버네티스 내부에서는 Endpoint(엔드포인트)라는 별도의 오브젝트가 이 연결 고리를 관리한다.

  • 동작 방식: 서비스 이름과 동일한 이름의 엔드포인트가 자동으로 생성되며, 그 안에 파드의 실제 IP와 포트 정보가 담긴다.
  • 수동 제어: 서비스에 셀렉터를 넣지 않고 직접 엔드포인트를 만들어 연결할 수도 있다. 이를 통해 내부 파드가 아닌 외부 IP 주소를 서비스와 연결하는 것도 가능하다.

3. ExternalName: 외부 연결의 안정성 확보

파드가 외부 사이트(예: GitHub)에 접근할 때, 외부 사이트도 주소가 바뀌면 처음부터 파드를 그냥 수정해서 재배포해야 할까?

  • 정의: 외부 도메인 주소를 서비스 이름으로 매핑하는 CNAME 역할을 한다.
    • DNS cache가 내부와 외부 DNS를 찾아서 IP를 알아낸다.
  • 장점: 파드는 내부 서비스 이름만 가리키고 있으면 된다. 만약 외부 접속 경로가 변경되더라도 파드의 수정 없이 서비스의 externalName 속성만 업데이트하면 즉시 반영된다.

 

 

💡꼬리 질문

Q: Headless 서비스를 사용할 때, 파드의 hostname과 subdomain을 설정하지 않으면 어떤 일이 벌어질까? 개별 파드 도메인 생성이 실패해도 서비스 이름만으로 모든 파드 IP를 받아올 수는 있을까?

 

 

결론: 서비스 이름으로 전체 IP 목록은 받아올 수 있지만, 특정 파드 하나를 지칭하는 도메인은 생성되지 않는다.

  1. 동작 원리: 서비스의 clusterIP: None 설정만으로도 DNS는 해당 서비스에 묶인 모든 파드 IP를 반환한다(A 레코드 목록).
  2. 한계: 하지만 hostname과 subdomain이 없으면 각 파드에 대한 개별 레코드(pod-name.service-name...)가 DNS에 등록되지 않는다.
  3. 실무적 영향: 이 경우 로드밸런싱이 아닌 '직접 특정 인스턴스를 찾아가야 하는' 분산 데이터베이스(Master-Slave 구조) 운영 시 문제가 발생할 수 있다.

 


 

 

이번 실습에서는 변하는 IP들 사이에서 변하지 않는 이름(DNS)을 활용해 파드들이 서로를 찾고, 외부와 소통하는 실제 과정을 터미널에서 확인해 보겠다.

 

1. Headless Service 실습: "파드 한 놈만 골라잡기"

서비스 IP를 거치지 않고 특정 파드와 1:1로 직접 통신해야 할 때 사용하는 방식이다.

apiVersion: v1
kind: Service
metadata:
  name: headless1
spec:
  selector:
    svc: headless
  ports:
    - port: 80
      targetPort: 8080    
  clusterIP: None
  • 설정: 서비스의 clusterIP 속성을 None으로 설정하여 IP를 생성하지 않는다.
apiVersion: v1
kind: Pod
metadata:
  name: pod4
  labels:
    svc: headless
spec:
  hostname: pod-a
  subdomain: headless1
  containers:
  - name: container
    image: kubetm/app
---
apiVersion: v1
kind: Pod
metadata:
  name: pod5
  labels:
    svc: headless
spec:
  hostname: pod-b
  subdomain: headless1
  containers:
  - name: container
    image: kubetm/app
  • 파드 식별: 파드를 생성할 때 hostname과 subdomain(서비스 이름과 일치)을 명시적으로 부여한다.
  • 결과: nslookup headless1을 하면 연결된 모든 파드들의 IP 목록이 리턴된다. 또한, pod-a.headless1과 같이 '파드이름.서비스이름' 형태의 고유 도메인으로 특정 파드에 직접 curl 호출이 가능하다.

 

2. Endpoint 실습: "라벨 없이 수동으로 연결하기"

서비스와 파드 사이의 실제 연결 고리인 Endpoint 오브젝트를 직접 제어해 본다.

apiVersion: v1
kind: Service
metadata:
  name: endpoint1
spec:
  selector:
    svc: endpoint
  ports:
  - port: 8080

인프런_대세는 쿠버네티스

  • 자동 생성 확인: 일반적인 서비스(endpoint1)를 만들면 쿠버네티스가 서비스 이름과 똑같은 이름의 엔드포인트를 자동으로 생성해 파드 정보를 관리한다. 이 자동 생성 방식은 클러스터 내 파드가 연결 대상이며, 라벨 일치 시에 자동 관리되는 것이다.
apiVersion: v1
kind: Pod
metadata:
  name: pod9
spec:
  containers:
  - name: container
    image: kubetm/app
apiVersion: v1
kind: Service
metadata:
  name: endpoint2
spec:
  ports:
  - port: 8080
apiVersion: v1
kind: Endpoints
metadata:
  name: endpoint2
subsets:
 - addresses:
   - ip: <pod-ip>
   ports:
   - port: 8080
  • 수동 연결 (내부): 서비스와 파드 모두 라벨/셀렉터 없이 생성한 후, 서비스 이름과 동일한 이름의 Endpoints 오브젝트를 직접 만들어 파드 IP를 넣어주면 정상적으로 통신이 연결된다.
apiVersion: v1
kind: Endpoints
metadata:
  name: endpoint3
subsets:
 - addresses:
   - ip: 185.199.110.133
   ports:
   - port: 443
  • 수동 연결 (외부): 깃허브(GitHub)와 같은 외부 서버의 IP를 엔드포인트에 등록하면, 파드 입장에서는 내부 서비스 이름만 호출해도 외부 자원에 접근할 수 있게 된다.

 

3. ExternalName 실습: "외부 도메인 안전하게 매핑하기"

외부 도메인 주소가 변경될 가능성이 클 때 매우 유용한 전략이다.

apiVersion: v1
kind: Service
metadata:
 name: externalname1
spec:
 type: ExternalName
 externalName: raw.githubusercontent.com
  • 설정: 서비스 타입을 ExternalName으로 지정하고, 외부 도메인(예: raw.githubusercontent.com)을 등록한다.
  • 운영 장점: 파드는 클러스터 내부의 서비스 이름(externalname1)만 바라보고 개발하면 된다. 나중에 데이터 소스가 깃허브에서 구글로 바뀌더라도 파드를 수정할 필요 없이 서비스의 externalName 값만 변경하면 즉시 반영된다.

 

 

💡 꼬리 질문

Q: 실습에서 엔드포인트(Endpoint)에 수동으로 외부 IP를 넣는 방식과 ExternalName 서비스를 사용하는 방식 중, 대상 서버의 도메인 이름이 존재할 때 실무적으로 더 권장되는 방식은 무엇이며 그 이유는 무엇일까?

 

 

결론: ExternalName 서비스를 사용하는 방식이 압도적으로 유리하다.

  1. 가변성 대응: 외부 서버의 IP는 예고 없이 바뀔 수 있다. 엔드포인트 방식은 IP가 바뀔 때마다 엔지니어가 수동으로 수정해야 하지만, ExternalName은 도메인 이름을 바라보므로 대상 서버의 IP가 바뀌어도 아무런 조치가 필요 없다.
  2. 보안: 실습 스크립트에서도 언급되었듯, 깃허브 같은 서비스는 보안상의 이유로 Host 헤더가 일치하지 않으면 접근을 차단하기도 한다. ExternalName은 CNAME 레코드를 통해 도메인 기반의 자연스러운 연결을 지원하므로 이러한 보안 정책 대응에 더 적합하다.

 

 

위 학습용 정리 내용 및 사진 자료는 "인프런_대세는 쿠버네티스(https://inf.run/Lv5RV)" 강의를 소스로 작성하였습니다