토폴로지 기반 라우팅
개요
클라우드 환경에서 클러스터를 운영하다보면, 어느 지역에서 어떤 가용영역이 있어서 어떻게 트래픽을 분산해야 효율적인지에 대한 문제가 중요해진다.
예를 들어보자.
EKS를 운영하는데 3개의 AZ에 전부 노드를 배치했고, 서비스 하나가 모든 az에 위치한 파드를 백엔드로 두고 있다.
이때 1 az에 있는 다른 파드가 서비스에 트래픽을 보냈다.
기본적으로 이 요청은 서비스의 트래픽 분산 규칙에 정의된 대로, 다양한 az에 위치한 백엔드로 분산된다..
즉, 1 az에 있는 백엔드로 가야 별도의 비용이 발생하지 않는데 멋대로 3az로 트래픽이 가버리는 상황이 발생할 수도 있다는 말이다.
일단 트래픽 처리 속도도 느릴 것이고, 추가적으로 크로스존 비용까지 감당해야 하는 웃픈 상황이 나올 수 있다.
물론 분산 처리를 통해 효율적으로 트래픽을 처리하게 될 수도 있지만, 오히려 이렇게 비효율적인 상황 역시 충분히 나올 수 있다는 것이다.
이에 대한 세세한 설정을 하기 위해 등장한 것이 바로 토폴로지 기반 라우팅이다.
최대한 같은 존으로 트래픽을 보냄으로써 처리 효율성과 비용 효율성을 챙기는 것을 도와준다!
참고로 이것은 1.27버전까지 Topology Aware Hints라고 불렀다.
service.kubernetes.io/topology-aware-hints
이라는 어노테이션을 사용하는 방식이었다.
현재는 서비스에 service.kubernetes.io/topology-mode : Auto
라는 어노테이션을 다는 방식으로 활성화할 수 있다.
동작 방식
서비스에 엔드포인트슬라이스를 연결하는 컨트롤러가 토폴로지에 대한 정보를 가지고 엔드포인트 슬라이스를 세분화시켜준다.
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-hints
labels:
kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
zone: zone-a
hints:
forZones:
- name: "zone-a"
이런 식으로 엔드포인트슬라이스 오브젝트가 만들어지게 되는 것이다.
구체적으로 region과 zone에 대한 정보를 활용하는 것이다.
맨 아래 hints
필드를 통해 어떤 존의 노드에서 이 엔포슬을 썼으면 하는지 작성해준다.
무조건 1az의 엔드포인트들에 대해 1az만 힌트로 달아두는 게 아니라 비례적으로 분산을 해주는 식이다.
그러면 각 노드의 kube-proxy가 이 힌트를 활용해 노드의 분산 규칙을 작성한다.
힌트에 자신의 존이 적혔다면 해당 엔포슬을 사용하는 건데, 비례적 분산이기에 결과적으로는 다른 존으로 트래픽이 갈 수도 있다.
각 zone에 충분한 수의 엔드포인트가 있을 때, 엔포슬 컨트롤러가 알아서 엔포슬을 세분화시킨다.
좋은 상황
같은 토폴로지로 트래픽을 보내는 게 아무때나 이득을 보는 게 절대 아니다.
그럼 이 기능을 언제 사용하는 것이 좋을까?
- 트래픽이 공평하게 분산되고 있는 상황
- 대량의 트래픽이 한 존에서만 발생하고 있는 상황이라고 해보자.
- 위에서 본 예시에서 1az에서만 엄청나게 요청을 보내는 상황이라면, 이걸 전부 1az에서 처리하는 게 오히려 손해일 수 있다.
- 모든 처리를 빠르게 하기 위해서 모든 백엔드로 트래픽을 보내는 게 이득일 수 있다는 것.
- 서비스가 각 존에 3개 이상의 엔드포인트를 가지고 있는 상황
- 각 존에 3개 미만의 엔드포인트가 있다면, 엔포슬이 엔드포인트 분산을 제대로 못 시킬 수 있다.(50퍼 확률?)
In a three zone cluster, this means 9 or more endpoints. If there are fewer than 3 endpoints per zone, there is a high (≈50%) probability that the EndpointSlice controller will not be able to allocate endpoints evenly and instead will fall back to the default cluster-wide routing approach.
How It Works
The "Auto" heuristic attempts to proportionally allocate a number of endpoints to each zone. Note that this heuristic works best for Services that have a significant number of endpoints.
ㅐ
컨트롤러는 각 존에 비례적으로 엔드포인트를 할당한다.
이 비율은 해당 존에 있는 노드, 혹은 cpu 개수를 기반으로 한다.
가령 한 존에 2개의 cpu가 있고 다른 존에는 하나만 있다고 있다면, 컨트롤러는 2개가 있는 쪽에 더 많은 엔드포인트를 부여한다.
프록시는 엔포슬 오브젝트에 적힌 힌트를 기반으로 엔드포인트를 필터링한다.
보험
이 라우팅은 엄격하게 적용되도록 설계되지 않았다.
컨트롤 플레인과 프록시는 여기에 몇 가지 보험 규칙을 걸어뒀다.
- 충분치 않은 엔드포인트 개수
- 해당 존에 엔드포인트 개수가 충분치 않다면 컨트롤러는 애초에 힌트를 부여하지 않는다.
- 균형 잡힌 할당이 불가능할 때
- 존에 따라 균형 잡히게 엔드포인트를 분산할 수 없는 상황이 있을 수 있다.
- 1 az가 노드 개수도 두 배에 cpu 코어도 많은데 다른 az와 비교해 막상 엔드포인트는 훨씬 적다면, 이런 힌트가 무의미할 수 있다.
- 컨트롤러는 각 존의 예상 과부하치(expected overload)를 들고 있고, 토폴로지 분산을 했더니 이 임계값을 넘길 경우 힌트를 부여하지 않는다.
- 참고로 이 값은 실시간으로 업데이트되는 값이 아니기에 그럼에도 문제가 생길 여지는 있다.
- 한 개 이상의 노드가 불충분한 정보를 가졌을 때
- 노드에
topology.kubernetes.io/zone
라벨이 없거나, cpu에 대한 정보가 없다면 컨트롤러는 판단의 근거가 없기에 엔드포인트 오브젝트에 힌트를 부여하지 않는다.
- 노드에
- 한 개 이상의 엔드포인트가 zone 관련 정보가 아예 없을 때
- kube proxy가 막상 엔포슬 오브젝트를 받아봤더니 어떤 놈에는 힌트가 부여 안 돼 있다면, 프록시는 현재 힌트에 문제가 있다 판단하고 규칙을 작성하는데 해당 정보를 고려하지 않는다.
- 자신의 존을 힌트로 낸 엔포슬이 없을 때
- 프록시는 자신이 있는 노드에 대한 힌트가 없는 상황이면 그냥 모든 엔드포인트로 규칙을 쓴다.
- 새롭게 클러스터에 추가된 존에서 이 상황이 보통 발생한다.
제약
- 트래픽 정책 중
internalTrafficPolicy : Local
이 설정된 서비스에 대해선 적용되지 않는다. - 한 존에 여러 부분집합을 둔 서비스에 대해서는 그다지 효과를 보지 못한다.
- 컨트롤러는 준비가 되지 않은 노드의 정보를 무시하므로, 그런 노드가 많을 때 예상치 못한 동작이 일어날 수 있다.
node-role.kubernetes.io/control-plane
,node-role.kubernetes.io/master
라벨이 설정된 노드를 무시한다.- eks라면 어차피 컨트롤 플레인이 우리가 건드릴 영역이 아니라 큰 상관은 없을 것이다.
- 테인트, 톨러레이션 정보를 고려하지 않는다.
- 오토스케일링과 함께 동작이 잘 안 될 수 있다.
- 가령 이 설정으로 인해 한 존에서 많은 트래픽이 몰렸다고 해도 HPA가 해당 존에 파드를 추가해주는 것은 보장할 수 없다.
- 내 생각에는 이건 추후에 고려될 가능성이 있긴 한데, hpa를 관리하는 팀이 신경을 써줘야 할 듯..
- 가령 이 설정으로 인해 한 존에서 많은 트래픽이 몰렸다고 해도 HPA가 해당 존에 파드를 추가해주는 것은 보장할 수 없다.
커스텀 휴리스틱
휴리스틱은 보통 가능한 최선책, 러프한 접근법 등을 뜻하는 표현으로 이해하고 있다.
적절하게 번역할 단어를 잘 못 찾겠다.
모든 케이스에 잘 들어맞는 해결책은 존재하지 않을 것이다.
그래서 관련한 설정을 1.27부터 커스텀할 수 있도록 지원하려한다는 듯..?
관련 문서
이름 | noteType | created |
---|