Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Przekład artykułu przygotowany przed rozpoczęciem kursu „Infrastrukturalna platforma oparta na Kubernetes”.

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Jak zaoszczędzić na kosztach chmurowych podczas pracy z Kubernetes? Nie ma jednego, słusznego rozwiązania, ale w tym artykule opisano kilka narzędzi, które pomogą efektywniej zarządzać zasobami i zmniejszyć wydatki na obliczenia w chmurze.

Napisałem ten artykuł z myślą o Kubernetes dla AWS, ale będzie on (prawie) w pełni zastosowany także dla innych dostawców chmur. Zakładam, że Twój klaster(y) już ma skonfigurowaną automatyczną skalowalność (cluster-autoscaler). Usunięcie zasobów i zmniejszenie skali wdrożenia pozwoli zaoszczędzić tylko wtedy, gdy również zmniejszy Twój park węzłów roboczych (instancji EC2).

W tym artykule zostaną omówione:

  • oczyszczanie nieużywanych zasobów (kube-janitor)
  • zmniejszenie skalowania w czasie nieaktywnym (kube-downscaler)
  • wykorzystanie poziomego automatycznego skalowania (HPA),
  • zmniejszenie nadmiarowego rezerwowania zasobów (kube-resource-report, VPA)
  • wykorzystanie instancji Spot

Oczyszczanie nieużywanych zasobów

Praca w szybko zmieniającym się środowisku - to świetne. Chcemy, aby organizacje techniczne przyspieszały. Szybsza dostawa oprogramowania oznacza również większą liczbę wdrożeń PR, środowisk w podglądzie, prototypów i rozwiązań analitycznych. Wszystko wdrażane na Kubernetes. Kto ma czas na ręczne czyszczenie testowych wdrożeń? Łatwo zapomnieć o usunięciu eksperymentu sprzed tygodnia. Rachunek za chmurę ostatecznie wzrośnie z powodu tego, że zapomnieliśmy o zamknięciu:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

(Henning Jacobs:
Cóż:
(cytując) Cory Quinn:
Mit: Twój rachunek AWS to funkcja zależności od liczby Twoich użytkowników.
Fakt: Twój rachunek AWS to funkcja zależności od liczby Twoich inżynierów.

Ivan Kurnosov (w odpowiedzi):
Prawdziwy fakt: Twój rachunek AWS to funkcja zależności od liczby rzeczy, które zapomniałeś wyłączyć/usunąć.)

Kubernetes Janitor (kube-janitor) pomaga oczyścić Twój klaster. Konfiguracja janitora jest elastyczna zarówno dla użycia globalnego, jak i lokalnego:

  • Ogólne zasady dla całego klastra mogą określać maksymalny czas życia (TTL — time-to-live) dla PR/testowych wdrożeń.
  • Poszczególne zasoby mogą być adnotowane za pomocą janitor/ttl, na przykład, aby automatycznie usunąć spike/prototyp po 7 dniach.

Ogólne zasady są definiowane w pliku YAML. Jego ścieżka jest przekazywana przez parametr --rules-file w kube-janitor. Oto przykład zasady dla usunięcia wszystkich przestrzeni nazw z -pr- w nazwie po dwóch dniach:

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

Następny przykład reguluje użycie etykiety application na podach Deployment i StatefulSet dla wszystkich nowych Deployments/StatefulSet w 2020 roku, ale jednocześnie pozwala na przeprowadzanie testów bez tej etykiety przez tydzień:

- id: require-application-label
  # usuń deployments i statefulsets bez etykiety "application"
  resources:
    - deployments
    - statefulsets
  # patrz http://jmespath.org/specification.html
  jmespath: "!(spec.template.metadata.labels.application) && metadata.creationTimestamp > '2020-01-01'"
  ttl: 7d

Uruchomienie ograniczonej czasowo wersji demonstracyjnej przez 30 minut w klastrze, gdzie działa 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ą trwałe wolumeny (AWS EBS). Przy usuwaniu Kubernetes StatefulSet nie są usuwane jego trwałe wolumeny (PVC — PersistentVolumeClaim). Nieużywane wolumeny EBS mogą łatwo prowadzić do kosztów rzędu setek dolarów miesięcznie. Kubernetes Janitor ma funkcję do czyszczenia nieużywanych PVC. Na przykład ta zasada usunie wszystkie PVC, które nie są zamontowane przez moduł i do których nie odwołuje się StatefulSet lub 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ć klaster w „czystości” i zapobiec powolnemu gromadzeniu się kosztów obliczeń w chmurze. Aby uzyskać instrukcje dotyczące wdrożenia i konfiguracji, postępuj zgodnie z README kube-janitor.

Zmniejszenie skalowania w czasie bezczynności

Systemy testowe i tymczasowe zazwyczaj wymagają działania tylko w godzinach pracy. Niektóre aplikacje produkcyjne, takie jak zaplecze/narzędzia administracyjne, również wymagają ograniczonej dostępności i mogą być wyłączane w nocy.

