Zaoszczędź na kosztach chmury Kubernetes w AWS

Tłumaczenie artykułu zostało przygotowane w przeddzień rozpoczęcia kursu „Platforma infrastrukturalna oparta na Kubernetesie”.

Zaoszczędź na kosztach chmury Kubernetes w AWS

Jak zaoszczędzić na kosztach chmury pracując z Kubernetesem? Nie ma jednego dobrego rozwiązania, ale w tym artykule opisano kilka narzędzi, które mogą pomóc Ci efektywniej zarządzać zasobami i obniżyć koszty przetwarzania w chmurze.

Napisałem ten artykuł z myślą o Kubernetes dla AWS, ale będzie on miał zastosowanie (prawie) dokładnie w ten sam sposób w przypadku innych dostawców usług w chmurze. Zakładam, że Twoje klastry mają już skonfigurowane automatyczne skalowanie (automatyczne skalowanie klastrów). Usunięcie zasobów i zmniejszenie skali wdrożenia pozwoli zaoszczędzić pieniądze tylko wtedy, gdy zmniejszy także flotę węzłów roboczych (instancje EC2).

W tym artykule omówimy:

  • sprzątanie nieużywanych zasobów (kube-woźny)
  • Zmniejsz skalowanie w godzinach poza pracą (kube-downscaler)
  • przy użyciu poziomego autoskalowania (HPA),
  • redukcja nadmiernej rezerwacji zasobów (raport zasobów kube, VPA)
  • przy użyciu instancji Spota

Oczyszczanie niewykorzystanych zasobów

Praca w dynamicznym środowisku jest świetna. Chcemy organizacji technologicznych przyśpieszony. Szybsze dostarczanie oprogramowania oznacza także więcej wdrożeń PR, środowisk podglądu, prototypów i rozwiązań analitycznych. Wszystko jest wdrożone na Kubernetesie. Kto ma czas na ręczne czyszczenie wdrożeń testowych? Łatwo zapomnieć o usunięciu eksperymentu sprzed tygodnia. Rachunek za chmurę wzrośnie z powodu czegoś, o czym zapomnieliśmy zamknąć:

Zaoszczędź na kosztach chmury Kubernetes w AWS

(Henning Jacobs:
Żyza:
(cytuje) Corey Quinn:
Mit: Twoje konto AWS jest funkcją liczby posiadanych użytkowników.
Fakt: Twój wynik AWS jest funkcją liczby posiadanych inżynierów.

Iwan Kurnosow (w odpowiedzi):
Prawdziwy fakt: Twój wynik AWS jest funkcją liczby rzeczy, które zapomniałeś wyłączyć/usunąć.)

Woźny Kubernetesa (kube-janitor) pomaga oczyścić klaster. Konfiguracja woźnego jest elastyczna zarówno do użytku globalnego, jak i lokalnego:

  • Reguły obowiązujące w całym klastrze mogą definiować maksymalny czas wygaśnięcia (TTL) dla wdrożeń PR/testowych.
  • Poszczególne zasoby można opatrzyć adnotacją janitor/ttl, na przykład aby automatycznie usunąć kolec/prototyp po 7 dniach.

Ogólne zasady są zdefiniowane w pliku YAML. Jego ścieżka jest przekazywana przez parametr --rules-file w kube-janitor. Oto przykładowa reguła służąca do usuwania wszystkich przestrzeni nazw -pr- w imieniu po dwóch dniach:

- id: cleanup-resources-from-pull-requests
  resources:
    - namespaces
  jmespath: "contains(metadata.name, '-pr-')"
  ttl: 2d

Poniższy przykład reguluje użycie etykiety aplikacji na podach Deployment i StatefulSet dla wszystkich nowych Deployments/StatefulSets w 2020 roku, ale jednocześnie pozwala na wykonanie testów bez tej etykiety przez tydzień:

- id: require-application-label
  # удалить deployments и statefulsets без метки "application"
  resources:
    - deployments
    - statefulsets
  # см. http://jmespath.org/specification.html
  jmespath: "!(spec.template.metadata.labels.application) && metadata.creationTimestamp > '2020-01-01'"
  ttl: 7d

Uruchom ograniczone czasowo demo przez 30 minut w klastrze z uruchomionym kube-janitor:

kubectl run nginx-demo --image=nginx
kubectl annotate deploy nginx-demo janitor/ttl=30m

