Kubernetes

서비스 API 카테고리(Headless)

jih0ssang 2024. 2. 11. 18:52

서비스 API 카테고리

컨테이너를 외부에 공개하는 엔드포인트를 제공하는 리소스

- 서비스

   - ClusterIP

 - ExternalIP  (ClusterIP의 한 종류)

   - NodePort

   - LoadBalancer

   - 그 외 서비스 기능

   - Headless (None)

   - ExternalName

   - None-Selector

- 인그레스

 

 

Headless (None)

클라이언트가 Pod 1개가 아니라 모든 Pod 들과 통신해야 한다면요? 

만약 같은 서비스 아래에 있는 Pod A 와 Pod B 가 서로 통신해야 한다면요?
다행히도, Kubernetes 는 클라이언트에게 Pod IP 리스트를 알려주는 방법을 DNS Lookup 을 통해 제공합니다. 

  • 보통의 서비스: Service의 DNS Lookup은 IP 주소 1개 반환(라운드로빈) 
  • 헤드리스 서비스: Service의 DNS Lookup은 Service에 소속된 POD의 모든 IP 반환

헤드리스 서비스 생성하려면 두 가지 조건을 만족해야 한다.

  1. 서비스의 spec.type이 ClusterIP일 것
  2. 서비스의 spec.clusterIP가 None일 것
  3. [옵션] 서비스의 metadata.name이 스테이트풀셋의 spec.serviceName과 같을 것

세 번째 조건을 만족하지 않을 경우 DNS 라운드 로빈으로 디스커버리하는 헤드리스 서비스로만 동작하여 파드명으로 질의할 수 없다.

apiVersion: v1
kind: Service
metadata:
  name: sample-headless
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 80
    targetPort: 80
  selector:
    app: sample-app

 

위의 서비스와 조합하여 사용할 스테이트풀셋 예제는 다음과 같다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-statefulset-headless
spec:
  serviceName: sample-headless
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: nginx-container
        image: amsy810/echo-nginx:v2.0

 

spec.serviceName이 서비스의 metadata.name과 일치하게 되어 있다.

 

헤드리스 서비스로 파드명 이름 해석

서비스 이름 해석은 '서비스명.네임스페이스명.svc.cluster.lcoal'로 질의한다. 헤드리스 서비스의 경우는 FQDN에 대해 정방형으로 질의하면 ClusterIP 대신 DNS 라운드 로빈에서 여러 파드의 IP 주소가 반환되기 때문에 부하 분산 때는 클라이언트 캐시 등을 생각해야 한다. 레플리카셋 등의 리소스에도 다음과 같이 DNS 라운드 로빈에서 IP 주소가 반환되도록 할 수 있다.

# 스테이트풀셋의 서비스 디스커버리에서는 DNS 라운드 로빈으로 IP주소를 반환
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod \ --command -- dig sample-headless.default.svc.cluster.local
...(생략)...
;; QUESTION SECTION:
;sample-headless.default.svc.cluster.local.	IN	A

;; ANSWER SECTION:
sample-headless.default.svc.cluster.local.	30	IN	A	10.0.2.50
sample-headless.default.svc.cluster.local.	30	IN	A	10.0.0.35
sample-headless.default.svc.cluster.local.	30	IN	A	10.0.1.41

 

 

일반적으로 클러스터 내부 DNS에서 파드명으로 이름 해석을 할 수 없게 되어 있다.

스테이트풀셋이 헤드리스 서비스를 사용하고 서비스의 metadata.name이 스테이트풀셋의 spec.serviceName과 동일한 경우, 추가로 다음과 같이 파드 단위의 이름 해석을 할 수 있다.

 

  • 파드명.서비스명.네임스페이스명.svc.cluster.local
# 파드명으로 서비스 디스커버리
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod \ --command -- dig sample-statefulset-headless-0.sample-headless.default.svc.cluster.local
...(생략)...
;; ANSWER SECTION:
sample-statefulset-headless-0.sample-headless.default.svc.cluster.local. 30 IN A 10.0.1.41
...(생략)...

 

  • 파드명.서비스명
  • 파드명.서비스명.네임스페이스명
# 컨테이너 내부의 resolv.conf 확인
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- cat /etc/resolv.conf
nameserver 10.3.240.10
search default.svc.cluster.local svc.cluster.local cluster.local c.psu-statest-20200113.
internal google.internal
options ndots:5
pod "testpod" deleted

컨테이너 내부의 resolv.conf 등에는 search 지시자로 다음과 같은 엔트리(파라미터)가 들어있어 '파드명.서비스명'이나 '파드명.서비스명.네임스페이스명'으로 질의할 수 있다.

 

스테이트풀셋 외의 파드명으로 이름 해석

스테이트풀셋의 경우만 파드명으로 이름 해석이 가능하다고 설명했지만, 파드에 설정을 추가하여 파드명으로 이름 해석을 할 수도 있다. 파드에서 이름을 해석하려면 파드에 spec.hostname과 헤드리스 서비스명과 동일한 spec.subdomain 설정을 추가한다. 이때, spec.hostname은 파드명이 아니어도 된다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-subdomain
  labels:
    app: sample-app
spec:
  hostname: sample-hostname
  subdomain: sample-subdomain
  containers:
  - name: nginx-container
    image: amsy810/tools:v2.0
 
 ---
 apiVersion: v1
 kind: Service
 metadata:
   name: sample-subdomain
 spec:
   type: ClutserIP
   clusterIP: None
   ports: []
   selector:
     app: sample-app

 

이 설정을 하면 다음과 같이 파드 단위로 이름 해석을 할 수 있게 된다.

Hostname명.Subdomain명/서비스명.네임스페이스명.svc.cluster.local

 

디플로이먼트 등에서 이 설정을 할 경우, manifest 구조상 여러 레플리카에서 같은 hostname만 설정할 수 있다.

또한, 같은 hostname이 지정된 경우는 하나의 A 레코드만 반환되기 때문에 디플로이먼트 등에는 개별 파드명으로 이름 해석이 가능하도록 설정할 수 없다.

# 파드명으로 서비스 디스커버리
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-hostname.sample-subdomain.default.svc.cluster.local
...(생략)...
;; ANSWER SECTION:
sample-hostname.sample-subdomain.default.svc.cluster.local. 30 IN A 10.0.1.46
...(생략)...