Skalowanie aplikacji

Usługi chmurowe, laboratorium 6


Agenda

  1. Stan startowy i limity planu F1
  2. Scale up: F1 -> B1
  3. Pomiar bazowy: load test jednej instancji
  4. Scale out: dwie instancje, session affinity
  5. Ten sam test na dwóch instancjach, metryki
  6. Eksperyment: stan lokalny na wielu instancjach
  7. Bottleneck: wspólna baza danych
  8. Autoscale: idea i wymagania planu
  9. Raport, powrót do F1, sprzątanie

Cel

Sprawdzić w praktyce:

externalized state -> możliwość uruchomienia wielu instancji

  • scale up i scale out na tej samej aplikacji
  • pomiar przepustowości przed i po
  • rozkład ruchu i metryki per instancja
  • co się dzieje, gdy stan jest lokalny
  • gdzie kończy się skalowanie aplikacji

Kod CloudNotes się nie zmienia.


Architektura: przed i w trakcie

przed (cp05):
                         Managed Database (psql-cloudnotes-<SUFFIX>)
                           ^
    Client -> HTTPS -> App Service (plan F1, 1 instancja)
                           v
                         Blob Storage (stcloudnotes<SUFFIX>)
w trakcie:
                                       +-> instancja 1 --+
    Client -> HTTPS -> load balancer --+                 +-> Managed Database
                       App Service     +-> instancja 2 --+-> Blob Storage
                       (plan B1, 2 instancje)

Po zajęciach: z powrotem F1, 1 instancja, checkpoint cp05-database.


Scale up czy scale out?

Scale upScale out
zmianawiększa maszynawięcej maszyn
App Servicepoziom planuliczba instancji
wymaganierestart instancjistateless, load balancer
limitsufit jednej maszynylimit planu, wspólne zależności
awaria maszynyusługa stoipozostałe instancje działają

Limity planów App Service

PoziomInstancjeSkalowanieLinux, Sweden Central
F1 Free1brak0 USD
B1 Basicdo 3ręczne0,018 USD/h za instancję
S1 Standarddo 10ręczne + reguły autoscale0,095 USD/h za instancję
P0v3 Premium v3do 30+ automatic scaling0,089 USD/h za instancję

F1: limit czasu CPU. Nie uruchamiaj load testów na F1.


Koszt

  • B1, 1-2 instancje przez zajęcia: około 0,1 USD z kredytu
  • PostgreSQL i Storage: jak na L05
  • Płacisz za każdą instancję planu, nie za aplikację

Ryzyko: zapomniany B1 z 2 instancjami = około 26 USD miesięcznie

Cleanup: 2 -> 1 instancja, potem B1 -> F1.

Nie usuwaj limitu wydatków, nie przechodź na Pay-As-You-Go.


Krok 1: stan startowy

az resource list --resource-group rg-cloudnotes --query "[].{name:name, type:type}" --output table
az postgres flexible-server start --resource-group rg-cloudnotes --name psql-cloudnotes-<SUFFIX>
git -C ~/cloudnotes archive --format=zip --output ~/cloudnotes-app.zip HEAD:src
az webapp deploy --resource-group rg-cloudnotes --name app-cloudnotes-<SUFFIX> \
  --src-path ~/cloudnotes-app.zip --type zip
sudo apt-get install -y apache2-utils

Oczekiwane backendy: postgres, blob, sync.

Brak serwera bazy: „Odtworzenie stanu cp05-database” w L05.


Krok 2: jedna instancja, limit F1

az appservice plan show --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> \
  --query "{sku:sku.name, tier:sku.tier, instances:sku.capacity}" --output table
az webapp list-instances --resource-group rg-cloudnotes --name app-cloudnotes-<SUFFIX> --query "[].name" --output tsv
az appservice plan update --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> --number-of-workers 2
  • F1: błąd albo nadal 1 instancja
  • Identyfikator instancji: stopka strony i pole instance w /health

Krok 3: scale up F1 -> B1

az appservice plan update --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> --sku B1

curl -s https://app-cloudnotes-<SUFFIX>.azurewebsites.net/health | jq "{status, instance, started_at}"
  • B1, Basic, 1 instancja, maksymalnie 3
  • nowy instance i started_at
  • notatki zostały