Kolejnym źródłem rosnących kosztów są wolumeny stałe (AWS EBS). Usunięcie zestawu Kubernetes StatefulSet nie powoduje usunięcia jego trwałych woluminów (PVC — PersistentVolumeClaim). Niewykorzystane woluminy EBS mogą z łatwością skutkować kosztami rzędu setek dolarów miesięcznie. Kubernetes Janitor ma funkcję czyszczenia nieużywanych PVC. Na przykład ta reguła usunie wszystkie obwody PVC, które nie są zamontowane przez moduł i do których nie odwołuje się StatefulSet ani CronJob:

# удалить все PVC, которые не смонтированы и на которые не ссылаются StatefulSets
- id: remove-unused-pvcs
  resources:
  - persistentvolumeclaims
  jmespath: "_context.pvc_is_not_mounted && _context.pvc_is_not_referenced"
  ttl: 24h

Kubernetes Janitor może pomóc Ci utrzymać porządek w klastrze i zapobiec powolnemu narastaniu kosztów przetwarzania w chmurze. Aby zapoznać się z instrukcjami wdrażania i konfiguracji, postępuj zgodnie z instrukcjami PRZECZYTAJ kube-janitor.

Ogranicz skalowanie w godzinach poza pracą

Systemy testowe i pomostowe zazwyczaj muszą działać wyłącznie w godzinach pracy. Niektóre aplikacje produkcyjne, takie jak narzędzia zaplecza/administracji, również wymagają jedynie ograniczonej dostępności i mogą zostać wyłączone z dnia na dzień.

Downscaler Kubernetesa (kube-downscaler) umożliwia użytkownikom i operatorom skalowanie systemu poza godzinami pracy. Wdrożenia i zestawy StatefulSets można skalować do zerowych replik. CronJobs może zostać zawieszony. Kubernetes Downscaler jest skonfigurowany dla całego klastra, co najmniej jednej przestrzeni nazw albo poszczególnych zasobów. Możesz ustawić „czas bezczynności” lub odwrotnie „czas pracy”. Na przykład, aby maksymalnie ograniczyć skalowanie w nocy i w weekendy:

image: hjacobs/kube-downscaler:20.4.3
args:
  - --interval=30
  # не отключать компоненты инфраструктуры
  - --exclude-namespaces=kube-system,infra
  # не отключать kube-downscaler, а также оставить Postgres Operator, чтобы исключенными БД можно было управлять
  - --exclude-deployments=kube-downscaler,postgres-operator
  - --default-uptime=Mon-Fri 08:00-20:00 Europe/Berlin
  - --include-resources=deployments,statefulsets,stacks,cronjobs
  - --deployment-time-annotation=deployment-time

Oto wykres skalowania węzłów roboczych klastra w weekendy:

Zaoszczędź na kosztach chmury Kubernetes w AWS

Skalowanie w dół z ~13 do 4 węzłów roboczych z pewnością powoduje zauważalną różnicę w rachunku za AWS.

Ale co, jeśli muszę pracować podczas „przestoju” klastra? Niektóre wdrożenia można trwale wykluczyć ze skalowania, dodając adnotację downscaler/exclude: true. Wdrożenia można tymczasowo wykluczyć za pomocą adnotacji downscaler/exclude-aż z bezwzględnym znacznikiem czasu w formacie RRRR-MM-DD HH:MM (UTC). W razie potrzeby cały klaster można zmniejszyć, wdrażając zasobnik z adnotacją downscaler/force-uptimena przykład uruchamiając nginx blank:

kubectl run scale-up --image=nginx
kubectl annotate deploy scale-up janitor/ttl=1h # удалить развертывание через час
kubectl annotate pod $(kubectl get pod -l run=scale-up -o jsonpath="{.items[0].metadata.name}") downscaler/force-uptime=true

zobaczyć PRZECZYTAJ Kube-downscaler, jeśli interesują Cię instrukcje wdrażania i dodatkowe opcje.

Użyj poziomego automatycznego skalowania

Wiele aplikacji/usług radzi sobie z dynamicznym wzorcem ładowania: czasami ich moduły są bezczynne, a czasami pracują z pełną wydajnością. Eksploatacja stałej floty kapsuł w celu poradzenia sobie z maksymalnym obciążeniem szczytowym jest nieekonomiczna. Kubernetes obsługuje automatyczne skalowanie poziome w obrębie zasobu Poziomy automatyczny skaler Pod (HPA). Użycie procesora jest często dobrym wskaźnikiem skalowania:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 100
        type: Utilization

