Usługa bez stanu w stosunku do serwera modeli
Typowa kontener usługi sieciowej jest prawie bezstanowa: obraz zawiera kod aplikacji i kilka zależności, uruchamia się w sekundę, a każdy wystąpienie jest funkcjonalnie wymienne z każdym innym. To dokładnie to, co sprawia, że skalowanie poziome i przeprowadzanie aktualizacji wersji Kubernetes są tak skuteczne — zabij jeden pód, uruchom inny, nic nie stracono, ponieważ nic istotnego nie zamieszkiwało tego póda. Serwer modeli maszynowego uczenia zaszczyca tę założeniu w konkretny, decydujący sposób: kontener to nie tylko kod, ale też kod plus duży, wersjonowany artefakt binarny (wagi przyswojone, często kilkaset gigabajtów dla dużego modelu), który musi zostać załadowany do pamięci lub pamięci GPU przed tym, jak kontener może obsłużyć pojedynczy żądanie. Ten krok załadowania może trwać od sekund do wielu minut w zależności od rozmiaru modelu i miejsca przechowywania wag.
To zmienia założenia operacyjne, które były zaprogramowane na domyślach Kubernetes. Przygotowanie czerwotarcia, które sprawdza tylko „czy serwer HTTP escuchuje”, zwracając pód jako gotowy podczas gdy model nadal jest załadowywany, powoduje przeprowadzanie ruchu rzeczywistego do póda, które zakończy się timeoutem lub awarią; wdrożenia ML potrzebują przygotowania czerwotarcia, które sprawdza konkretnie, czy model został załadowany do pamięci. Czas uruchamiania bezpośrednio wpływa na szybkość skalowania podczas obciążenia lub przeprowadzania nowej wersji modelu — dwustanowy czas załadowania modelu oznacza, że automatyczne skalowanie reaguje z opóźnieniem dwustu sekund, a nie instantanicjalnie jak w bezstanowej aplikacji sieciowej, co musi być uwzględnione w planowaniu kapacitety, a nie po prostu przypisane.
Kartki graficzne nie są zasobem, który można podzielić tak jak procesory CPU lub pamięć
Kubernetes harmonogramuje CPU i pamięć przy użyciu jasno definiowanych, podzielnych jednostek — możesz poprosić o 250 millicore CPU i 512MB pamięci, a harmonogram zgodnie z tym rozpakuje wiele małych pudeł na jeden węzeł, dzieląc podstawowy zasób. Kartki graficzne, standardowo jako zasób urządzenia w modelu pluginu Kubernetes, są harmonogramowane jako całości, niepodzielne jednostki: pudełko albo otrzymuje całkowitą kartę graficzną, albo nic, ponieważ do niedawna nie istniała standardowa i bezpieczna metoda pozwalająca dwóm niesolidarnym procesom dzielić pamięć i obliczenia karty graficznej bez przeszkadzania lub zniszczenia drugiego. To oznacza, że lighweight model potrzebujący tylko 2GB z 40GB pamięci karty graficznej nadal monopolizuje całą urządzenie podczas prostackiej harmonogramacji, co jest prawdziwym i nieefektywnym domyślnie, który spowodował powstanie pełnego ekosystemu workaroundów — sprzętowe podziałowanie na wielokrotne instancje karty graficznej (MIG) w nowszych karcach centrum danych firmy NVIDIA, oraz harmonogramy podzielone na czas, które pozwalają kilku pudełkom dzieląc obliczenia karty graficznej przez róznicę, co kosztuje nieco interwencji między obciążeniami.
Kartki graficzne węzły są również wystarczająco drogimi i rzadko występującymi z punktu widzenia skalowania chmury, że traktowanie ich jak zwykłe węzły obliczeniowe w grupie skalującej jest błąd, który zespoły często popełniają raz i potem naprawiają. Zbiór dedykowanych węzłów z taintami i tolerancjami specyficznymi dla kart graficznych (tak aby tylko pudełka wyraźnie żądające karty graficznej były harmonogramowane tam, co utrzymuje obcy ruch sieciowy od drogiego sprzętu), połączone z agresywnym skalowaniem do zera podczas wolności, jest standardową praktyką specjalnie dlatego, że instancje kart graficznych mogą kosztować od pięciu do dwudziestu razy więcej na godzinę w porównaniu do równych instancji CPU, a płacenie za długotrwałe zasoby A100 podczas nocy, ponieważ pulka węzłów dla batch inference nigdy nie skaliła się w dół, jest powszechnym i drogim początkowym błędem.
Wersjonowanie artefaktu, nie tylko kodu
Tag standardowej obrazu Docker (myapp:v1.4.2) zapisuje kod aplikacji, ale dla kontenera serwujących ML istnieje drugi artefakt — wagi modelu, który jest niezależnie wersjonowany i zmienia się na całkowicie innym tempie niż kod — grupa naukowo-dydaktyczna może ponownie trenować i wysyłać nową wersję modelu co tydzień, podczas gdy sam kod serwujący jest aktualizowany tylko raz na kwartał. Wklejanie wag bezpośrednio do obrazu (COPY model.bin do Dockerfile) oznacza, że każda aktualizacja modelu spowoduje pełną przestawienie obrazu i wysłanie wielogigabajtowego obrazu, co jest wolne i łączy dwie rzeczy, które powinny być niezależnie wdrożone i odwrotnie zrzucone. Popularniejszy wzorzec produkcyjny traktuje artefakt modelu jako zewnętrzne stan, który jest pobierany przy starcie kontenera (lub montowany poprzez init container) z rejestru modeli lub magazynu obiektów, a wersja artefaktu jest pinowana za pomocą hasha lub tagu odniesionego w konfiguracji wdrożenia, a nie w Dockerfile — tym sposobem cofnięcie się do złej wersji modelu nie wymaga ponownego przestawienia i wdrażania całego obrazu, tylko odwzorowania wdrożenia na poprzednią wersję artefaktu, co jest znacznie szybszym i bezpieczniejszym ścieżką cofnięcia się.
Inferyncja w czasie rzeczywistym versus dostarczanie w czasie rzeczywistym jako różne wzory kontenerów
Inferyncja w czasie rzeczywistym (interfejs API widoczny dla użytkownika, który musi odpowiedzieć w mniej niż 200 milisekund) i inferyncja w pulpicie (ocena dziesięciu milionów rekordów nocą do generowania dziennego raportu) są wystarczająco architekturalnie różne, że zazwyczaj są wdrożone jako różne rodzaje obciążenia Kubernetes, a nie tylko różne konfiguracje tego samego. Dostarczanie w czasie rzeczywistym jest typowo rozdzieleniem (Deployment) z automatycznym skalowaniem pionowego grupy kontenerów reagującym na głębokość kolejki żądań lub opóźnienie, utrzymywane ciepło ciągle, ponieważ opóźnienia w czasie startu są nieakceptowalne dla końcowego użytkownika czekającego na odpowiedź. Inferyncja w pulpicie jest znacznie naturalniejsza jako Kubernetes Job lub, dla naprawdę dużych obciążen, orkestrowany przez silnik przepływu pracy, tak jak Kubeflow Pipelines lub Argo Workflows, który może uruchomić pulę kontenerów GPU, rozdzielić dziesięciomilionowe rekordy między nimi i spienioną całą pływówkę do zera kosztu w chwili zakończenia zadania — wzór, który byłby odpadły dla dostarczania w czasie rzeczywistym (stałe opóźnienia startowe) ale jest dokładnie prawidłowy dla obciążenia typu szczytowego, planowanego, gdzie maksymalizujesz koszt całkowity i przepustowość zamiast opóźnień na żądanie.
Często zadawane pytania
Dlaczego Kubernetes nie może domyślnie dzielić jednego GPU między wiele małych podeszczalnych wersji modeli?
Standardowy plugin urządzenia GPU w Kubernetes traktuje GPU jako jednostkę niewydzielną, ponieważ historycznie nie było bezpiecznej standardowej metody izolacji pamięci i obliczeń GPU między niepowiązanymi procesami; nowe technologie podziału sprzętowego (MIG) oraz programowe harmonogramy czasowe istnieją specjalnie do obracania się tego ograniczenia.
Mam wstawić wagę modelu treningowej w obrazie Docker'a?
Zazwyczaj nie dla systemów produkcyjnych, ponieważ wagi modelu aktualizują się o wiele częściej niż kod aplikacji; pobieranie artefaktu modelu z zewnętrznego rejestru na starcie zachowuje oddzielne i odwracalne wersjonowanie modeli i kodu.
Dlaczego czas ładowania modelu ma większe znaczenie dla wdrożeń ML niż dla typowych usług web?
Ładowanie dużego modelu do pamięci RAM lub GPU przed uruchomieniem kontenera, który może służyć do obsługi ruchu, może zajmować od sekund do minut, co bezpośrednio wpływa na czas reakcji automatycznego skalowania i wymaga oznaczenia gotowości modelowej, aby balancer ruchu nie przekierowywał ruchu do nadal ładowanej podeszczalki.
Dlaczego zazwyczaj zadania inferencji w zestawach danych są wdrażane inaczej niż interaktywne API modeli?
Inferencja w zestawach danych jest porywcza i skupiona na maksymalizacji przepływności, więc pasuje do Kubernetes Job lub motoru pracy, który skaluje pulę GPU do góry i z powrotem do zera wokół zadania; interaktywne serwowanie musi być podtrzymane kontynuowanie, aby uniknąć opóźnienia startowego dla końcowych użytkowników, co pasuje do standardowej skalowanej Deployment.
▶ Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz Containerising Machine Learning: Why GPU Scheduling Changes the Rules i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.