Przekład artykułu przygotowany przed rozpoczęciem kursu .

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ść (). 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 ()
- zmniejszenie skalowania w czasie nieaktywnym ()
- wykorzystanie poziomego automatycznego skalowania (HPA),
- zmniejszenie nadmiarowego rezerwowania zasobów (, VPA)
- wykorzystanie instancji Spot
Oczyszczanie nieużywanych zasobów
Praca w szybko zmieniającym się środowisku - to świetne. Chcemy, aby organizacje techniczne . 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:

(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ąć.)
(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: 2dNastę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: 7dUruchomienie 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=30mKolejnym ź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: 24hKubernetes 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 .
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.
(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-timeOto wykres skalowania węzłów roboczych klastra w weekendy:

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=trueZobacz , 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 (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: UtilizationZalando stworzył komponent do prostego podłączania metryk użytkowników do skalowania: (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: AverageValueKonfiguracja 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: .
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.
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.
(kube-resource-report) pokazuje nadmiarowe rezerwy i może pomóc Ci określić potencjał oszczędności:

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:

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", — . Podczas gdy w przypadku EC2 , 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 (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:

do komponentów infrastruktury. Aplikacje, które nie są krytyczne, również mogą korzystać z VPA.
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:

Napisałem mały w 2019 roku, a niedawno w .
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. . 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 oficjalnego automatycznego skalowania klastra z priorytetami puli węzłów.
- Węzły Spot 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 .
Jakie są twoje najlepsze praktyki dotyczące oszczędzania kosztów w chmurze przy użyciu Kubernetes? Daj znać w .
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" ().
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.
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ć!
Źródło: habr.com
