Skalowanie podów w Kubernetesie. Porady praktyczne
Słyszałeś o „skalowaniu Kubernetesa”, ale nigdy nie dotknąłeś tego zagadnienia? Przeczytaj nasz praktyczny poradnik o skalowaniu podów w Kubernetesie!
Niezależnie od rodzaju instalacji, jeśli Wasz klaster zlokalizowany jest w chmurze, prawdopodobnie korzystacie zarówno z możliwości skalowania węzłów (czyli możliwości automatycznych zmian ilości maszyn w klastrze) jak i mechanizmu pozwalającego na automatyczne zmiany liczby Pod’ów danego Deployment’u (przepraszam z góry za słownictwo angielskie, ale jakoś nie przyjęło się żadne polskie określenie – choć Informatyczny Słownik Angielsko-Polski proponuje tutaj oczywiste słowo “wdrożenie”, jakoś nie pasuje ono w tym kontekście) w zależności od ich zużycia zasobów, odpowiednio są to:
● Cluster Autoscaler (CA),
● Horizontal Pod Autoscaler (HPA).
W środowisku produkcyjnym realizacja HPA jest podstawowym filarem działania mikroserwisów i możliwości ich automatycznego reagowania na zwiększenie ilości żądań – co jest zresztą jedną z podstawowych zalet i powodów używania Kubernetesa w ogóle. Uzupełnia się to dość dobrze z równoległym działaniem Cluster Autoscaler (choć powiedzmy dla pełności obrazu, że w przypadku klastrów on-premise nierzadko rezygnuje się z używania CA i proces dodawania nowych maszyn pozostawia się do decyzji i wykonania w sposób manualny administratorowi). Niemniej jednak w tym artykule skupiamy się na realizacji w chmurze i problemach z realizacją HPA, chociaż, jak zobaczycie poniżej, pewne zasady działania HPA zawsze miały związek z realizacją CA (lub jej brakiem).
Konfiguracja HPA – podstawy
Konfiguracja HPA z założenia jest dość prostym zadaniem. Dany Deployment uruchamiamy, podając w definicji liczbę replik, które powinny być uruchomione przez klaster, a do tego dla HPA dodajemy informację o:
– minimalnej liczbie replik, które powinny być uruchomione,
– maksymalnej liczbie replik, które mogą być uruchomione,
– średniej utylizacji CPU (procentowo) do utrzymania przez repliki.
O ile minimalna/maksymalna liczba replik nie wymaga komentarza, o tyle trzeci parametr jest interesujący. Procentowa wartość wymaga bowiem pułapu – ten obliczany jest na podstawie tzw. poziomu żądań (Requests), czyli wartości zdefiniowanej dla Deploymentu.
W definicji zasobu w pliku YAML przedstawia się to następująco:
Dla powyższej (przykładowej) definicji ustawienie HPA mogłoby w takim razie zostać zdefiniowane w sposób jak pokazano na przykładzie:
Oczywiście bazowanie na samym użyciu CPU ogółem nie wystarcza – w grę może wchodzić skalowanie na podstawie zużycia pamięci (druga z podstawowych metryk dostępnych “z pudełka”), ale najczęściej potrzebne jest monitorowanie innych metryk i skalowanie również na podstawie ich wartości. Jest to określane mianem “HPA z niestandardowymi metrykami“ (HPA with Custom Metrics) i szczerze mówiąc, trudno wyobrazić sobie wdrożenie produkcyjne bez takiej możliwości.
Konfiguracja HPA z metrykami niestandardowymi
Z zasady, skalowanie można oprzeć o metryki:
– podstawowe (dostępne poprzez API metrics.k8s.io),
– niestandardowe pochodzące od podów,
– niestandardowe pochodzące od innych obiektów,
– zewnętrzne (inne metryki dostępne w systemie monitoringu).
Metryki niestandardowe mogą wymagać bardziej zaawansowanego monitoringu klastra, ale pozwalają na bardzo elastyczne skalowanie. W przypadku użycia większej ilości metryk zamiast podawania pojedynczego warunku (jak powyżej), konstruuje się listę metrics – w niej umieszczając konkretne metryki.
Należy przy tym zwrócić uwagę, że w poniższym przykładzie zmieniono sposób odnoszenia się do pożądanych wartości – w miejsce procentowo oczekiwanego poziomu AverageUtilization wprowadzono AverageValue, jak również odpowiednio target.averageUtilization zmienia się wtedy na target.averageValue. Metryki niestandardowe wymagają wartości bezpośrednich.
Przykład poniżej obrazuje użycie po jednej z każdego rodzaju metryk:
Użyta metryka typu obiektowego nie jest powiązana z konkretnym Podem, ale z niezależnym obiektem w klastrze (tutaj Ingress) – stąd powyższy przykład ustawia HPA na utrzymanie przez każdy Pod średniej 1000 pakietów na sekundę, ale 5 tysięcy żądań na sekundę dotyczy już sumy dla wszystkich podów obsługujących dany Ingress.
Metryki zewnętrzne (External) mogą być pobierane z jakichkolwiek zewnętrznych systemów i ich możliwości są praktycznie nieograniczone – może to być na przykład wywołanie choćby którego z GoogleAPI (jeśli nasz klaster jest zrealizowany w GKE). Jest to temat bardzo obszerny i przykłady metryk zewnętrznych mogą stanowić temat osobnego artykułu.
W przykładzie powyżej metrykę zewnętrzną oparto o oczekiwaną wartość długości kolejki AWS SQS (skalowanie się odbędzie, gdy długość kolejki przekroczy oczekiwaną) i zrealizowano poprzez definicję w K8S bezpośredniej metryki z AWS CloudWatch w sposób następujący:
HPA wszystkie warunki będzie ewaluował po kolei, w turach. Wybierze natomiast najwyższą wartość liczby replik, jaką obliczy na podstawie każdej z podanych metryk.
Requesty i Limity – konfiguracja
Podstawowe działanie HPA oparte jest na wartościach utylizacji CPU oraz pamięci, które podawane są poprzez liczbę dotyczącą żądań (Requests) i ograniczeń (Limits). Jednym z typowych błędów jest brak zrozumienia, czego dotyczą obydwie wartości.
Zgodnie z dokumentacją, Requests określa poziom, którego Pod nie powinien przekroczyć (ale może). Limits z kolei są wartością twardą – określają poziom zasobów, jakiego dany Pod nie może przekraczać. Należy tutaj zaznaczyć – nie oznacza to, że klaster w jakiś magiczny sposób nie przydzieli dodatkowych zasobów! Przekroczenie poziomu określonego przez Limit spowoduje, że Pod znajdzie się wysoko na liście kandydatów do usunięcia (podobnie stanie się z podami, które nie posiadają określonych limitów). Oznacza to tylko tyle, że w wypadku uruchomienia OOMKillera, prawdopodobnie zostanie sterminowany. Nie oznacza to, że nie będzie wcześniej pobierał zasobów ponad swój limit.
Jak dobrać poziomy Limits i Requests? Najlepszą praktyką jest uruchomienie Deploymentu bez ograniczeń, obserwacja jak zachowuje się podczas jałowego działania, a także podczas regularnego obciążenia, wreszcie podczas szczytowych wartości obciążeń. Nie jest jednak prosto określić, w jakim stanie obecnie pod się znajduje, jeśli mamy niewiele replik. Sytuacja zmienia się przy większej liczbie replik. Rozważmy poniższy przykład:
Ewidentnie część podów jest w stanie jałowym. Zależnie od typu aplikacji (być może pody czekają na jakiś warunek) sytuacja może się zmienić już po krótkim czasie:
Analizując powyższy przykład, można dojść do wniosku, że pod należy skonfigurować z Requestami 150Mi dla pamięci oraz mniej więcej 20m dla CPU (większe wartości oznaczają, że pody są zajęte pracą), a Limity powinny wynosić odpowiednio 250Mi i 200m (bo więcej pod nie powinien skonsumować, nawet jak jest aktywny).
W praktyce, należy wziąć pod uwagę, że podczas startu poda kontener może wykonywać dodatkowe prace i na samym początku zabiera więcej zasobów – co oznacza konieczność podniesienia zarówno limitów (żeby kontenera nie zabiło na samym początku), jak i requestów (żeby klaster nie dostawał sygnału, że start kontenera powoduje konieczność kolejnego skalowania – to prowadzi do sygnałów, że skalowanie następuje zbyt szybko).
W podanym przykładzie administrator (Kuizinas, “Mistake that cost thousands (Kubernetes, GKE)”, Medium.com) był zmuszony podnieść Limits na 500m – nie wiedział jednak dlaczego. Mam nadzieję, że dzięki powyższym rozważaniom skonfigurujecie swoje klastry bez problemów.
Happy helming!
dr inż. Maciej Rostański
DevOps Engineer, Hostersi
Pytania? Skontaktuj się z nami
Zobacz również:
Wysoka dostępność serwisu. Jak ją ustalić i wyliczyć?
Jak używać pliku stanu zdalnego remotestate w środowisku Terraform?
Rewolucja w bezpiecznych połączeniach VPN z WireGuard
Chmura obliczeniowa nie taka straszna. Wprowadzenie do Amazon Web Services
Tworzenie nowego konta AWS i zasobów przy użyciu opcji multiple provider w środowisku Terraform