Engineering/Kubernetes (K8S)

[Kubernetes] 쿠버네티스 서비스(Service) Deep Dive - (6) Endpoint

[앙금빵] 2022. 8. 7.

 

리마인드 - 로드밸런서 특징

외부 서버에 직접적으로 포트 번호 공개할 필요 없이 직접 접근을 하지 않아도 로드밸런서의 엔드포인트를 통해 어플리케이션에 접근할 수 있다.

 

첫째, 안정적인 서비스 끝점(엔드포인트) 제공

쿠버네티스는 각 노드의 생성 및 삭제가 자유롭다. 그때마다 노드 IP를 추적하는 일은 비효율적이며, 안정적인 서비스 Endpoint를 제공을 필요로 한다. ClusterIP 타입의 서비스가 Pod 레벨에서의 안정적인 엔드포인트를 제공하는 것이라면,  로드밸런서는 노드레벨에서의 안정적인 엔드포인트를 제공한다.

 

로드밸런서가 클러스터 서버 앞단에 존재함으로서 클라이언트 입장에서 각각의 서버 IP(master, worker)를 알 필요 없이 로드밸런서의 IP 또는 도메인 주소만 가지고 요청을 보낼 수 있는 편리성을 제공한다.

 

둘째, 정상적인 노드 대상으로 트래픽 라우팅

직접적으로 특정 노드로 요청을 보내는 대신, 로드밸런서를 통해 사용가능한(Available) 노드 대상으로 트래픽을 전달한다. 또한, 로드밸런서는 모든 노드에 균등하게 트래픽을 전달하는 특징을 지닌다.

 

 


서비스 엔드포인트 

엔드포인트 개요

서비스는 일반적으로 Label Selector를 통해 서비스 오브젝트와 매칭되는 뒷단의 파드 집단(Pod Set)으로 트래픽을 전달한다. Label Selector와 별개로 서비스 오브젝트는 spec 이나 status 속성에서 파드들과 직접적으로 연결(link)되는 항목을 포함하지 않는다.

Service YAML 예시

 

그러나, kubectl describe <<service name>> 을 통해 서비스 내용을 살펴보면 엔드포인트에 대한 리소스를 확인할 수 있다. 

root@k8s-m:~# kubectl describe svc kiada
Name:                     kiada
Namespace:                john
...
Endpoints:                172.16.197.3:8080,172.16.197.4:8080,172.16.228.71:8080 + 1 more...
Port:                     https  443/TCP
TargetPort:               8443/TCP
NodePort:                 https  30443/TCP
Endpoints:                172.16.197.3:8443,172.16.197.4:8443,172.16.228.71:8443 + 1 more...
...

 

엔드포인트 리소스는 다른 쿠버네티스 리소스와 유사하며 kubectl get endpoints 명령어를 통해서도 확인이 가능하다.

 

endpoints 를 줄여서 ep 로도 이용할 수 있다. 주의할 점으로 Endpoint 복수형인 Endpoints 를 사용하며, Endpoint 로 명령어를 입력하면 검색되지 않는다.
# 'endpoints' 를 줄여서 'ep' 로 검색 가능하다.
root@k8s-m:~# kubectl get ep
NAME    ENDPOINTS                                                            AGE
kiada   172.16.197.3:8443,172.16.197.4:8443,172.16.228.71:8443 + 5 more...   60d
quiz    172.16.46.3:8080                                                     82d
quote   172.16.197.1:80,172.16.197.2:80,172.16.228.65:80 + 1 more...         82d

# "endpoint" 로 두면, 검색되지 않는다.
root@k8s-m:~# kubectl get endpoint
error: the server doesn't have a resource type "endpoint"

엔드포인트 검색시 한 네임스페이스에서 확인되는 3개 대상이 확인되며, 각 대상마다 연결된 IP 들을 확인할 수 있다.


 

엔드포인트에 대한 이해

kubectl get -o yaml 명령어를 통해 엔드포인트에 대하여 조금 더 자세히 알아보자.

root@k8s-m:~# kubectl get ep kiada -o yaml
apiVersion: v1
kind: Endpoints
metadata:
  creationTimestamp: "2022-06-07T14:23:53Z"
  name: kiada
  namespace: john