Scale up = nowa maszyna. Też wymaga stateless.


Krok 4: pomiar bazowy

/debug/cpu?ms=250: każde żądanie zajmuje CPU na 250 ms

ab -n 80 -c 8 -l "https://app-cloudnotes-<SUFFIX>.azurewebsites.net/debug/cpu?ms=250" | tee ~/l06-ab-1.txt
grep -E "Failed requests|Requests per second|Time per request" ~/l06-ab-1.txt
WartośćOczekiwanieSkąd
Requests per secondokoło 41 rdzeń / 250 ms
Time per requestokoło 2000 ms8 w kolejce x 250 ms

Bottleneck: CPU jednej instancji. Przeciążenie = rosnący czas, nie błędy.


Krok 5: scale out do 2 instancji

az appservice plan update --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> --number-of-workers 2
seq 20 | xargs -I{} curl -s https://app-cloudnotes-<SUFFIX>.azurewebsites.net/api/notes \
  | jq -r '"\(.instance) notatek: \(.notes | length)"' | sort | uniq -c
     11 3f7a9c2e81b4 notatek: 3
      9 a04d5e6f7c12 notatek: 3

Dwie instancje, te same dane. Load balancer wbudowany w App Service.


Session affinity

curl -s -o /dev/null -D - https://app-cloudnotes-<SUFFIX>.azurewebsites.net/ | grep -i "set-cookie"
az webapp update --resource-group rg-cloudnotes --name app-cloudnotes-<SUFFIX> --client-affinity-enabled false
  • Ciasteczko ARRAffinity przykleja przeglądarkę do jednej instancji
  • curl nie trzyma ciasteczek, więc trafia w obie
  • Affinity to obejście dla stanu w pamięci, nie rozwiązanie
  • CloudNotes jest stateless -> affinity off (zostaje po zajęciach)

Krok 6: ten sam test, dwie instancje

ab -n 80 -c 8 -l "https://app-cloudnotes-<SUFFIX>.azurewebsites.net/debug/cpu?ms=250" | tee ~/l06-ab-2.txt
grep -E "Requests per second|Time per request" ~/l06-ab-1.txt ~/l06-ab-2.txt
1 instancja2 instancje
Requests per secondokoło 4około 8
Time per requestokoło 2000 msokoło 1000 ms

Prawie liniowo, bo bottleneck był w każdej instancji osobno.


Metryki per instancja

az monitor metrics list --resource plan-cloudnotes-<SUFFIX> --resource-group rg-cloudnotes \
  --resource-type Microsoft.Web/serverfarms --metric CpuPercentage --aggregation Maximum --dimension Instance \
  --query 'value[0].timeseries[].{instance:metadatavalues[0].value, cpu:data[?maximum].maximum}' --output json

Portal: plan -> Metrics -> CPU Percentage -> Apply splitting -> Instance

  • test 1: jedna seria blisko 100%
  • test 2: obie serie wysoko
  • metryki spływają z opóźnieniem kilku minut

Krok 7: stan lokalny na wielu instancjach

az webapp config appsettings delete --resource-group rg-cloudnotes --name app-cloudnotes-<SUFFIX> \
  --setting-names DATABASE_URL --output none
seq 6 | xargs -I{} curl -s -o /dev/null -w "%{http_code} " -X POST \
  -F "title=Lokalna {}" -F "content=Zapisana na instancji" https://app-cloudnotes-<SUFFIX>.azurewebsites.net/notes
     12 3f7a9c2e81b4: Lokalna 5, Lokalna 3, Lokalna 2, Lokalna 1
      8 a04d5e6f7c12: Lokalna 6, Lokalna 4

Każda instancja ma własne notatki i własną notatkę #1.


Krok 7: przywrócenie i wniosek

az webapp config appsettings set --resource-group rg-cloudnotes --name app-cloudnotes-<SUFFIX> \
  --settings DATABASE_URL="postgresql://cloudnotesadmin:<HASLO>@psql-cloudnotes-<SUFFIX>.postgres.database.azure.com:5432/cloudnotes?sslmode=require" \
  --output none
