10 typowych błędów podczas korzystania z Kubernetesa

Notatka. przeł.: Autorami tego artykułu są inżynierowie z małej czeskiej firmy Pipetail. Udało im się ułożyć wspaniałą listę [czasami banalnych, ale jednak] bardzo palących problemów i błędnych przekonań związanych z działaniem klastrów Kubernetes.

10 typowych błędów podczas korzystania z Kubernetesa

Przez lata korzystania z Kubernetesa pracowaliśmy z dużą liczbą klastrów (zarówno zarządzanych, jak i niezarządzanych – na GCP, AWS i Azure). Z biegiem czasu zaczęliśmy zauważać, że niektóre błędy ciągle się powtarzały. Jednak nie ma w tym żadnego wstydu: większość z nich zrobiliśmy sami!

W artykule zawarto najczęstsze błędy, a także wskazano, jak je poprawić.

1. Zasoby: żądania i limity

Pozycja ta zdecydowanie zasługuje na największą uwagę i pierwsze miejsce na liście.

Zwykle żądanie procesora albo nie jest w ogóle określony, albo ma bardzo niską wartość (aby umieścić jak najwięcej strąków w każdym węźle, jak to możliwe). W ten sposób węzły ulegają przeciążeniu. W okresach dużego obciążenia moc obliczeniowa węzła jest w pełni wykorzystywana, a określone obciążenie otrzymuje tylko to, czego „żądało” Dławienie procesora. Prowadzi to do zwiększonych opóźnień aplikacji, przekroczeń limitu czasu i innych nieprzyjemnych konsekwencji. (Przeczytaj więcej na ten temat w naszym innym niedawnym tłumaczeniu: „Limity procesora i agresywne throttling w Kubernetesie" - około. tłumacz.)

Najlepszy wysiłek (niezwykle nie Zalecana):

resources: {}

Niezwykle niskie żądanie procesora (bardzo nie Zalecana):

   resources:
      Requests:
        cpu: "1m"

Z drugiej strony obecność limitu procesora może prowadzić do nieuzasadnionego pomijania cykli zegara przez pody, nawet jeśli procesor węzła nie jest w pełni obciążony. Ponownie może to prowadzić do zwiększonych opóźnień. Kontrowersje wokół parametru trwają Limit CFS procesora w jądrze Linuksa i dławienie procesora w zależności od ustawionych limitów, a także wyłączanie przydziału CFS... Niestety, limity procesora mogą powodować więcej problemów, niż mogą rozwiązać. Więcej informacji na ten temat można znaleźć pod linkiem poniżej.

Nadmierna selekcja (przesadne zaangażowanie) problemy z pamięcią mogą prowadzić do większych problemów. Osiągnięcie limitu procesora pociąga za sobą pominięcie cykli zegara, natomiast osiągnięcie limitu pamięci wiąże się z zabiciem zasobnika. Czy kiedykolwiek obserwowałeś OOMkill? Tak, właśnie o tym mówimy.

Czy chcesz zminimalizować prawdopodobieństwo wystąpienia takiej sytuacji? Nie alokuj nadmiernie pamięci i korzystaj z gwarantowanej QoS (jakości usług), ustawiając żądanie pamięci na limit (jak w przykładzie poniżej). Więcej na ten temat przeczytasz w Prezentacje Henninga Jacobsa (główny inżynier w Zalando).

Pękający (większa szansa na OOMkilled):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Gwarancja:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Co będzie potencjalnie pomocne przy konfigurowaniu zasobów?

Z serwer metryk możesz zobaczyć bieżące zużycie zasobów procesora i wykorzystanie pamięci przez pody (i znajdujące się w nich kontenery). Najprawdopodobniej już go używasz. Po prostu uruchom następujące polecenia:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Pokazują jednak tylko bieżące wykorzystanie. Może dać ci przybliżone pojęcie o rzędzie wielkości, ale ostatecznie będziesz potrzebować historia zmian wskaźników w czasie (aby odpowiedzieć na pytania typu: „Jakie było szczytowe obciążenie procesora?”, „Jakie było obciążenie wczoraj rano?” itp.). Do tego możesz użyć Prometheus, DataDog i inne narzędzia. Po prostu pobierają metryki z serwera metryk i przechowują je, a użytkownik może o nie pytać i odpowiednio je wykreślać.

VerticalPodAutoskaler pozwala on zautomatyzować ten proces. Śledzi historię wykorzystania procesora i pamięci oraz konfiguruje nowe żądania i limity na podstawie tych informacji.

Efektywne wykorzystanie mocy obliczeniowej nie jest zadaniem łatwym. To jak granie w Tetrisa przez cały czas. Jeśli przepłacasz za moc obliczeniową przy niskim średnim zużyciu (powiedzmy ~10%), polecamy przyjrzeć się produktom opartym na AWS Fargate lub Virtual Kubelet. Zbudowane są w oparciu o model rozliczeniowy bezserwerowy/pay-per-usage, który w takich warunkach może okazać się tańszy.

2. Sondy żywotności i gotowości

Domyślnie kontrole żywotności i gotowości nie są włączone w Kubernetes. Czasem zapominają je włączyć...

Ale jak inaczej zainicjować ponowne uruchomienie usługi w przypadku błędu krytycznego? A skąd moduł równoważenia obciążenia wie, że pod jest gotowy do przyjęcia ruchu? A może poradzi sobie z większym ruchem?

Testy te są często mylone ze sobą:

  • Żywotność — sprawdzenie „przeżywalności”, które w przypadku niepowodzenia uruchamia ponownie kapsułę;
  • Gotowość — sprawdzenie gotowości, jeśli się nie powiedzie, odłączy poda od usługi Kubernetes (można to sprawdzić za pomocą kubectl get endpoints) i ruch do niego nie dociera, dopóki następna kontrola nie zakończy się pomyślnie.

Obie te kontrole WYKONYWANE PRZEZ CAŁY CYKL ŻYCIA PODA. To jest bardzo ważne.

Powszechnym błędnym przekonaniem jest to, że sondy gotowości są uruchamiane tylko przy uruchomieniu, aby wyważarka mogła wiedzieć, że kapsuła jest gotowa (Ready) i może rozpocząć przetwarzanie ruchu. Jest to jednak tylko jedna z możliwości ich wykorzystania.

Inną możliwością jest stwierdzenie, że ruch na kapsule jest nadmierny i przeciąża go (lub kapsuła wykonuje obliczenia wymagające dużej ilości zasobów). W tym przypadku pomaga sprawdzenie gotowości zmniejsz obciążenie kapsuły i „schłodź” ją. Umożliwia to pomyślne zakończenie kontroli gotowości w przyszłości ponownie zwiększ obciążenie kapsuły. W tym przypadku (jeśli test gotowości zakończy się niepowodzeniem) niepowodzenie testu żywotności przyniosłoby efekt przeciwny do zamierzonego. Po co restartować kapsułę, która jest zdrowa i ciężko pracuje?

Dlatego w niektórych przypadkach lepszy jest brak kontroli niż włączenie ich z niepoprawnie skonfigurowanymi parametrami. Jak wspomniano powyżej, jeśli sprawdzenie żywotności kopii sprawdzenie gotowości, to masz duże kłopoty. Możliwą opcją jest konfiguracja tylko test gotowościI niebezpieczna żywotność odłożyć na bok.

Obydwa typy kontroli nie powinny zakończyć się niepowodzeniem, gdy zawiodą typowe zależności, w przeciwnym razie doprowadzi to do kaskadowej (lawinowej) awarii wszystkich zasobników. Innymi słowy, nie rób sobie krzywdy.

3. LoadBalancer dla każdej usługi HTTP

Najprawdopodobniej masz w swoim klastrze usługi HTTP, które chcesz przekazać na zewnątrz.

Jeśli otworzysz usługę jako type: LoadBalancer, jego kontroler (w zależności od usługodawcy) zapewni i wynegocjuje zewnętrzny LoadBalancer (niekoniecznie działający na L7, ale raczej nawet na L4), co może mieć wpływ na koszt (zewnętrzny statyczny adres IPv4, moc obliczeniową, rozliczenie sekundowe ) ze względu na konieczność stworzenia dużej liczby takich zasobów.

W takim przypadku znacznie bardziej logiczne jest użycie jednego zewnętrznego modułu równoważenia obciążenia, otwierając usługi jako type: NodePort. Albo jeszcze lepiej, rozwiń coś takiego kontroler ruchu przychodzącego nginx (or Traefik), który będzie tym jedynym Port węzła punkt końcowy powiązany z zewnętrznym modułem równoważenia obciążenia i będzie kierować ruch w klastrze za pomocą ingres-Zasoby Kubernetesa.

Inne (mikro)usługi wewnątrzklastrowe, które współdziałają ze sobą, mogą „komunikować się” za pomocą usług takich jak IP klastra oraz wbudowany mechanizm wykrywania usług poprzez DNS. Po prostu nie używaj ich publicznego DNS/IP, ponieważ może to mieć wpływ na opóźnienia i zwiększać koszty usług w chmurze.

4. Autoskalowanie klastra bez uwzględnienia jego cech

Podczas dodawania węzłów do klastra i usuwania ich z klastra nie należy polegać na niektórych podstawowych metrykach, takich jak użycie procesora CPU w tych węzłach. Planowanie strąków musi uwzględniać wiele ograniczenia, takie jak powinowactwo pod/węzła, zmiany i tolerancje, żądania zasobów, QoS itp. Korzystanie z zewnętrznego autoskalera, który nie uwzględnia tych niuansów, może prowadzić do problemów.

Wyobraź sobie, że należy zaplanować określony pod, ale żądana jest/demontowana cała dostępna moc procesora i pod utknie w stanie Pending. Zewnętrzny autoskaler widzi średnie aktualne obciążenie procesora (a nie żądane) i nie inicjuje rozbudowy (skalowanie) - nie dodaje kolejnego węzła. W rezultacie ten pod nie zostanie zaplanowany.

W tym przypadku odwrotne skalowanie (skalowanie) — usunięcie węzła z klastra jest zawsze trudniejsze do wdrożenia. Wyobraź sobie, że masz moduł stanowy (z podłączoną pamięcią trwałą). Stałe wolumeny zwykle należą do określona strefa dostępności i nie są replikowane w regionie. Zatem jeśli zewnętrzny moduł automatycznego skalowania usunie węzeł z tym zasobnikiem, program planujący nie będzie mógł zaplanować tego zasobnika w innym węźle, ponieważ można to zrobić tylko w strefie dostępności, w której znajduje się magazyn trwały. Pod będzie utknął w stanie Pending.

Bardzo popularny w społeczności Kubernetes automatyczne skalowanie klastrów. Działa na klastrze, obsługuje API od największych dostawców usług w chmurze, uwzględnia wszystkie ograniczenia i może skalować się w powyższych przypadkach. Umożliwia także skalowanie przy zachowaniu wszystkich ustawionych limitów, oszczędzając w ten sposób pieniądze (które w przeciwnym razie zostałyby wydane na niewykorzystaną pojemność).

5. Zaniedbanie możliwości IAM/RBAC

Uważaj na używanie użytkowników IAM z trwałymi sekretami dla maszyny i aplikacje. Organizuj tymczasowy dostęp za pomocą ról i kont usług (konta serwisowe).

Często spotykamy się z faktem, że klucze dostępu (i sekrety) są zakodowane na stałe w konfiguracji aplikacji, a także zaniedbujemy rotację sekretów pomimo posiadania dostępu do Cloud IAM. W stosownych przypadkach używaj ról uprawnień i kont usług zamiast użytkowników.

10 typowych błędów podczas korzystania z Kubernetesa

Zapomnij o kube2iam i przejdź od razu do ról IAM dla kont usług (zgodnie z opisem w notatka o tej samej nazwie Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Jedna adnotacja. Nie takie trudne, prawda?

Nie nadawaj także uprawnień kontom usług i profilom instancji admin и cluster-adminjeśli tego nie potrzebują. Jest to nieco trudniejsze do wdrożenia, szczególnie w RBAC K8, ale zdecydowanie warte wysiłku.

6. Nie polegaj na automatycznej blokadzie powinowactwa w przypadku podów

Wyobraź sobie, że masz trzy repliki jakiegoś wdrożenia w węźle. Węzeł upada, a wraz z nim wszystkie repliki. Nieprzyjemna sytuacja, prawda? Ale dlaczego wszystkie repliki znajdowały się w tym samym węźle? Czy Kubernetes nie powinien zapewniać wysokiej dostępności (HA)?!

Niestety planista Kubernetesa z własnej inicjatywy nie przestrzega zasad odrębnej egzystencji (antypowinowactwo) dla strąków. Należy je wyraźnie określić:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

To wszystko. Teraz pody będą harmonogramowane na różnych węzłach (ten warunek jest sprawdzany tylko podczas planowania, a nie podczas ich działania - stąd requiredDuringSchedulingIgnoredDuringExecution).

Tutaj mówimy o podAntiAffinity w różnych węzłach: topologyKey: "kubernetes.io/hostname", - a nie o różnych strefach dostępności. Aby wdrożyć pełnoprawny HA, będziesz musiał głębiej zagłębić się w ten temat.

7. Ignorowanie budżetów PodDisruption

Wyobraź sobie, że masz obciążenie produkcyjne w klastrze Kubernetes. Okresowo węzły i sam klaster muszą zostać zaktualizowane (lub wycofane z eksploatacji). PodDisruptionBudget (PDB) to coś w rodzaju umowy gwarancji usług pomiędzy administratorami klastra a użytkownikami.

PDB pozwala uniknąć przerw w świadczeniu usług spowodowanych brakiem węzłów:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

W tym przykładzie Ty, jako użytkownik klastra, mówisz administratorom: „Hej, mam usługę zookeepera i niezależnie od tego, co zrobisz, chciałbym mieć zawsze dostępne co najmniej 2 repliki tej usługi”.

Możesz przeczytać więcej na ten temat tutaj.

8. Wielu użytkowników lub środowisk we wspólnym klastrze

Przestrzenie nazw Kubernetesa (przestrzenie nazw) nie zapewniają mocnej izolacji.

Powszechnym błędnym przekonaniem jest to, że jeśli wdrożysz obciążenie inne niż produkcyjne w jednej przestrzeni nazw, a obciążenie prod w innej, wówczas nie będą na siebie w żaden sposób wpływać... Jednak pewien poziom izolacji można osiągnąć za pomocą żądań/ograniczeń zasobów, ustawiania przydziałów i ustawiania klas priorytetów. Pewną „fizyczną” izolację w płaszczyźnie danych zapewniają powinowactwa, tolerancje, skazy (lub selektory węzłów), ale taka separacja jest dość trudne wprowadzić w życie.

Ci, którzy muszą połączyć oba typy obciążeń w tym samym klastrze, będą musieli poradzić sobie ze złożonością. Jeśli nie ma takiej potrzeby i Cię na nią stać jeszcze jedno skupisko (powiedzmy w chmurze publicznej), wtedy lepiej to zrobić. Pozwoli to osiągnąć znacznie wyższy poziom izolacji.

9. externalTrafficPolicy: Klaster

Bardzo często obserwujemy, że cały ruch wewnątrz klastra przechodzi przez usługę taką jak NodePort, dla której ustawiono domyślną politykę externalTrafficPolicy: Cluster... To znaczy, że Port węzła jest otwarty w każdym węźle w klastrze i możesz używać dowolnego z nich do interakcji z żądaną usługą (zestawem podów).

10 typowych błędów podczas korzystania z Kubernetesa

Jednocześnie realne pody powiązane z wyżej wymienioną usługą NodePort są zazwyczaj dostępne tylko na niektórych podzbiór tych węzłów. Innymi słowy, jeśli połączę się z węzłem, który nie ma wymaganego poda, przekieruje on ruch do innego węzła, dodanie chmielu oraz rosnące opóźnienia (jeśli węzły znajdują się w różnych strefach dostępności/centrach danych, opóźnienie może być dość duże; ponadto wzrosną koszty ruchu wychodzącego).

Z drugiej strony, jeśli określona usługa Kubernetes ma zestaw zasad externalTrafficPolicy: Local, wówczas NodePort otwiera się tylko na tych węzłach, na których faktycznie działają wymagane pody. Podczas korzystania z zewnętrznego modułu równoważenia obciążenia, który sprawdza stan (sprawdzanie stanu) punkty końcowe (jak to się robi AWS-ELB), On prześle ruch tylko do niezbędnych węzłów, co będzie miało korzystny wpływ na opóźnienia, potrzeby obliczeniowe, rachunki za wyjście (a zdrowy rozsądek podpowiada to samo).

Istnieje duże prawdopodobieństwo, że już używasz czegoś takiego Traefik lub kontroler ruchu przychodzącego nginx jako punkt końcowy NodePort (lub LoadBalancer, który również korzysta z NodePort) do kierowania ruchu przychodzącego HTTP, a ustawienie tej opcji może znacznie zmniejszyć opóźnienia dla takich żądań.

В tę publikację Możesz dowiedzieć się więcej o externalTrafficPolicy, jego zaletach i wadach.

10. Nie przywiązuj się do klastrów i nie nadużywaj płaszczyzny kontroli

Wcześniej zwyczajowo nazywano serwery nazwami własnymi: Anton, HAL9000 i Colossus... Dziś zostały one zastąpione przez losowo generowane identyfikatory. Przyzwyczajenie jednak pozostało i obecnie nazwy własne idą do skupisk.

Typowa historia (oparta na prawdziwych wydarzeniach): wszystko zaczęło się od sprawdzenia koncepcji, dzięki czemu klaster miał dumną nazwę testowanie… Minęły lata, a on NADAL jest używany w produkcji i wszyscy boją się go dotknąć.

Nie ma nic zabawnego w zamienianiu się klastrów w zwierzęta domowe, dlatego zalecamy okresowe usuwanie ich podczas ćwiczeń odzyskiwanie po awarii (to pomoże inżynieria chaosu - około. tłumacz.). Poza tym nie zaszkodzi popracować nad warstwą kontrolną (sterowanie samolotem). Strach przed dotknięciem go nie jest dobrym znakiem. itp. martwy? Chłopaki, naprawdę macie kłopoty!

Z drugiej strony nie należy dać się ponieść manipulacji. Z czasem warstwa kontrolna może działać wolno. Najprawdopodobniej jest to spowodowane dużą liczbą obiektów tworzonych bez ich rotacji (częsta sytuacja przy używaniu Helma z ustawieniami domyślnymi, dlatego jego stan w mapach konfiguracyjnych/sekretach nie jest aktualizowany - w rezultacie gromadzą się tysiące obiektów warstwa kontrolna) lub przy ciągłej edycji obiektów kube-api (do automatycznego skalowania, do CI/CD, do monitorowania, dzienników zdarzeń, kontrolerów itp.).

Dodatkowo zalecamy sprawdzenie umów SLA/SLO z zarządzanym dostawcą Kubernetes i zwrócenie uwagi na gwarancje. Sprzedawca może zagwarantować dostępność warstwy kontrolnej (lub jego podskładników), ale nie opóźnienie p99 żądań, które do niego wysyłasz. Inaczej mówiąc, możesz wejść kubectl get nodesi otrzymaj odpowiedź dopiero po 10 minutach i nie będzie to stanowić naruszenia warunków umowy o świadczenie usług.

11. Bonus: użycie najnowszego tagu

Ale to już klasyk. Ostatnio rzadziej spotykamy się z tą techniką, gdyż wielu, wyciągnąwszy wnioski z gorzkich doświadczeń, zaprzestało używania tagu :latest i zacząłem przypinać wersje. Brawo!

ECR zachowuje niezmienność znaczników obrazu; Zalecamy zapoznanie się z tą niezwykłą funkcją.

Streszczenie

Nie oczekuj, że wszystko zadziała z dnia na dzień: Kubernetes nie jest panaceum. Zła aplikacja tak pozostanie nawet w Kubernetesie (i prawdopodobnie będzie jeszcze gorzej). Nieostrożność doprowadzi do nadmiernej złożoności, powolnej i stresującej pracy warstwy kontrolnej. Ponadto ryzykujesz pozostawieniem bez strategii odzyskiwania po awarii. Nie oczekuj, że Kubernetes zapewni izolację i wysoką dostępność od razu po wyjęciu z pudełka. Poświęć trochę czasu na uczynienie swojej aplikacji prawdziwie natywną w chmurze.

Możesz zapoznać się z nieudanymi doświadczeniami różnych zespołów w ten zbiór opowiadań przez Henninga Jacobsa.

Osoby chcące dodać coś do listy błędów podanych w tym artykule mogą skontaktować się z nami na Twitterze (@MarekBartik, @MstrsObserver).

PS od tłumacza

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz