Späť na blog

Kubernetes APF vyhladovanie: keď jeden controller zablokuje kubectl

Symptómy sú zradné, lebo na prvý pohľad to vyzerá ako “Kubernetes je dnes divný”:

  • kubectl get pods visí alebo timeoutuje.
  • Rollouty sa zaseknú (“waiting for rollout” donekonečna).
  • Controllery začnú logovať 429 a agresívne retryovať.
  • Nody vyzerajú OK, etcd vyzerá OK a CPU na API serveri nie je na 100%.

Toto som v produkcii riešil viackrát. Root cause nebol etcd ani “pomalý apiserver”. Bol to API Priority and Fairness (APF), ktoré robilo presne to, čo má — len naša konfigurácia spôsobila, že kritický control-plane traffic súťažil s hlučným controllerom.

Testované na: Kubernetes 1.29–1.31, managed control plane aj self-hosted kube-apiserver, Prometheus scrape apiserver metrík.

Prečo je to v roku 2026 častejšie

APF už nie je exotika. Multi-tenant clustre, GitOps, operátory a “všetko je controller” znamená, že API load je permanentne pod tlakom. Keď APF nesprávne izoluje traffic, dostanete failure mode, ktorý vyzerá ako náhodná flaky infra — až kým nepozriete APF metriky.

Incident (anonymizovaný)

Nasadil som interný controller (CRD) a mal som bug:

  • robil cluster-wide LIST na pody
  • robil cluster-wide LIST na secrety
  • v každom reconcile loope
  • a pri chybe retryoval agresívne

Súčasne sme mali custom FlowSchema pre “platform automation”, ktoré matchovalo väčšinu service accountov a mapovalo ich do jedného priority levelu s nízkymi shares.

Blast radius:

  • GitOps driftoval
  • HPA prestalo reagovať
  • kubectl bolo nespoľahlivé pre všetkých
  • pár workloadov malo restarty, lebo controllery nedokázali updateovať objekty

Constraint:

  • nemohol som “len zväčšiť apiserver” (managed control plane).
  • potreboval som mitigáciu v clustri: izolovať hlučný controller a ochrániť systémový traffic.

Timeline (čo som reálne spravil)

  • T-0: report: “kubectl visí”, rollouty stoja.
  • T+5m: vidím vlnu 429 v logoch controllerov a GitOps retry.
  • T+10m: grafujem APF rejections a vidím step change po mojom deployi.
  • T+20m: nájdem FlowSchema, ktoré sa správa ako catch-all pre service accounty.
  • T+30m: scale down hlučného controllera (okamžitá úľava).
  • T+45m: pridám FlowSchema + PriorityLevelConfiguration na izoláciu.
  • T+60m: rejections padnú, control plane sa stabilizuje, kubectl funguje.

Mechanizmus: ako vznikne APF vyhladovanie

APF je “traffic router” + limit na concurrency

APF matchuje request do FlowSchema, ktorá ho namapuje na PriorityLevelConfiguration.

Priority level kontroluje:

  • koľko “seats” (concurrency) flow dostane (assuredConcurrencyShares)
  • či sa requesty queueujú alebo rejectujú pri overload (Queuing vs Reject)

Vyhladovanie vznikne, keď “všetko matchuje rovnaký flow”

Najčastejšia chyba nie je “APF je broken”.

Je to:

  • FlowSchema matchuje viac trafficu, než ste chceli (typicky system:authenticated)
  • FlowSchema má vyššiu precedence, než si myslíte
  • kritický a bulk traffic skončí v jednom priority bucket-e

Keď sa bucket nasýti, klienti vidia:

  • queued latency (kubectl visí)
  • 429 rejections (controllery retryujú → ešte viac loadu)
  • nestabilné watch/list správanie (viac retry, viac LISTov)

Prečo retry spraví z incidentu špirálu

429 je “slušná” chyba. Veľa controllerov to berie ako transient a retryuje tvrdo. Vznikne loop:

  1. APF rejectne (429)
  2. controller retryuje → vyšší request rate
  3. APF rejectne viac
  4. control plane je nepoužiteľný pre všetko, čo zdieľa rovnaký priority level

Runbook: rýchla diagnostika APF starvation

Čo skontrolovať ako prvé

  1. Potvrdiť 429 a timeouty v čase
  • logy controllerov (GitOps, operátory, custom controllery)
  1. Pozrieť APF rejection metriky Ak scrapujete kube-apiserver metriky, toto je najrýchlejší signál.

PromQL príklady (mená/labely sa môžu jemne líšiť podľa distribúcie):

# Celkové APF rejections
sum(rate(apiserver_flowcontrol_rejected_requests_total[5m]))

# Rejections podľa priority levelu
sum by (priority_level) (rate(apiserver_flowcontrol_rejected_requests_total[5m]))

