Requests, Limits i QoS: jak Kubernetes klasyfikuje Pody
- data
- kategoria
- Containers
- także w
- Capacity & Performance
- czytanie
- 3 min / 647 słów
Mamy już trzy elementy układanki:
requests.cpu -> deklarowane zapotrzebowanie + względna waga
limits.cpu -> maksymalny CPU bandwidth
contention -> konkurencja o dostępne CPU
Kubernetes dokłada do tego jeszcze jedną warstwę:
QoS class
Każdy Pod otrzymuje jedną z trzech klas:
Guaranteed
Burstable
BestEffort
Nazwy brzmią tak, jakby bezpośrednio określały „jakość CPU”, ale to zbyt duże uproszczenie.
QoS class jest przede wszystkim klasyfikacją wynikającą z konfiguracji CPU i memory requests/limits. Kubernetes wykorzystuje ją między innymi przy podejmowaniu decyzji pod resource pressure.
W tym tekście interesuje nas wyłącznie to, jak klasy powstają i co oznaczają w kontekście CPU.
BestEffort
Najprostszy przypadek:
containers:
- name: app
image: example/app
Brak:
requests.cpu
limits.cpu
requests.memory
limits.memory
Pod otrzymuje klasę:
BestEffort
Żeby Pod był BestEffort, żaden jego kontener nie może posiadać requestu ani limitu CPU lub memory.
To nie oznacza jednak:
Pod nie może używać CPU
Wręcz przeciwnie.
Jeżeli node ma wolną moc obliczeniową, BestEffort może ją wykorzystywać.
Na przykład:
node = 16 CPU
inne workloady używają = 4 CPU
BestEffort workload chce = 8 CPU
może realnie dostać te 8 CPU, jeśli nic innego mu w tym nie przeszkadza.
BestEffort nie oznacza więc:
mało CPU
Oznacza raczej:
brak zadeklarowanego CPU request
brak CPU limit
Burstable
Teraz ustawmy:
resources:
requests:
cpu: 500m
Nie ma limitu.
Pod nie spełnia kryteriów Guaranteed, ale posiada konfigurację zasobów.
Trafia więc do:
Burstable
Do klasy Burstable trafia Pod, który:
nie jest Guaranteed
oraz posiada przynajmniej jeden request lub limit CPU albo memory.
Przykład:
resources:
requests:
cpu: 500m
limits:
cpu: "2"
również będzie typowym przypadkiem Burstable, zakładając że pozostała konfiguracja Poda nie spełnia warunków Guaranteed.
W kontekście CPU oznacza to, że możemy mieć:
request = 500m
limit = 2 CPU
czyli:
scheduler accounting = 0.5 CPU
relative CPU weight = wynikający z 500m
maksymalny bandwidth = 2 CPU
QoS nie zastępuje requestów i limitów.
Jest klasyfikacją zbudowaną na ich podstawie.
Guaranteed
Teraz konfiguracja:
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "1"
memory: 1Gi
Jeżeli każdy kontener w Podzie ma dodatnie requesty i limity CPU oraz memory, a dla każdego zasobu:
request == limit
Pod otrzymuje klasę:
Guaranteed
Czyli dla CPU:
requests.cpu = limits.cpu
ale samo to nie wystarcza.
Memory również musi spełniać odpowiednie warunki.
To ważny detal.
Konfiguracja:
resources:
requests:
cpu: "1"
limits:
cpu: "1"
nie oznacza automatycznie:
Guaranteed
jeżeli nie spełniliśmy również wymagań dotyczących memory.
QoS jest klasyfikacją całego Poda, nie wyłącznie CPU.
Guaranteed nie oznacza dedicated CPU
Nazwa może sugerować:
Guaranteed
=
mam zagwarantowany fizyczny core
Nie.
Pod:
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "1"
memory: 1Gi
może być Guaranteed, ale nadal działać na współdzielonych CPU.
Z punktu widzenia mechanizmów, które już znamy:
request = 1 CPU
limit = 1 CPU
oznacza:
scheduler uwzględnia 1 CPU capacity
workload posiada CPU weight wynikający z requestu
workload posiada bandwidth limit odpowiadający 1 CPU
Nie oznacza:
CPU #7 należy tylko do tego Poda
Ekskluzywne CPU wymagają dodatkowego mechanizmu CPU Managera i odpowiedniej konfiguracji node'a. Kubernetes dokumentuje, że Pody Guaranteed mogą kwalifikować się do exclusive CPUs przy użyciu polityki static, ale sama klasa Guaranteed nie tworzy takiego przypisania.
Trzy Pody na jednym CPU
Załóżmy jeden CPU i trzy workloady.
Pod A - BestEffort
resources: {}
Pod B - Burstable
resources:
requests:
cpu: 500m
Pod C - Guaranteed
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "1"
memory: 1Gi
Nie należy myśleć:
Guaranteed zawsze pierwszy
Burstable potem
BestEffort na końcu
Linux scheduler nie interpretuje nazw Kubernetes QoS w ten sposób.
Runtime działa na mechanizmach cgroups.
Pod B ma CPU weight wynikający z requestu:
500m
Pod C ma weight wynikający z:
1 CPU
i dodatkowo posiada CPU bandwidth limit:
1 CPU
Pod A nie posiada zadeklarowanego CPU requestu, więc jego pozycja przy contention będzie odpowiednio słabsza zgodnie z konfiguracją cgroups wygenerowaną przez kubelet.
To nie jest klasyczny scheduler priority:
Guaranteed > Burstable > BestEffort
QoS class sama w sobie nie jest odpowiednikiem nice, scheduler priority ani kolejki priorytetowej CPU.
Najważniejsze: QoS to wynik konfiguracji
Dobry model mentalny:
requests + limits
|
v
Kubernetes wylicza QoS class
|
v
Guaranteed / Burstable / BestEffort
Nie odwrotnie.
Nie konfigurujemy bezpośrednio:
qosClass: Guaranteed
Kubernetes wylicza klasę na podstawie konfiguracji zasobów.
Można ją zobaczyć na przykład w:
kubectl get pod <pod> -o jsonpath='{.status.qosClass}'
i otrzymać:
Guaranteed
albo:
Burstable
czy:
BestEffort
Klasa jest przypisywana przy utworzeniu Poda i pozostaje jego klasą przez lifetime Poda; obecne mechanizmy in-place resize nie pozwalają zmianą zasobów przejść do innej klasy QoS.
Jedna pułapka: limit bez jawnego requestu
Załóżmy:
resources:
limits:
cpu: "1"
bez:
requests:
cpu:
Nie należy automatycznie zakładać:
CPU request = 0
Jeżeli request nie został podany, Kubernetes może ustawić request na wartość limitu. W efekcie kontener może skończyć z:
request = 1 CPU
limit = 1 CPU
To ma znaczenie zarówno dla schedulera, jak i dla ustalania QoS.
Dlatego analizując działający Pod, warto patrzeć na finalną konfigurację zasobów, a nie wyłącznie na fragment manifestu, który pamiętamy z repozytorium.
QoS nie zmienia znaczenia requestu ani limitu
To ważne, żeby nie tworzyć kolejnej abstrakcji tam, gdzie jej nie ma.
Dla Burstable:
requests:
cpu: 500m
limits:
cpu: "2"
nadal:
500m
jest requestem, a:
2 CPU
jest bandwidth limitem.
Dla Guaranteed:
requests:
cpu: "1"
limits:
cpu: "1"
nadal są to te same dwa mechanizmy.
Po prostu ich wartości są równe.
QoS nie tworzy nowego sposobu schedulowania CPU.
Model mentalny
Najprościej:
BestEffort
request = brak
limit = brak
Burstable
jakakolwiek konfiguracja CPU/memory,
która nie spełnia Guaranteed
Guaranteed
dla każdego kontenera:
CPU request = CPU limit > 0
Memory request = Memory limit > 0
W kontekście CPU najważniejsze jest jednak:
QoS class
!=
CPU priority class
oraz:
Guaranteed
!=
dedicated CPU
Request nadal odpowiada za deklarowaną capacity i względną wagę.
Limit nadal odpowiada za maksymalny CPU bandwidth.
QoS jest warstwą klasyfikacji Kubernetes zbudowaną na tych ustawieniach - nie trzecim, niezależnym mechanizmem przydzielania CPU.