EndpointSlices
EndpointSlices
EndpointSlice API
쿠버네티스에서 EndpointSlice는 네트워크 엔드포인트 집합에 대한 참조를 포함해요. 제어 플레인은 셀렉터가 지정된 모든 쿠버네티스 Service에 대해 EndpointSlice를 자동으로 만들어요. 이러한 EndpointSlice는 Service 셀렉터와 일치하는 모든 파드에 대한 참조를 포함해요. EndpointSlice는 IP 패밀리, 프로토콜, 포트 번호, Service 이름의 고유한 조합으로 네트워크 엔드포인트를 그룹화해요. EndpointSlice 객체의 이름은 유효한 DNS 하위 도메인 이름이어야 해요.
예를 들어, 예시 쿠버네티스 Service가 소유하는 샘플 EndpointSlice 객체는 다음과 같아요.
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개 이하의 엔드포인트를 가지도록 EndpointSlice를 만들고 관리해요. 이를 --max-endpoints-per-slice kube-controller-manager 플래그로 구성할 수 있으며, 최대 1000까지 가능해요.
EndpointSlice는 내부 트래픽을 어떻게 라우팅할지에 대해 kube-proxy의 신뢰 소스(source of truth) 역할을 해요.
출처: 문서
본문
주소 유형 (Address types)
EndpointSlice는 두 가지 주소 유형을 지원해요.
- IPv4
- IPv6
각 EndpointSlice 객체는 특정 IP 주소 유형을 나타내요. Service가 IPv4와 IPv6 모두로 제공된다면, EndpointSlice 객체가 최소 두 개 있을 거예요(하나는 IPv4용, 하나는 IPv6용).
조건 (Conditions)
EndpointSlice API는 소비자에게 유용할 수 있는 엔드포인트에 대한 조건을 저장해요. 세 가지 조건은 serving, terminating, ready예요.
Serving
serving 조건은 엔드포인트가 현재 응답을 서비스 중이며 Service 트래픽의 대상으로 사용되어야 함을 나타내요. 파드가 뒷받침하는 엔드포인트의 경우 이는 파드의 Ready 조건에 매핑돼요.
Terminating
terminating 조건은 엔드포인트가 종료 중임을 나타내요. 파드가 뒷받침하는 엔드포인트의 경우 이 조건은 파드가 처음 삭제될 때(즉, 삭제 타임스탬프를 받을 때, 하지만 파드의 컨테이너가 종료되기 전일 가능성이 높을 때) 설정돼요.
Service 프록시는 보통 terminating 엔드포인트를 무시하지만, 사용 가능한 모든 엔드포인트가 종료 중이라면 serving과 terminating을 모두 만족하는 엔드포인트에 트래픽을 라우팅할 수 있어요. (이것은 기반 파드의 롤링 업데이트 중에 Service 트래픽이 손실되지 않도록 보장하는 데 도움이 돼요.)
Ready
ready 조건은 기본적으로 "serving 이고 not terminating"을 확인하는 지름길이에요(spec.publishNotReadyAddresses가 true로 설정된 Service에서는 항상 true가 되긴 하지만).
토폴로지 정보 (Topology information)
EndpointSlice 안의 각 엔드포인트는 관련 토폴로지 정보를 포함할 수 있어요. 토폴로지 정보는 엔드포인트의 위치와 해당 노드 및 존에 대한 정보를 포함해요. 이들은 EndpointSlice의 다음과 같은 엔드포인트별 필드에서 사용할 수 있어요.
nodeName- 이 엔드포인트가 있는 노드의 이름.zone- 이 엔드포인트가 있는 존.
관리 (Management)
가장 흔하게 제어 플레인(특히 endpoint slice 컨트롤러)이 EndpointSlice 객체를 만들고 관리해요. 서비스 메시 구현 같은 EndpointSlice의 다른 다양한 사용 사례가 있어, 다른 개체나 컨트롤러가 추가 EndpointSlice 집합을 관리할 수 있어요.
여러 개체가 서로 간섭하지 않고 EndpointSlice를 관리할 수 있도록, 쿠버네티스는 endpointslice.kubernetes.io/managed-by 라벨을 정의해 EndpointSlice를 관리하는 개체를 나타내요. endpoint slice 컨트롤러는 자신이 관리하는 모든 EndpointSlice에서 이 라벨의 값으로 endpointslice-controller.k8s.io를 설정해요. EndpointSlice를 관리하는 다른 개체도 이 라벨에 고유한 값을 설정해야 해요.
소유권 (Ownership)
대부분의 사용 사례에서 EndpointSlice는 엔드포인트 슬라이스 객체가 엔드포인트를 추적하는 Service가 소유해요. 이 소유권은 각 EndpointSlice의 소유자 참조(owner reference)와, Service에 속한 모든 EndpointSlice를 간단히 조회할 수 있게 해주는 kubernetes.io/service-name 라벨로 표시돼요.
EndpointSlice 분포 (Distribution of EndpointSlices)
각 EndpointSlice에는 리소스 안의 모든 엔드포인트에 적용되는 포트 집합이 있어요. Service에 명명된 포트(named ports)를 사용하면, 파드는 같은 명명된 포트에 대해 서로 다른 대상 포트 번호를 가질 수 있어, 다른 EndpointSlice가 필요할 수 있어요.
제어 플레인은 EndpointSlice를 가능한 한 꽉 채우려고 하지만, 적극적으로 재균형(rebalance)하지는 않아요. 로직은 꽤 단순해요.
- 기존 EndpointSlice를 반복하고, 더 이상 원하지 않는 엔드포인트를 제거하고 변경된 일치하는 엔드포인트를 업데이트해요.
- 첫 번째 단계에서 수정된 EndpointSlice를 반복하고, 필요한 새 엔드포인트로 채워요.
- 여전히 추가할 새 엔드포인트가 있다면, 이전에 변경되지 않은 슬라이스에 넣거나 새 것을 만들려고 해요.
중요하게도, 세 번째 단계는 완벽하게 가득 찬 EndpointSlice 분포보다 EndpointSlice 업데이트 제한을 우선시해요. 예를 들어 추가할 새 엔드포인트 10개와 각각 5개 더 담을 여유가 있는 EndpointSlice 2개가 있다면, 이 접근 방식은 기존 EndpointSlice 2개를 채우는 대신 새 EndpointSlice를 만들어요. 다시 말해, 단일 EndpointSlice 생성이 여러 EndpointSlice 업데이트보다 선호돼요.
각 노드에서 kube-proxy가 실행되며 EndpointSlice를 감시하므로, EndpointSlice의 모든 변경은 클러스터의 모든 노드로 전송되기 때문에 상대적으로 비싸져요. 이 접근 방식은 가득 차지 않은 EndpointSlice가 여러 개가 될 수 있더라도 모든 노드로 보내야 하는 변경 수를 제한하려는 의도예요.
실제로 이 이상적이지 않은 분포는 드물어야 해요. EndpointSlice 컨트롤러가 처리하는 대부분의 변경은 기존 EndpointSlice에 들어갈 만큼 작을 것이고, 그렇지 않다면 어차피 곧 새 EndpointSlice가 필요할 가능성이 높아요. Deployment의 롤링 업데이트는 또한 모든 파드와 해당 엔드포인트가 교체되면서 EndpointSlice의 자연스러운 재패킹을 제공해요.
중복 엔드포인트 (Duplicate endpoints)
EndpointSlice 변경의 특성 때문에, 엔드포인트는 같은 시점에 둘 이상의 EndpointSlice에 표현될 수 있어요. 이는 서로 다른 EndpointSlice 객체에 대한 변경이 쿠버네티스 클라이언트 watch/캐시에 다른 시점에 도착할 수 있으므로 자연스럽게 발생해요.
참고:
EndpointSlice API의 클라이언트는 Service에 연결된 모든 기존 EndpointSlice를 반복하고 고유한 네트워크 엔드포인트의 완전한 목록을 만들어야 해요. 엔드포인트가 다른 EndpointSlice에서 중복될 수 있다는 점을 언급하는 것이 중요해요.
이 엔드포인트 집계 및 중복 제거를 수행하는 참조 구현은 kube-proxy 안의 EndpointSliceCache 코드의 일부로 찾을 수 있어요.
EndpointSlice 미러링 (EndpointSlice mirroring)
EndpointSlice API는 더 오래된 Endpoints API의 대체품이에요. kube-proxy가 Endpoints 리소스에 기반해 트래픽을 라우팅할 것을 기대하는 오래된 컨트롤러와 사용자 워크로드와의 호환성을 보존하기 위해, 클러스터의 제어 플레인은 대부분의 사용자가 만든 Endpoints 리소스를 해당 EndpointSlice로 미러링해요.
(하지만 이 기능은 다른 Endpoints API와 함께 폐기됐어요. selectorless Service에 대한 엔드포인트를 수동으로 지정하는 사용자는 Endpoints 리소스를 만들고 그것을 미러링하게 두기보다 EndpointSlice 리소스를 직접 만들어 지정해야 해요.)
제어 플레인은 다음 경우를 제외하고 Endpoints 리소스를 미러링해요.
- Endpoints 리소스에
endpointslice.kubernetes.io/skip-mirror라벨이 true로 설정되어 있음. - Endpoints 리소스에
control-plane.alpha.kubernetes.io/leader어노테이션이 있음. - 해당 Service 리소스가 존재하지 않음.
- 해당 Service 리소스에 nil이 아닌 셀렉터가 있음.
개별 Endpoints 리소스는 여러 EndpointSlice로 변환될 수 있어요. 이는 Endpoints 리소스에 여러 부분 집합(subset)이 있거나 여러 IP 패밀리(IPv4 및 IPv6)의 엔드포인트를 포함하는 경우 발생해요. 부분 집합당 최대 1000개 주소가 EndpointSlice로 미러링돼요.
더 알아보기 (Learn more)
- 서비스로 애플리케이션 연결하기 튜토리얼 따라가기
- EndpointSlice API 참조 읽기
- Endpoints API 참조 읽기