Kubernetes Downscaler (kube-downscaler) umożliwia użytkownikom i operatorom zmniejszenie skali systemu w czasie bezczynności. Możliwe jest skalowanie Deployments i StatefulSets do zera replik. CronJobs mogą być wstrzymane. Kubernetes Downscaler jest konfigurowany dla całego klastra, jednego lub kilku przestrzeni nazw lub pojedynczych zasobów. Można ustawić либо „czas bezczynności”, либо „czas pracy”. Na przykład, aby maksymalnie zmniejszyć skalowanie w ciągu nocy i w weekendy:

image: hjacobs/kube-downscaler:20.4.3
args:
  - --interval=30
  # nie wyłączać komponentów infrastruktury
  - --exclude-namespaces=kube-system,infra
  # nie wyłączać kube-downscaler oraz zostawić Postgres Operator, aby wykluczone bazy danych mogły być zarządzane
  - --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:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Zmniejszenie skali z ~13 do 4 węzłów roboczych zdecydowanie daje odczuwalną różnicę w rachunku AWS.

Ale co, jeśli muszę pracować w „czasie bezczynności” klastra? Niektóre wdrożenia można na stałe wykluczyć ze skalowania, dodając adnotację downscaler/exclude: true. Wdrożenia mogą być tymczasowo wykluczone za pomocą adnotacji downscaler/exclude-until z absolutnym znacznikiem czasu w formacie RRRR-MM-DD GG:MM (UTC). W razie potrzeby cały klaster można ponownie skalować przez wdrożenie podu z adnotacją downscaler/force-uptime, na przykład przez uruchomienie wzorca nginx:

kubectl run scale-up --image=nginx
kubectl annotate deploy scale-up janitor/ttl=1h # usuń wdrożenie po godzinie
kubectl annotate pod $(kubectl get pod -l run=scale-up -o jsonpath="{.items[0].metadata.name}") downscaler/force-uptime=true

Zobacz README kube-downscaler, jeśli interesuje cię instrukcja wdrożenia i dodatkowe opcje.

Użyj automatycznego skalowania poziomego

Wiele aplikacji/usług ma do czynienia z dynamicznym schematem obciążenia: czasami ich moduły są bezczynne, a czasami działają z pełną mocą. Utrzymywanie stałej floty podów, aby sprostać maksymalnemu obciążeniu szczytowemu, nie jest ekonomiczne. Kubernetes wspiera poziome automatyczne skalowanie przez zasób HorizontalPodAutoscaler (HPA). Zużycie CPU jest często dobrym wskaźnikiem do 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ł komponent do prostego podłączania metryk użytkowników do skalowania: Kube Metrics Adapter (kube-metrics-adapter) to uniwersalny adapter metryk dla Kubernetes, który może zbierać i udostępniać metryki użytkowników oraz zewnętrzne metryki do poziomego automatycznego skalowania podów. Obsługuje skalowanie na podstawie metryk Prometheus, kolejek SQS i innych ustawień. Na przykład, aby skalować wdrożenie na podstawie metryki użytkownika, przedstawionej przez aplikację w formacie 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

Konfiguracja poziomego automatycznego skalowania z użyciem HPA powinna być jednym z domyślnych działań w celu zwiększenia wydajności usług bez uwzględnienia stanu. Spotify ma prezentację z ich doświadczeniami i zaleceniami dla HPA: skaluj swoje wdrożenia, a nie swój portfel.

Zmniejszenie nadmiarowego rezerwowania zasobów

Obciążenia Kubernetes określają swoje potrzeby w zakresie CPU/pamięci poprzez 'żądania zasobów' (resource requests). Zasoby CPU mierzone są w wirtualnych rdzeniach lub częściej w 'milicentonach' (millicores), na przykład 500m oznacza 50% vCPU. Zasoby pamięci mierzone są w bajtach, a zwykłe sufiksy można wykorzystać, na przykład 500Mi, co oznacza 500 megabajtów. Żądania zasobów 'blokują' ilość na węzłach roboczych, co oznacza, że moduł z żądaniem CPU wynoszącym 1000m na węźle z 4 wirtualnymi CPU zostawi tylko 3 wirtualne CPU dostępne dla innych modułów. [1]

Slack (nadmiar rezerwowania) — to różnica między zapotrzebowaniem na zasoby a rzeczywistym wykorzystaniem. Na przykład, pod, który żąda 2 GiB pamięci, ale używa tylko 200 MiB, ma około 1,8 GiB "zbędnej" pamięci. Zbędne zasoby kosztują. Można w przybliżeniu oszacować, że 1 GiB zbędnej pamięci kosztuje około 10 dolarów miesięcznie. [2]

