엔드포인트슬라이스
Kubernetes v1.21 [stable]
쿠버네티스의 엔드포인트슬라이스 API 는 쿠버네티스 클러스터 내의 네트워크 엔드포인트를 추적하는 방법을 제공한다. 엔드포인트슬라이스는 엔드포인트보다 더 유동적이고, 확장 가능한 대안을 제안한다.
엔드포인트슬라이스 API
쿠버네티스에서 엔드포인트슬라이스는 일련의 네트워크 엔드포인트에 대한 참조를 포함한다. 쿠버네티스 서비스에 셀렉터가 지정되면 컨트롤 플레인은 자동으로 엔드포인트슬라이스를 생성한다. 이 엔드포인트슬라이스는 서비스 셀렉터와 매치되는 모든 파드들을 포함하고 참조한다. 엔드포인트슬라이스는 프로토콜, 포트 번호 및 서비스 이름의 고유한 조합을 통해 네트워크 엔드포인트를 그룹화한다. 엔드포인트슬라이스 오브젝트의 이름은 유효한 DNS 서브도메인 이름이어야 한다.
예를 들어, 여기에 example
쿠버네티스 서비스가 소유하는 엔드포인트슬라이스
객체 샘플이 있다.
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-abc
labels:
kubernetes.io/service-name: example
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
nodeName: node-1
zone: us-west2-a
기본적으로, 컨트롤 플레인은 각각 100개 이하의 엔드포인트를
갖도록 엔드포인트슬라이스를
생성하고 관리한다. --max-endpoints-per-slice
kube-controller-manager
플래그를 사용하여, 최대 1000개까지 구성할 수 있다.
엔드포인트슬라이스는 내부 트래픽을 라우트하는 방법에 대해 kube-proxy에 신뢰할 수 있는 소스로 역할을 할 수 있다.
주소 유형
엔드포인트슬라이스는 다음 주소 유형을 지원한다.
- IPv4
- IPv6
- FQDN (전체 주소 도메인 이름)
각 엔드포인트슬라이스 객체는 특정한 IP 주소 유형을 나타낸다. IPv4와 IPv6를 사용할 수 있는 서비스가 있을 경우, 최소 두개의 엔드포인트슬라이스 객체가 존재한다(IPv4를 위해 하나, IPv6를 위해 하나).
컨디션
엔드포인트슬라이스 API는 컨슈머에게 유용한 엔드포인트에 대한 조건을 저장한다.
조건은 ready
, serving
및 Terminating
세 가지가 있다.
Ready
ready
는 파드의 Ready
조건에 매핑되는 조건이다. Ready
조건이 True
로 설정된 실행 중인 파드는
이 엔드포인트슬라이스 조건도 true
로 설정되어야 한다. 호환성의
이유로, 파드가 종료될 때 ready
는 절대 true
가 되면 안 된다. 컨슈머는 serving
조건을 참조하여
파드 종료 준비 상태(readiness)를 검사해야 한다.
이 규칙의 유일한 예외는 spec.publishNotReadyAddresses
가 true
로 설정된 서비스이다.
이러한 서비스의 엔드 포인트는 항상 ready
조건이 true
로 설정된다.
Serving
Kubernetes v1.22 [beta]
serving
은 종료 상태를 고려하지 않는다는 점을 제외하면 ready
조건과 동일하다.
엔드포인트슬라이스 API 컨슈머는 파드가 종료되는 동안 파드 준비 상태에 관심이 있다면
이 조건을 확인해야 한다.
serving
은 ready
와 거의 동일하지만 ready
의 기존 의미가 깨지는 것을 방지하기 위해 추가되었다.
엔드포인트를 종료하기 위해 ready
가 true
일 수 있다면 기존 클라이언트에게는 예상치 못한 일이 될 수 있다.
역사적으로 종료된 엔드포인트는 처음부터 엔드포인트 또는 엔드포인트슬라이스 API에 포함되지 않았기 때문이다.
이러한 이유로 ready
는 엔드포인트 종료를 위해 always false
이며,
클라이언트가 ready
에 대한 기존 의미와 관계없이 파드 종료 준비 상태를
추적 할 수 있도록 v1.20에 새로운 조건 serving
이 추가되었다.Terminating
Kubernetes v1.22 [beta]
종료(Terminating)
는 엔드포인트가 종료되는지 여부를 나타내는 조건이다.
파드의 경우 삭제 타임 스탬프가 설정된 모든 파드이다.
토폴로지 정보
엔드포인트슬라이스 내의 각 엔드 포인트는 관련 토폴로지 정보를 포함할 수 있다. 토폴로지 정보에는 엔드 포인트의 위치와 해당 노드 및 영역에 대한 정보가 포함된다. 엔드포인트슬라이스의 다음의 엔드 포인트별 필드에서 사용할 수 있다.
*nodeName
- 이 엔드 포인트가 있는 노드의 이름이다.
*zone
- 이 엔드 포인트가 있는 영역이다.
v1 API에서는, 전용 필드 nodeName
및 zone
을 위해 엔드 포인트별
topology
가 효과적으로 제거되었다.
EndpointSlice
리소스의 endpoint
필드에 임의의 토폴로지 필드를 설정하는 것은
더 이상 사용되지 않으며 v1 API에서 지원되지 않는다.
대신, v1 API는 개별 nodeName
및 zone
필드 설정을 지원한다.
이러한 필드는 API 버전 간에 자동으로 번역된다.
예를 들어, v1beta1 API의 topology
필드에 있는 "topology.kubernetes.io/zone"
키 값은
v1 API의 zone
필드로 접근할 수 있다.
관리
대부분의 경우, 컨트롤 플레인(특히, 엔드포인트슬라이스 컨트롤러)는 엔드포인트슬라이스 오브젝트를 생성하고 관리한다. 다른 엔티티나 컨트롤러가 추가 엔드포인트슬라이스 집합을 관리하게 할 수 있는 서비스 메시 구현과 같이 엔드포인트슬라이스에 대한 다양한 다른 유스케이스가 있다.
여러 엔티티가 서로 간섭하지 않고 엔드포인트슬라이스를
관리할 수 있도록 쿠버네티스는 엔드포인트슬라이스를 관리하는
엔티티를 나타내는 endpointslice.kubernetes.io/managed-by
레이블을
정의한다.
엔드포인트슬라이스 컨트롤러는 관리하는 모든 엔드포인트슬라이스에 레이블의 값으로
endpointslice-controller.k8s.io
를 설정한다. 엔드포인트슬라이스를
관리하는 다른 엔티티도 이 레이블에 고유한 값을 설정해야 한다.
소유권
대부분의 유스케이스에서, 엔드포인트슬라이스 오브젝트가 엔드포인트를
추적하는 서비스가 엔드포인트슬라이스를 소유한다. 이 소유권은 각 엔드포인트슬라이스의 소유자
참조와 서비스에 속한 모든 엔드포인트슬라이스의 간단한 조회를 가능하게 하는
kubernetes.io/service-name
레이블로 표시된다.
엔드포인트슬라이스 미러링
경우에 따라, 애플리케이션이 사용자 지정 엔드포인트 리소스를 생성한다. 이러한 애플리케이션이 엔드포인트와 엔드포인트슬라이스 리소스에 동시에 쓸 필요가 없도록 클러스터의 컨트롤 플레인은 대부분의 엔드포인트 리소스를 해당 엔드포인트슬라이스에 미러링한다.
컨트롤 플레인은 다음을 제외하고 엔드포인트 리소스를 미러링한다.
- 엔드포인트 리소스에는
endpointslice.kubernetes.io/skip-mirror
레이블이true
로 설정되어 있다. - 엔드포인트 리소스에는
control-plane.alpha.kubernetes.io/leader
어노테이션이 있다. - 해당 서비스 리소스가 존재하지 않는다.
- 해당 서비스 리소스에 nil이 아닌 셀렉터가 있다.
개별 엔드포인트 리소스는 여러 엔드포인트슬라이스로 변환될 수 있다. 엔드포인트 리소스에 여러 하위 집합이 있거나 여러 IP 제품군(IPv4 및 IPv6)이 있는 엔드포인트가 포함된 경우 변환이 일어난다. 하위 집합 당 최대 1000개의 주소가 엔드포인트슬라이스에 미러링된다.
엔드포인트슬라이스의 배포
각 엔드포인트슬라이스에는 리소스 내에 모든 엔드포인트가 적용되는 포트 집합이 있다. 서비스에 알려진 포트를 사용하는 경우 파드는 동일하게 알려진 포트에 대해 다른 대상 포트 번호로 끝날 수 있으며 다른 엔드포인트슬라이스가 필요하다. 이는 하위 집합이 엔드포인트와 그룹화하는 방식의 논리와 유사하다.
컨트롤 플레인은 엔드포인트슬라이스를 최대한 채우려고 노력하지만, 적극적으로 재조정하지는 않는다. 로직은 매우 직관적이다.
- 기존 엔드포인트슬라이스에 대해 반복적으로, 더 이상 필요하지 않는 엔드포인트를 제거하고 변경에 의해 일치하는 엔드포인트를 업데이트 한다.
- 첫 번째 단계에서 수정된 엔드포인트슬라이스를 반복해서 필요한 새 엔드포인트로 채운다.
- 추가할 새 엔드포인트가 여전히 남아있으면, 이전에 변경되지 않은 슬라이스에 엔드포인트를 맞추거나 새로운 것을 생성한다.
중요한 것은, 세 번째 단계는 엔드포인트슬라이스를 완벽하게 전부 배포하는 것보다 엔드포인트슬라이스 업데이트 제한을 우선시한다. 예를 들어, 추가할 새 엔드포인트가 10개이고 각각 5개의 공간을 사용할 수 있는 엔드포인트 공간이 있는 2개의 엔드포인트슬라이스가 있는 경우, 이 방법은 기존 엔드포인트슬라이스 2개를 채우는 대신에 새 엔드포인트슬라이스를 생성한다. 다른 말로, 단일 엔드포인트슬라이스를 생성하는 것이 여러 엔드포인트슬라이스를 업데이트하는 것 보다 더 선호된다.
각 노드에서 kube-proxy를 실행하고 엔드포인트슬라이스를 관찰하면, 엔드포인트슬라이스에 대한 모든 변경 사항이 클러스터의 모든 노드로 전송되기 때문에 상대적으로 비용이 많이 소요된다. 이 방법은 여러 엔드포인트슬라이스가 가득 차지 않은 결과가 발생할지라도, 모든 노드에 전송해야 하는 변경 횟수를 의도적으로 제한하기 위한 것이다.
실제로는, 이러한 이상적이지 않은 분배는 드물 것이다. 엔드포인트슬라이스 컨트롤러에서 처리하는 대부분의 변경 내용은 기존 엔드포인트슬라이스에 적합할 정도로 적고, 그렇지 않은 경우 새 엔드포인트슬라이스가 필요할 수 있다. 디플로이먼트의 롤링 업데이트도 모든 파드와 해당 교체되는 엔드포인트에 대해서 엔드포인트슬라이스를 자연스럽게 재포장한다.
중복 엔드포인트
엔드포인트슬라이스 변경의 특성으로 인해, 엔드포인트는 동시에 둘 이상의 엔드포인트슬라이스에 표시될 수 있다. 이는 다른 엔드포인트슬라이스 오브젝트에 대한 변경 사항이 다른 시간에서의 쿠버네티스 클라이언트 워치(watch)/캐시에 도착할 수 있기 때문에 자연스럽게 발생한다.
엔드포인트슬라이스 API의 클라이언트는 반드시 서비스에 연관된 모든 존재하는 엔드포인트슬라이스에 대해 반복하고, 고유한 각 네트워크 엔드포인트들의 완전한 리스트를 구성해야 한다. 엔드포인트는 다른 엔드포인트슬라이스에서 중복될 수 있음에 유의한다.
엔드포인트 집계와 중복 제거 방법에 대한 레퍼런스 구현은 kube-proxy
의
EndpointSliceCache
구현에서 찾을 수 있다.
엔드포인트와 비교
기존 엔드포인트 API는 쿠버네티스에서 네트워크 엔드포인트를 추적하는 간단하고 직접적인 방법을 제공했다. 쿠버네티스 클러스터와 서비스가 더 많은 수의 백엔드 파드로 더 많은 트래픽을 처리하고 전송하는 방향으로 성장함에 따라, 이 API의 한계가 더욱 눈에 띄게 되었다. 특히나, 많은 수의 네트워크 엔드포인트로 확장하는 것에 어려움이 있었다.
서비스에 대한 모든 네트워크 엔드포인트가 단일 엔드포인트 객체에 저장되기 때문에 이러한 엔드포인트 객체들이 상당히 커지는 경우도 있었다. 안정적인 서비스(오랜 기간 동안 같은 엔드포인트 세트)의 경우 영향은 비교적 덜 눈에 띄지만, 여전히 쿠버네티스의 일부 사용 사례들은 잘 처리되지 않았다.
서비스가 많은 백엔드 엔드포인트를 가지고 워크로드가 자주 증가하거나, 새로운 변경사항이 자주 롤 아웃 될 경우, 해당 서비스의 단일 엔드포인트 객체에 대한 각 업데이트는 쿠버네티스 클러스터 컴포넌트 사이(컨트롤 플레인 내, 그리고 노드와 API 서버 사이)에 상당한 네트워크 트래픽이 발생할 것임을 의미했다. 이러한 추가 트래픽은 또한 CPU 사용 관점에서도 굉장한 비용을 발생시켰다.
엔드포인트슬라이스 사용 시, 단일 파드를 추가하거나 삭제하는 작업은 (다수의 파드를 추가/삭제하는 작업과 비교했을 때) 해당 변경을 감시하고 있는 클라이언트에 동일한 _수_의 업데이트를 트리거한다. 하지만, 파드 대규모 추가/삭제의 경우 업데이트 메시지의 크기는 훨씬 작다.
엔드포인트슬라이스는 듀얼 스택 네트워킹과 토폴로지 인식 라우팅과 같은 새로운 기능에 대한 혁신을 가능하게 했다.
다음 내용
- 서비스와 애플리케이션 연결하기 튜토리얼을 따라하기
- 엔드포인트슬라이스 API 레퍼런스 를 읽어보기
- 엔드포인트 API 레퍼런스 를 읽어보기