...
subsets:
- addresses:
  - ip: 172.16.197.3
    nodeName: k8s-w3
    targetRef:
      kind: Pod
      name: kiada-002
      namespace: john
      resourceVersion: "244467"
      uid: 83c89909-5d55-4273-8178-4bdae8f69aad
   - ip: 172.16.197.4
    nodeName: k8s-w3
    targetRef:
      kind: Pod
      name: kiada-canary
      namespace: john
      resourceVersion: "244498"
      uid: d882776f-019f-4753-b30d-19351f06ed49
  - ip: 172.16.228.71
    nodeName: k8s-w1
    targetRef:
      kind: Pod
      name: kiada-001
      namespace: john
      resourceVersion: "53692"
      uid: e3e10485-00b4-4e0d-9bfd-ce3215de76ef
 ...
  ports:
  - name: https
    port: 8443
    protocol: TCP
  - name: http
    port: 8080
    protocol: TCP

 

연결된 각 파드들이 addresses 항목으로 열거되어 있는 것을 확인할 수 있다. kiada 엔드포인트 오브젝트 내 존재하는 모든 endpoint 들이 한 엔드포인트 subset에 포함되어 있는 것을 확인할 수 있는데, 이는 이들이 모두 같은 포트번호를 가지기 때문이다. 만약, 한 파드그룹은 8080 포트를 사용하고, 다른 파드그룹은 8088 포트번호를 사용한다면, subset이 2개를 가질 것이다.

 

다시말해, subset 필드값은 파드그룹이 사용하는 포트 종류 수와 동일하다.


엔드포인트 수동 구성하기

Label Selector를 통해 서비스 오브젝트를 생성할 때 쿠버네티스는 자동으로 엔드포인트와 엔드포인트 슬라이스 오브젝트를 생성한다. 그러나, 서비스의 Label Select 없이 생성하면 엔드포인트를 수동으로 구성하고 업데이트를 할 수 있다.

 

수동으로 관리되는 엔드포인트를 사용하여 서비스를 만들기 위해서는 서비스와 엔드포인트 각각 리소스를 별개로 만들어야 한다. 한번 직접 생성해보자. 

<Service without Pod Selector>

아래 포트 80으로 들어오는 연결을 허용하는 external-service라는 서비스를 정의하는 YAML 파일이다. Pod Selector를 별도로 정의하지 않은 것에 의의를 두자. 유의할 점으로 서비스 오브젝트 이름은 엔트포인트 이름과 일치해야 한다.

apiVersion: v1
kind: Service
metadata:
  name: external-service # 엔드포인트 오브젝트 이름과 일치해야 한다.
spec:
  ports:
  - name: http
    port: 80

 

<Endpoint Object>

여기서 엔드포인트는 별도의 리소스이며, 서비스 속성은 아니다. 유의할 점으로는 엔드포인트 오브젝트 이름은 서비스 이름과 일치해야 한다. 여기서 EndpointSlice 는 쿠버네티스에서 자동으로 생성해주기에 별도로 생성할 필요 없다.

apiVersion: v1
kind: Endpoints
metadata:
  name: external-service # 서비스 오브젝트 이름과 일치해야 한다.
subsets:
- addresses:
  - ip: 1.1.1.1
  - ip: 2.2.2.2
  ports:
  - name: http
    port: 88

서비스와 엔드포인트가 만들어진 이후에는 Pod Selector가 있는 일반 서비스처럼 서비스를 사용할 수 있다.

 

2개의 수동 엔드 포인트 구성도

이후에 수동으로 구성한 클러스터 내부에서 external-service를 사용하기로 결정하였다면, Selector를 추가함으로써 클러스터 내 존재하는 pod 들에게 트래픽을 분배하고 쿠버네티스는 엔드포인트를 자동으로 관리해준다. 반대로 클러스터 외부 환경에서 서비스를 사용하고자 한다면 Selector를 제거하면 된다. 그러면 쿠버네티스는 더 이상 엔드포인트에 대하여 관여하지 않으며 직접 엔드포인트를 관리할 수 있다.

 

이러한 과정을 진행하기 위해 굳이 서비스를 삭제하고 생성하는 방식으로 수정할 필요는 없다. 서비스 오브젝트를 수정만 진행하면 되는 부분이며, Cluster IP는 항상 constant로 남아 있기에, 클라이언트는 서비스 변동사항에 대하여 인지하지 못한다.

 


참조

 

댓글