# Rejections podľa FlowSchema
topk(10, sum by (flow_schema) (rate(apiserver_flowcontrol_rejected_requests_total[5m])))
  1. Skontrolovať FlowSchema a precedence Mňa už párkrát zabil “catch-all” FlowSchema, ktoré matchovalo viac než sme chceli.
kubectl get flowschemas.flowcontrol.apiserver.k8s.io
kubectl get prioritylevelconfigurations.flowcontrol.apiserver.k8s.io

A potom:

kubectl get flowschemas.flowcontrol.apiserver.k8s.io -o yaml | grep -n "matchingPrecedence" -n

Hľadám:

  • nízke matchingPrecedence (vyššia priorita)
  • broad match na system:authenticated
  • pravidlá, ktoré omylom pokrývajú “všetko”

Ako potvrdiť hypotézu

A. Nájsť FlowSchema / priority level pod tlakom Z metrík si vyberiem top flow_schema alebo priority_level a otvorím ho:

kubectl get flowschema <name> -o yaml
kubectl get prioritylevelconfiguration <name> -o yaml

Otázky:

  • kto matchuje FlowSchema? (service accounts, groups, namespace)
  • je tam môj controller?
  • je tam queueing? aký je queue limit?
  • koľko shares má ten priority level?

B. Korelovať s konkrétnym hlučným klientom U mňa to bol nový controller. Ak nie je jasné, najrýchlejšie je:

  • dočasne scale down podozrivé controllery (GitOps, operátory)
  • sledovať, či rejections padnú
  • prestať, keď nájdete vinníka

Je to hrubé, ale pri incidente to funguje.

Bezpečné mitigácie (v poradí)

  1. Scale down / pauznúť hlučný controller Najrýchlejšie rozbije retry loop.
kubectl -n <ns> scale deploy/<controller> --replicas=0
  1. Znížiť jeho client-side QPS/burst Ak vlastníte kód, je to typicky drobná zmena. Ak nie, hľadajte konfiguráciu/env.

  2. Izolovať ho v APF FlowSchema matchujúca iba jeho service account + low-share PriorityLevelConfiguration.

  3. Opraviť ochranu systémového trafficu Ak váš APF config náhodne znížil prioritu system flow, opravte precedence a shares.

Rizikové mitigácie

  • Vypnúť APF (často nemožné na managed control plane; a viete si vyrobiť tvrdší meltdown).
  • Slepo zvyšovať shares všade
    • stratíte fairness a môžete odpáliť etcd load.
  • Reštartovať controllery ako “fix”
    • restart často zvýši list/watch a búrku zhorší.

Čo sme zmenili (konkrétne)

1) Izolácia controllera cez PriorityLevelConfiguration

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3
kind: PriorityLevelConfiguration
metadata:
  name: plc-noisy-controller
spec:
  type: Limited
  limited:
    assuredConcurrencyShares: 5
    limitResponse:
      type: Queuing
      queuing:
        queues: 16
        handSize: 4
        queueLengthLimit: 50

2) FlowSchema iba pre konkrétny service account

apiVersion: flowcontrol.apiserver.k8s.io/v1beta3
kind: FlowSchema
metadata:
  name: fs-noisy-controller
spec:
  matchingPrecedence: 2000
  priorityLevelConfiguration:
    name: plc-noisy-controller
  rules:
  - subjects:
    - kind: ServiceAccount
      serviceAccount:
        name: noisy-controller
        namespace: platform
    resourceRules:
    - apiGroups: ["*"]
      resources: ["*"]
      verbs: ["*"]
      namespaces: ["*"]
      clusterScope: true

3) Fix bug + rate limit v controlleri

Skutočný fix bol prestať robiť cluster-wide LIST v tight loope. APF izolácia ale zabezpečila, že jedna bugnutá verzia už nedokáže vyhladovať celý cluster.

Ako verifikovať

  1. APF rejections padnú
sum(rate(apiserver_flowcontrol_rejected_requests_total[5m]))
  1. kubectl je znova rýchly
time kubectl get ns >/dev/null
time kubectl get pods -A --request-timeout=10s >/dev/null
  1. controllery prestanú spamovať 429 GitOps aj kube-controller-manager sa upokoja.

Prevencia / guardrails

  • API QPS budgety per controller
  • APF pravidlá
    • žiadna FlowSchema nesmie matchovať system:authenticated bez review
    • zmeny precedence musia ísť cez diff review
  • Alerty
    • APF rejections > 0 dlhšie než N min
    • queue depth non-zero pre kritické priority levels
  • Game day
    • staging: nasimulovať hlučný controller a dokázať, že cluster ostane použiteľný

Súvisiace čítanie

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "Kubernetes APF vyhladovanie: keď jeden controller zablokuje kubectl". https://www.michal-drozd.com/sk/blog/kubernetes-api-priority-fairness-vyhladovanie/ (Publikované 14. novembra 2025).