Zalando stworzyło komponent umożliwiający łatwe łączenie niestandardowych metryk w celu skalowania: Adapter metryk Kube (kube-metrics-adapter) to ogólny adapter metryk dla platformy Kubernetes, który może zbierać i udostępniać metryki niestandardowe i zewnętrzne na potrzeby automatycznego skalowania poziomego podów. Obsługuje skalowanie w oparciu o metryki Prometheus, kolejki SQS i inne ustawienia. Na przykład, aby skalować wdrożenie do niestandardowej metryki reprezentowanej przez samą aplikację jako JSON w /metrics, użyj:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  annotations:
    # metric-config.<metricType>.<metricName>.<collectorName>/<configKey>
    metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
    metric-config.pods.requests-per-second.json-path/path: /metrics
    metric-config.pods.requests-per-second.json-path/port: "9090"
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: requests-per-second
      target:
        averageValue: 1k
        type: AverageValue

Konfigurowanie poziomego automatycznego skalowania za pomocą HPA powinno być jedną z domyślnych akcji poprawiających wydajność usług bezstanowych. Spotify ma prezentację ze swoimi doświadczeniami i rekomendacjami dla HPA: skaluj wdrożenia, a nie swój portfel.

Zmniejsz nadmierne rezerwowanie zasobów

Obciążenia Kubernetes określają swoje potrzeby w zakresie procesora/pamięci poprzez „żądania zasobów”. Zasoby procesora są mierzone w rdzeniach wirtualnych lub częściej w „milicores”, na przykład 500m oznacza 50% vCPU. Zasoby pamięci mierzone są w bajtach i można stosować popularne przyrostki, takie jak 500Mi, co oznacza 500 megabajtów. Żądania zasobów „blokują” pojemność w węzłach roboczych, co oznacza, że ​​pod z żądaniem procesora na 1000 m w węźle z 4 procesorami wirtualnymi pozostawi tylko 3 procesory wirtualne dostępne dla innych podów. [1]

Slack (nadwyżka rezerwy) to różnica między żądanymi zasobami a rzeczywistym ich wykorzystaniem. Na przykład moduł, który żąda 2 GiB pamięci, ale wykorzystuje tylko 200 MiB, ma ~1,8 GiB „nadwyżki” pamięci. Nadmiar kosztuje. Można z grubsza oszacować, że 1 GiB nadmiarowej pamięci kosztuje ~10 dolarów miesięcznie. [2]

Raport o zasobach Kubernetes (kube-resource-report) wyświetla nadmierne rezerwy i może pomóc w określeniu potencjału oszczędności:

Zaoszczędź na kosztach chmury Kubernetes w AWS

Raport o zasobach Kubernetes pokazuje nadmiar zagregowany według aplikacji i poleceń. Dzięki temu możesz znaleźć miejsca, w których można zmniejszyć zapotrzebowanie na zasoby. Wygenerowany raport HTML zawiera jedynie migawkę wykorzystania zasobów. Aby określić odpowiednie żądania zasobów, należy przyjrzeć się zużyciu procesora/pamięci w czasie. Oto wykres Grafana dla „typowej” usługi obciążającej procesor: wszystkie pody wykorzystują znacznie mniej niż 3 żądane rdzenie procesora:

Zaoszczędź na kosztach chmury Kubernetes w AWS

Zmniejszenie żądania procesora z 3000 m do ~400 m zwalnia zasoby dla innych obciążeń i pozwala na zmniejszenie klastra.

„Średnie wykorzystanie procesora przez instancje EC2 często waha się w jednocyfrowym zakresie procentowym” pisze Corey Quinn. Podczas gdy dla EC2 oszacowanie odpowiedniego rozmiaru może być złą decyzjąZmiana niektórych zapytań o zasoby Kubernetes w pliku YAML jest łatwa i może przynieść ogromne oszczędności.

Ale czy naprawdę chcemy, żeby ludzie zmieniali wartości w plikach YAML? Nie, maszyny potrafią to zrobić znacznie lepiej! Kubernetes Pionowe automatyczne skalowanie podów (VPA) właśnie to robi: dostosowuje żądania zasobów i ograniczenia w zależności od obciążenia. Oto przykładowy wykres żądań procesora Prometheus (cienka niebieska linia) dostosowywanych w czasie przez VPA:

Zaoszczędź na kosztach chmury Kubernetes w AWS

Zalando stosuje VPA we wszystkich swoich klastrach dla elementów infrastruktury. Aplikacje niekrytyczne mogą również korzystać z VPA.

