API Priority and Fairness
개요
API Priority and Fairness는 kube-apiserver로 들어가는 요청에 섬세한 제한을 거는 기능이다.
크게 보면 rate limiting이지만, APF는 최대 요청 제한에 대한 설정을 더 세밀하게 할 수 있도록 지원하며
또한 공정하게 처리될 트래픽을 분배하는 큐 알고리즘을 도입하여 특정 요청이 계속 처리되지 못하는 상황을 방지한다.
APF는 watch 요청에 대해서만 적용된다는 점 참고하자.
exec이나 log와 같은 커넥션이 긴 다른 요청에 대해선 간섭하지 않는다.
APF는 --enable-priority-and-fairness
인자로 전달하여 설정하며 기본 활성화 상태이다.
또한 관련 리소스가 존재하며 현재 v1 상태이다.
재귀적 서버 시나리오
apf는 재귀 서버 시나리오에서 신중하게 쓰여야 한다.
가령 A에서 요청을 처리하며 B에 부가적 요청을 날린다고 쳐보자.
또 B에서는 요청을 처리하며 A에게 더 부가적인 요청을 날릴 수도 있다.
이럴 때 APF는 해당 모든 요청들에 적용되기에, 지나치게 전체 요청 처리에 제약을 야기할 수 있다.
다음의 경우가 대표적이다.
- 어드미션 웹훅을 날릴 때
- API Aggregation Layer을 이용할 때
이럴 때는 후속 요청에 대해 더 중요도를 높게 매겨서 제한에 덜 걸리게 만드는 것도 하나의 방법이 된다.
아니면 재귀적인 요청이 오가는 서버에 대해서는 제한을 걸지 않는 것도 좋다.
개념
동시성, 중요도(Priority Level)
단위 시간 동안 서버가 동시에 처리할 수 있는 양(concurrency capacity)은 정해져 있다.
이 수용량은 api 서버 인자 --max-requests-inflight
, --max-mutating-requests-inflight
를 통해 설정할 수 있다.
아무튼 문서에서는 동시성의 기본 단위를 seat이라 하는데, 이는 열차나 비행기에 전체 탑승 인원이 "좌석"을 기반으로 결정된다는 점에서 착안된 용어다.
예를 들면 100개의 자리가 있다면 한번에 서버는 100개의 요청을 처리할 수 있을 것이다.
물론 어떤 요청은 처리에 시간이 오래 걸리거나 많은 연산을 수행해야 하여 더 많은 자리를 차지할 수도 있다.
그래서 자리는 그저 동시성 수용량을 부분으로 쪼개 이해하기 위한 개념 정도로 생각하면 될 듯하다.
APF는 이 동시성 수용량을 쪼개 그룹 별로 할당하는 역할을 수행한다.
API 서버로 들어오는 요청의 속성 기반으로 분류한 뒤, 분류된 그룹에 따라 정책을 적용하는 방식으로 분류된 그룹을 Priority Level(중요도라 번역하겠다)이라 부른다.
중요도는 요청을 동시에 처리할 수 있는 공간을 나눠가지는 단위이기에, 다른 중요도에 속한 요청들은 서로의 자원을 뺏는 일이 발생하지 않는다.
그래서 파드에서 오는 요청이 많더라도 파드 스케줄링을 위한 kubelet의 요청에는 영향이 가지 않도록 세팅하는 것이 가능하다.
중요도 별 동시성 제한은 주기적으로 조정된다.
가령 활용도가 낮은 중요도 그룹의 동시성 수용량을 줄이고 부하가 큰 중요도 그룹의 수용량을 넓혀준다!
조정이 일어나는 범위에 대해서는 제한을 두거나 설정하는게 가능한데, 아래 리소스에서 자세히 보도록 한다.
watch 요청에 대한 실행 시간 조정
APF는 watch 요청에 대해서만 신경을 쓰는데, 위 동시성을 다루는 개념에서 watch 동작은 추가적인 고려사항을 동반한다.
하나의 watch 요청은 지속적이고, 또 처리하는 데이터의 개수도 다를 수 있는데 얼마나 자리를 선점해야 할까?
APF에서는 watch 요청 초기에 보내지는 많은 알림을 일단 한 자리로 간주한다.
근데 watch를 하게 되면 감시하고자 하는 리소스에 새로운 오브젝트가 생성, 삭제가 될 때마다 알림이 발생한다.
APF에서는 이러한 쓰기 작업으로 인해 알림이 발생하는 것도 카운팅하는데, 대신 쓰기 작업이 일어나는 것을 기준으로 보내야 하는 알림 개수와 자리 선점 시간을 추가적으로 자리로서 고려한다.
큐
중요도가 다른 그룹 간에는 서로 경합이 일어나지 않지만, 중요도 내의 여러 요청은 자원을 나눠써야 하는 입장이다.
또한 한 중요도에 많은 요청이 몰리면 당연히 버려지는 요청도 생기게 될 것이다.
APF는 이에 대해 큐를 사용한다.
특히 공정한 큐 알고리즘을 사용해 중요도 내 여러 요청들이 공평하게 수행될 수 있도록 돕는다.
구체적으로는 셔플 샤딩(큐에 해싱 나머지 연산을 이용해 배치)을 사용하는데, 알고리즘은 중요도 별로 유형을 달리 할 수 있다.
이러한 튜닝을 통해 큐에서 메모리를 더 사용하도록 공간을 넓힐 수도 있고, 아예 큐를 두지 않는 것도 가능하다.
각 요청은 요청자, 대상 네임스페이스 등을 구분자로 매칭되는 flow schema로 식별되는 플로우을 하나씩 지정받는다.
같은 중요 레벨의 플로우들은 거의 같은 가중치를 부여한다.
한 플로우에서 요청을 분류하면 그 다음에 큐에 배치한다.
- FlowSchema - 요청의 속성을 기반으로
들어오는 요청은 FlowSchema와 중요도 레벨에 따라 속성 기반 분류된다.
중요도 레벨은 동시성 제한을 통해 격리 층위를 부여해, 다른 중요도 레벨 간 기아 상태가 발생하는 것을 막는다.
여기에 공정한 큐 알고리즘이 들어가 다른 플로우 간 경합을 막아준다.
그리고 큐를 통해 순간 부하에 의한 요청 실패를 방지해준다.
관련 리소스
위의 개념을 바탕으로 APF에서는 두 가지 리소스를 제공한다.
PriorityLevelConfiguration
이건 중요도 그룹을 지정하는 리소스로, 그룹 별 동시성 수용량, 큐 튜닝 등의 설정을 할 수 있다.
중요도 수치를 설정할 수도 있는데, 이건 전체 동시성 양에서 비율적으로 산정된다.
그래서 이를 명목 상(nominal)의 숫자라고도 표현한다.
- type - 동시 처리량을 초과하는 요청을 어떻게 할지 지정
- Reject - 429 에러(Too Many Requests)
- Queue - 임계치 이상이면 큐에 적재
다음은 큐 관련 설정이다.
- queues - 큐의 개수를 설정하는 것으로 이것을 늘리면 다른 플로우간 충돌률을 줄인다.
- 메모리 사용량이 늘어난다.
- 1은 큐잉 로직을 비활성화한다.
- queueLengthLimit - 큐 사이즈 자체를 늘린다.
- handSize - 다른 플로우들과 전체 동시성 상에서 발생할 충돌 가능성을 조정한다.
- 핸드사이즈가 크면 두 개별 플로우 간 충돌을 줄인다.
- 대신 api 서버가 한번에 감당 가능한 플로우 개수를 줄인다.
- 단일 플로우에서 큐에 들어갈 수 있는 최대 요청 개수는 handSize * queueLengthLimit
FlowSchema
요청의 속성을 조건으로 중요도 그룹에 매핑하는 설정 리소스이다.
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: health-for-strangers
spec:
matchingPrecedence: 1000
priorityLevelConfiguration:
name: exempt
rules:
- nonResourceRules:
- nonResourceURLs:
- "/healthz"
- "/livez"
- "/readyz"
verbs:
- "*"
subjects:
- kind: Group
group:
name: "system:unauthenticated"
- matchingPrecedence - 정책 적용 순위. 낮은 녀석부터 요청에 조건 검증이 일어나며, 첫번째로 부합한 조건에 맞춰 중요도가 결정된다.
- rules - 실제 조건을 작성하는 필드로 여기에 한 조건이라도 매칭되면 적용된다.
distinguisherMethod.type은 요청이 플로우에 들어갈 때의 타입.
ByUser - 다른 유저의 수용량을 뺏지 않음
ByNamespace - 네임스페이스별 수용량을 뺏지 않음
설정 안하면 그냥 설정 안된다.
그럼 모든 요청이 하나의 단일 플로우로 인식될 것이다.
기본 APF
api 서버에는 mandatory, suggested 두 유형으로 먼저 APF 기능이 들어가 있다.
mandatory
여기에는 2개의 중요도가 들어있다.
- exempt
- 플로우 제어에 들어가지 않고 바로 처리되는 중요도.
system:masters
그룹의 요청은 이 중요도를 받는다.
- catch-all
- catch-all 플로우 스키마에 해당하는 중요도.
- 이걸 직접 쓰기보다는 커스텀해서 쓰자.
- 이건 굉장히 공유도 적고 큐잉을 하지 않는다.
suggested
- node-high
- 노드 헬스체크용
- system
system:nodes
그룹인데 헬스체크는 아닌 요청. 가령 kubelet에서 스케줄링된 파드 조회
- leader-election
- 내장 컨트롤러들의 요청. 가령 endpoint, configmap, lease 관리
- workload-high
- 내장 컨트롤러의 다른 요청들
- workload-low
- 서비스 어카운트로부터 오는 요청
- global-default
- 나머지 모든 요청
하위 문서
이름 | is-folder | index | noteType | created |
---|---|---|---|---|
컨트롤러 | - | - | knowledge | 2024-09-12 |
CRD | - | - | knowledge | 2024-09-14 |
API Aggregation Layer | false | - | knowledge | 2024-12-29 |
CNI | true | - | knowledge | 2025-01-08 |
kubeconfig | false | - | knowledge | 2025-01-13 |
설치 | false | - | knowledge | 2025-01-14 |
CRI | true | - | knowledge | 2025-02-24 |
CEL | false | - | knowledge | 2025-03-17 |
쿠버네티스 API 구조 | false | - | knowledge | 2025-03-19 |
오브젝트 | true | 0 | knowledge | 2024-12-23 |
코어 컴포넌트 | true | 0 | knowledge | 2025-03-19 |
클러스터 성능 최적화 | false | 13 | knowledge | 2025-08-30 |
API Priority and Fairness | false | 14 | knowledge | 2025-08-30 |
관련 문서
EXPLAIN - 파생 문서
이름0 | related | 생성 일자 |
---|
기타 문서
Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완이름0 | 코드 | 타입 | 생성 일자 |
---|