local state -> problem ze skalowaniem -> externalized state -> wiele instancji
  • Affinity ukryłoby problem do restartu, wdrożenia albo scale in
  • Notatki Lokalna przepadły: jedna zmiana konfiguracji = utrata danych

Krok 8: bottleneck przenosi się na bazę

ab -n 200 -c 8 -l "https://app-cloudnotes-<SUFFIX>.azurewebsites.net/api/notes" | tee ~/l06-ab-db.txt
az monitor metrics list --resource psql-cloudnotes-<SUFFIX> --resource-group rg-cloudnotes \
  --resource-type Microsoft.DBforPostgreSQL/flexibleServers --metric connections_succeeded --aggregation Total \
  --query 'value[0].timeseries[0].data[?total].{time:timeStamp, connections:total}' --output table
  • 1 żądanie = 1 nowe połączenie z bazą (brak puli połączeń)
  • B1ms: max_connections 50, dla aplikacji 35
  • 10 instancji x 4 procesy = 40 -> too many clients

Co jest wspólne dla wszystkich instancji i ile wytrzyma?


Jak odciążyć bazę?

SposóbKompromis
connection pool w aplikacjizmiana kodu, rozmiar puli x liczba instancji
pooler przed bazą (PgBouncer)niedostępny wbudowany w Burstable
cachedane mogą być nieaktualne
scale up bazykoszt, przerwa, sufit jednej maszyny
repliki do odczytuopóźnienie replikacji, koszt

Aplikacje skalujemy zwykle poziomo, bazy relacyjne pionowo.


Krok 9: autoscale

metryka -> reguła (próg, okno) -> akcja (+1 / -1) -> cooldown -> ponowna ocena
ElementPrzykład dla CloudNotes
scale outśrednie CPU > 70% przez dziesięć minut -> +1
scale inśrednie CPU < 30% przez dziesięć minut -> -1
minimum / maksimum2 / 4
  • Reguły: od S1. Automatic scaling: Premium v2/v3
  • Minimum = odporność, maksimum = budżet i baza
  • Portal: plan -> Scale out (App Service plan). Nie zmieniaj poziomu

Weryfikacja

Raport ~/l06-report.txt (polecenie w instrukcji), przed cleanupem:

  • plan B1, 2 instancje, affinity false
  • rozkład żądań: dwie instancje, ta sama liczba notatek
  • 3 load testy, Failed requests: 0, 2 instancje szybsze niż 1
  • eksperyment: różne listy notatek Lokalna
  • CPU per instancja, połączenia do bazy
  • /health: healthy, "database":"postgres"

Sprawdź: w raporcie nie ma postgresql://, AccountKey= ani hasła.


Sprzątanie

Kolejność ma znaczenie: F1 pozwala tylko na 1 instancję.

az appservice plan update --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> --number-of-workers 1
az appservice plan update --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> --sku F1
az appservice plan show --resource-group rg-cloudnotes --name plan-cloudnotes-<SUFFIX> \
  --query "{sku:sku.name, instances:sku.capacity}" --output table
rm ~/cloudnotes-app.zip
Wariant z L05Serwer bazyNa L07
A: bezpłatna ofertaaz postgres flexible-server stop ...start
B: brak ofertyaz postgres flexible-server delete ... --yesodtworzenie cp05
B1 App Serviceaz group delete --name rg-cloudnotes ...odtworzenie cp04 i cp05

Podsumowanie

  • Scale up = większa maszyna, ma sufit; scale out = więcej maszyn
  • Funkcje skalowania zależą od poziomu planu: F1 nic, B1 ręcznie, S1 reguły
  • PaaS: load balancer wbudowany, nowa instancja bez zmiany kodu
  • 2 instancje przy obciążeniu CPU: około 2 razy większa przepustowość
  • Stan lokalny + wiele instancji = różne dane dla użytkowników
  • Session affinity ukrywa problem, nie rozwiązuje go
  • Skalowanie aplikacji przenosi bottleneck na wspólną bazę
  • Autoscale: metryka, próg, okno, krok, minimum, maksimum
  • Płacisz za każdą instancję: po teście wróć do F1

Następnie: L07, awaria, restart i podstawy reliability.

 

Imię Nazwisko

OPIS