Złotowłosa od Fairwind to narzędzie, które tworzy VPA dla każdego wdrożenia w przestrzeni nazw, a następnie wyświetla rekomendację VPA na swoim panelu kontrolnym. Może pomóc programistom ustawić prawidłowe żądania procesora/pamięci dla swoich aplikacji:

Zaoszczędź na kosztach chmury Kubernetes w AWS

Napisałem mały wpis na blogu o VPA w 2019 r., a ostatnio w Społeczność użytkowników końcowych CNCF omawiała kwestię VPA.

Korzystanie z instancji punktowych EC2

Co nie mniej ważne, koszty AWS EC2 można obniżyć, wykorzystując instancje Spot jako węzły robocze Kubernetes [3]. Instancje Spot są dostępne z rabatem sięgającym nawet 90% w porównaniu z cenami On-Demand. Uruchamianie Kubernetes w EC2 Spot to dobra kombinacja: musisz określić kilka różnych typów instancji, aby uzyskać wyższą dostępność, co oznacza, że ​​możesz uzyskać większy węzeł za tę samą lub niższą cenę, a zwiększoną pojemność można wykorzystać w konteneryzowanych obciążeniach Kubernetes.

Jak uruchomić Kubernetes na EC2 Spot? Istnieje kilka opcji: skorzystaj z usługi strony trzeciej, takiej jak SpotInst (teraz nazywa się „Spot”, nie pytaj mnie dlaczego) lub po prostu dodaj grupę Spot AutoScalingGroup (ASG) do swojego klastra. Na przykład oto fragment CloudFormation dla „zoptymalizowanego pod kątem wydajności” Spot ASG z wieloma typami instancji:

MySpotAutoScalingGroup:
 Properties:
   HealthCheckGracePeriod: 300
   HealthCheckType: EC2
   MixedInstancesPolicy:
     InstancesDistribution:
       OnDemandPercentageAboveBaseCapacity: 0
       SpotAllocationStrategy: capacity-optimized
     LaunchTemplate:
       LaunchTemplateSpecification:
         LaunchTemplateId: !Ref LaunchTemplate
         Version: !GetAtt LaunchTemplate.LatestVersionNumber
       Overrides:
         - InstanceType: "m4.2xlarge"
         - InstanceType: "m4.4xlarge"
         - InstanceType: "m5.2xlarge"
         - InstanceType: "m5.4xlarge"
         - InstanceType: "r4.2xlarge"
         - InstanceType: "r4.4xlarge"
   LaunchTemplate:
     LaunchTemplateId: !Ref LaunchTemplate
     Version: !GetAtt LaunchTemplate.LatestVersionNumber
   MinSize: 0
   MaxSize: 100
   Tags:
   - Key: k8s.io/cluster-autoscaler/node-template/label/aws.amazon.com/spot
     PropagateAtLaunch: true
     Value: "true"

Kilka uwag na temat używania Spota z Kubernetesem:

  • Musisz obsłużyć zakończenia typu Spot, na przykład poprzez połączenie węzła, gdy instancja jest zatrzymana
  • Używa Zalando widelec oficjalne autoskalowanie klastra z priorytetami puli węzłów
  • Węzły punktowe można wymusić akceptować „rejestracje” obciążeń do uruchomienia w Spocie

Streszczenie

Mam nadzieję, że niektóre z przedstawionych narzędzi okażą się przydatne w obniżeniu rachunków za chmurę. Większość treści artykułu można znaleźć także na stronie moje wystąpienie na DevOps Gathering 2019 na YouTube i na slajdach.

Jakie są Twoje najlepsze praktyki dotyczące oszczędzania kosztów chmury w Kubernetes? Proszę o informację pod adresem Twitter (@try_except_).

[1] W rzeczywistości mniej niż 3 procesory wirtualne pozostaną użyteczne, ponieważ przepustowość węzła jest zmniejszana przez zarezerwowane zasoby systemowe. Kubernetes rozróżnia pojemność węzła fizycznego i zasoby „zaopatrzone” (Możliwość przydzielenia węzła).

[2] Przykład obliczenia: jedna instancja m5.large z 8 GiB pamięci kosztuje ~84 USD miesięcznie (eu-central-1, On-Demand), tj. blokowanie 1/8 węzła kosztuje około ~10 USD miesięcznie.

[3] Istnieje wiele innych sposobów na zmniejszenie rachunku EC2, takich jak Instancje Zastrzeżone, Plan Oszczędnościowy itp. - nie będę tutaj omawiał tych tematów, ale zdecydowanie powinieneś się im przyjrzeć!

Dowiedz się więcej o kursie.

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

Dodaj komentarz