쿠버네티스 운영 실무 · API 서버가 429 를 돌려준다 · 실습
오퍼레이터 하나가 클러스터 전체를 느리게 만들었다
목표
기본 FlowSchema 와 PriorityLevelConfiguration 을 순서대로 읽고, 폭주하는 서비스 어카운트를 위한 격리 칸을 만들어 분류가 실제로 바뀌는 것을 응답 헤더로 증명한 뒤, 우선순위 값이 겹쳤을 때의 규칙과 exempt 남용을 막는 감시 스크립트, 그리고 좌석 수 지표까지 확인한다.
왜 중요한가
API 서버는 들어오는 모든 요청을 분류하고 격리한다. FlowSchema 가 분류표이고, 요청은 matchingPrecedence 가 작은 것부터 대조해 처음 맞는 하나로 결정된다. 그 스키마가 가리키는 PriorityLevelConfiguration 이 그 칸의 폭 — 동시에 몇 개를 실행할 수 있고 넘치면 줄을 세울지 바로 거절할지 — 를 정한다. 이 구조가 중요한 이유는 하나다. 폭주하는 클라이언트가 있을 때 누가 굶는지를 설계할 수 있기 때문이다. 분류표를 읽을 줄 모르면 429 가 쌓이는 날 할 수 있는 일이 재시작뿐이고, 그건 같은 폭주를 다시 맞겠다는 뜻이다.
단계
1. 클러스터가 기본으로 넣어 둔 FlowSchema(이 실습에서 만들 것들과 구분하기 위해 이름이 ops- 나 zz-ops- 로 시작하지 않는 것으로 정의합니다)를 전부 /root/ops-apf/flowschemas.tsv 에 저장하세요 — <matchingPrecedence> 탭 <이름> 탭 <우선순위 등급 이름> 한 줄씩이고, 요청이 심사되는 순서대로 정렬합니다(숫자 정렬입니다).
2. 클러스터가 기본으로 넣어 둔 PriorityLevelConfiguration(이름이 ops- 로 시작하지 않는 것)을 전부 /root/ops-apf/levels.tsv 에 저장하세요 — <이름> 탭 <type> 탭 <nominalConcurrencyShares> 탭 <limitResponse.type> 한 줄씩, 이름 오름차순입니다. Exempt 등급처럼 값이 없는 칸은 - 로 적으세요.
3. 네임스페이스 ops-apf 를 만들고 서비스 어카운트 셋을 만드세요 — widget-operator, plain-reader, bulk-writer. 그다음 /root/ops-apf/classify.sh 를 만드세요 — 인자로 받은 서비스 어카운트를 흉내 내어 요청 하나를 보내고 응답 헤더의 X-Kubernetes-Pf-Flowschema-Uid 로 걸린 FlowSchema 이름만 표준출력에 찍습니다. plain-reader 로 돌려 그 결과를 /root/ops-apf/baseline.tsv 에 한 줄로 저장하세요 — plain-reader 탭 <스키마 이름>.
4. /root/ops-apf/ops-noisy.yaml 에 PriorityLevelConfiguration ops-noisy 를 쓰세요 — type: Limited, nominalConcurrencyShares: 5, lendablePercent: 50, borrowingLimitPercent: 20, limitResponse 는 type: Queue 이고 queuing 은 queues: 16, handSize: 4, queueLengthLimit: 50 입니다. 적용하세요.
5. /root/ops-apf/ops-noisy-operator.yaml 에 FlowSchema ops-noisy-operator 를 쓰세요 — matchingPrecedence: 950, priorityLevelConfiguration.name 은 ops-noisy, distinguisherMethod.type 은 ByUser, 규칙은 subject 가 kind: ServiceAccount 로 ops-apf 네임스페이스의 widget-operator 이고 resourceRules 는 verbs·apiGroups·resources·namespaces 를 모두 * 로 둡니다. 적용하세요.
6. classify.sh 를 widget-operator 와 plain-reader 로 각각 돌려 결과를 /root/ops-apf/classified.tsv 에 두 줄로 저장하세요 — <어카운트 이름> 탭 <스키마 이름> 이고 어카운트 이름 오름차순입니다.
7. /root/ops-apf/ops-bulk-a.yaml 과 /root/ops-apf/zz-ops-bulk-b.yaml 에 FlowSchema 두 개를 쓰세요. 둘 다 ops-apf 의 서비스 어카운트 bulk-writer 를 대상으로 하고 ops-noisy 를 가리키며 resourceRules 는 모두 * 입니다. ops-bulk-a 는 matchingPrecedence: 960·distinguisherMethod.type: ByUser, zz-ops-bulk-b 는 matchingPrecedence: 940·distinguisherMethod.type: ByNamespace 입니다. 둘 다 적용한 뒤 classify.sh bulk-writer 의 결과를 /root/ops-apf/precedence.tsv 에 두 줄로 저장하세요 — ops-bulk-a 탭 960 탭 <win 또는 lose>, zz-ops-bulk-b 탭 940 탭 <win 또는 lose>. 그리고 만약 두 스키마의 값이 똑같이 960 이었다면 어느 쪽이 이겼을지 그 이름만 /root/ops-apf/tie.txt 에 한 줄로 쓰세요.
8. /root/ops-apf/exempt-allow.txt 에 exempt 우선순위 등급을 가리켜도 되는 FlowSchema 이름을 한 줄씩 적으세요(지금 클러스터에 실제로 그런 스키마가 어떤 것들인지 먼저 확인하고, 그 이름들만 적습니다). 그리고 /root/ops-apf/exempt-guard.sh 를 만드세요 — exempt 를 가리키는 FlowSchema 를 모두 찾아 허용 목록에 있으면 OK <이름>, 없으면 VIOLATION <이름> 을 이름 순으로 표준출력에만 찍고, 위반이 하나라도 있으면 0 이 아닌 코드로 끝나야 합니다. 지금 상태에서 돌리면 위반이 없어야 합니다.
9. API 서버의 지표에서 apiserver_flowcontrol_nominal_limit_seats 를 읽어 /root/ops-apf/seats.tsv 에 저장하세요 — <우선순위 등급 이름> 탭 <좌석 수> 한 줄씩, 이름 오름차순입니다. ops-noisy 가 그 안에 있어야 합니다.
참고
matchingPrecedence는 값이 작을수록 먼저 심사됩니다.kubectl --v=8은 응답 헤더를 찍어 줍니다 — 분류 결과가 그 안에 UID 로 들어 있습니다.- APF 분류는 인가보다 먼저라서 403 이 돌아와도 헤더는 붙습니다.
- FlowSchema 의 status 에 Dangling 이 True 면 그 스키마는 무시되고 있는 것입니다.
- 흔한 실수: 문자열 정렬로 우선순위를 줄 세워 1000 이 2 보다 앞에 온다.
- 흔한 실수: 급하다고 exempt 를 가리키게 해서 서버를 지키는 장치를 통째로 없앤다.
- 참고: https://kubernetes.io/docs/concepts/cluster-administration/flow-control/
단계 9개
- 분류표를 우선순위 순으로 읽는다
- 각 칸의 폭을 읽는다
- 지금은 어느 칸으로 가는지 실측한다
- 폭주하는 오퍼레이터를 위한 칸을 만든다
- 그 칸으로 보내는 규칙을 쓴다
- 분류가 정말 바뀌었는지 헤더로 증명한다
- 같은 요청에 두 규칙이 걸릴 때
- exempt 를 가리키는 규칙을 감시한다
- 새 칸이 좌석을 실제로 받았는지 지표로 확인한다