Raport o zasobach Kubernetes (kube-resource-report) pokazuje nadmiarowe rezerwy i może pomóc Ci określić potencjał oszczędności:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Raport o zasobach Kubernetes pokazuje nadmiar, skonsolidowany przez aplikację i zespół. To pozwala znaleźć miejsca, w których można obniżyć zapotrzebowanie na zasoby. Wygenerowany raport HTML dostarcza jedynie migawkę wykorzystania zasobów. Należy obserwować wykorzystanie CPU/pamięci w czasie, aby określić odpowiednie zapotrzebowania na zasoby. Oto diagram Grafany dla "typowej" usługi obciążonej CPU: wszystkie pady używają znacznie mniej niż 3 przydzielone rdzenie CPU:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Zmniejszenie zapotrzebowania na CPU z 3000m do ~400m uwalnia zasoby dla innych obciążeń i pozwala na zmniejszenie klastra.

"Średnie wykorzystanie CPU instancji EC2 często oscyluje w granicach jednocyfrowych procentów", — napisał Cory Quinn. Podczas gdy w przypadku EC2 ocena odpowiedniego rozmiaru może być złym posunięciem, zmiana niektórych zapotrzebowań na zasoby Kubernetes w pliku YAML jest prosta i może przynieść ogromne oszczędności.

Czy naprawdę chcemy, aby ludzie zmieniali wartości w plikach YAML? Nie, maszyny mogą to zrobić znacznie lepiej! Kubernetes Autoskaler pionowy podów (VPA) dokładnie tym się zajmuje: dostosowuje zapotrzebowania na zasoby i ograniczenia do obciążenia. Oto przykład wykresu zapotrzebowania na CPU w Prometheus (cienka niebieska linia), dostosowanego przez VPA w czasie:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Zalando używa VPA we wszystkich swoich klastrach do komponentów infrastruktury. Aplikacje, które nie są krytyczne, również mogą korzystać z VPA.

Goldilocks 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 pulpicie nawigacyjnym. Może pomóc programistom ustawić odpowiednie zapotrzebowania na CPU/pamięć dla swoich aplikacji:

Oszczędzamy na kosztach chmurowych Kubernetes w AWS

Napisałem mały post na blogu o VPA w 2019 roku, a niedawno w społeczności użytkowników końcowych CNCF omawiano temat VPA.

Użycie instancji EC2 Spot

W końcu, co nie mniej ważne, koszty AWS EC2 można obniżyć, używając instancji Spot jako węzłów roboczych Kubernetes. [3]. Instancje Spot są dostępne z rabatem do 90% w porównaniu do cen na żądanie. Uruchamianie Kubernetes na EC2 Spot to dobra kombinacja: musisz wskazać kilka różnych typów instancji dla większej dostępności, co oznacza, że możesz uzyskać większy węzeł za tę samą lub niższą cenę, a zwiększona pojemność może być wykorzystywana przez obciążenia robocze kontenerów Kubernetes.

Jak uruchomić Kubernetes na EC2 Spot? Istnieje kilka opcji: użyć zewnętrznej usługi, takiej jak SpotInst (teraz nazywa się „Spot”, nie pytaj mnie dlaczego), lub po prostu dodać Spot AutoScalingGroup (ASG) do swojego klastra. Na przykład, oto fragment CloudFormation dla „optymalizowanego pod kątem pojemności” Spot ASG z kilkoma 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 dotyczących korzystania z instancji Spot z Kubernetes:

  • Musisz obsługiwać zakończenia Spot, na przykład poprzez wypuszczenie węzła przy zatrzymaniu instancji.
  • Zalando używa forka oficjalnego automatycznego skalowania klastra z priorytetami puli węzłów.
  • Węzły Spot można zmusić do przyjmowania „rejestracji” obciążeń roboczych do uruchomienia w Spot.

Podsumowanie

Mam nadzieję, że znajdziesz niektóre z prezentowanych narzędzi pomocne w obniżaniu rachunku za usługi chmurowe. Możesz znaleźć większość treści artykułu również w moim wystąpieniu na DevOps Gathering 2019 na YouTube oraz w postaci slajdów..

Jakie są twoje najlepsze praktyki dotyczące oszczędzania kosztów w chmurze przy użyciu Kubernetes? Daj znać w Twitterze (@try_except_).

[1] W rzeczywistości mniej niż 3 wirtualne CPU pozostaną gotowe do użycia, ponieważ przepustowość węzła zmniejsza się z powodu zarezerwowanych zasobów systemowych. Kubernetes rozróżnia fizyczną pojemność węzła i zasoby "alokowane" (Node Allocatable).

[2] Przykład obliczeń: jeden egzemplarz m5.large z 8 GiB pamięci kosztuje ~84 dolary miesięcznie (eu-central-1, On-Demand), tzn. zablokowanie 1/8 węzła kosztuje około ~10 dolarów miesięcznie.

[3] Istnieje wiele sposobów na obniżenie rachunku za EC2, na przykład rezerwowane egzemplarze, plan oszczędnościowy itd. — nie będę omawiać tych tematów tutaj, ale koniecznie musisz o nich poczytać!

Dowiedz się więcej o kursie.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster