O soluție AI, dincolo de algoritmi și tehnici, este despre timpul petrecut în obținerea de rezultate semnificative, iar timpul se traduce în cerințe hardware și managementul resurselor. Cerințele variază în funcție de scopul urmărit. În unele cazuri, un model poate fi antrenat pe o configurație simplă cu un singur CPU/GPU, în timp ce în altele, sunt necesare semnificativ mai multe resurse decât cele disponibile pe o singură mașină. Complexitatea este influențată de cantitatea de date și de tehnicile utilizate pentru antrenarea modelului. O infrastructură care se poate adapta rapid la cerințe este esențială pentru obținerea rezultatelor- aici intervine Kubernetes.
Pentru a detalia ce înseamnă Kubernetes și de ce este relevant în contextul AI, vom analiza cerințele unui sistem AI:
Antrenarea modelului poate dura ore (sau zile), având nevoie de hardware specializat pentru acest lucru.
Managementul modelelor: în practică se antrenează mai multe modele care sunt apoi testate și comparate. Ulterior, promovarea modelelor în producție trebuie făcută într-un mod în care să nu întrerupă activitatea, trebuind să ofere posibilitatea monitorizării, precum și o eventuală operațiune de roll-back.
Utilizarea modelelor: predicțiile trebuie făcute rapid, folosind o multitudine de modele.
Disponibilitate și scalabilitate: un model bun este inutil dacă nu este ușor accesibil de către utilizatori și aplicații.
Raport costuri/eficiență: este nevoie de un echilibru între cerințele de performanță și constrângerile bugetare.
Există multe lucruri de discutat despre Kubernetes despre ce este sau ce nu este. În acest articol, vă voi prezenta aspectele generale ale Kubernetes și modul în care este utilizat pentru a construi soluții rapide, scalabile și de încredere. Articolul nu se vrea a fi un tutorial Kubernetes, ci mai degrabă să vă ofere argumente de ce Kubernetes este o platformă potrivită pentru AI.
Toate lucrurile au un început și acest început în cazul nostru este nevoia de containerizare. Ce este containerizarea și de ce avem nevoie de ea? Cei care își amintesc cum erau create și instalate aplicațiile în trecut își vor aminti, de asemenea, problemele cauzate de gestionarea și scalarea acestora. O problemă fundamentală era legată de faptul că procesele desfășurate aveau acces la toate resursele sistemului, cu puține mecanisme de constrângere. Prin resurse mă refer la lucruri precum CPU, RAM, disc, rețea și așa mai departe. De ce contează asta? Acest lucru este esențial, deoarece se dorește utilizarea centrelor de calcul în cel mai eficient și profitabil mod. Chiar dacă centrele de date folosesc mașini puternice (server class) cu o mulțime de procesoare, RAM, DISK, GPU-uri, TPU-uri, FPGA, aceste resurse nu sunt infinite și nici gratuite.
De altfel, centrele de calcul moderne și noțiunea de cloud public nu ar fi existat astăzi fără soluții pentru gestionarea resurselor și constrângeri de securitate.
Dar de ce centre de calcul? Nu am putea folosi hardware care nu este specializat, prin urmare, mai ieftin ( "commodity hardware") ? Un avantaj al folosirii acestui tip de hardware este faptul că, datorită costului scăzut, se poate folosi un număr mare de unități. Acest lucru nu este foarte practic, deoarece duce la probleme de spațiu fizic și consum de energie, implicând indirect costuri crescute și amprente mai mari de carbon. Această soluție pur și simplu nu scalează, lucru dovedit și în practică.
De asemenea, nu putem vorbi despre containerizare fără a vorbi despre mașini virtuale (VM). VM-urile există de mult timp, dar containerele sunt mai recente. VM-urile permit partiționarea resurselor mașinii fizice într-un mod care permite diferite sisteme de operare (OS) pe aceeași mașină fizică (alias bare-metal).
Figura 1 : VM vs. Container
Cum se poate observa în imaginea de mai sus, fiecare VM are propriul sistem de operare. Poți avea mai multe VM-uri cu același sistem de operare sau sisteme de operare diferite pe același server fizic. În cazul containerelor, toate folosesc același sistem de operare. Un singur sistem de operare poate gestiona mai multe containere, prin urmare, în acest caz totul va fi legat de un singur sistem de operare. Linux este utilizat în marea majoritate a cazurilor, dar există și containere bazate pe Windows.
La fel ca și în cazul imaginilor virtuale, fiecare container este atașat de sistemul pe care rulează, deosebirea majoră fiind în modul în care containerele interacționează cu sistemul de operare host, diferență care le face semnificativ mai rapide și reduce costul de exploatare. Acesta este motivul pentru care containerele au devenit atât de populare. În practică, mașinile virtuale au în continuare un rol important în separarea la nivel de cluster, dat fiind faptul că se pot crea clustere folosind diferite sisteme de operare și versiuni ale acestora. Aceasta înseamnă ca utilizatorii sunt feriți de interacțiunea directă cu sistemele bare-metal.
Din toate acestea apare întrebarea, la care nu am răspuns încă: ce sunt, de fapt, aceste containere? Fără a intra în detalii, containerele Linux sunt bazate pe CGroups, care este o caracteristică a kernelului Linux, care permite limitarea resurselor. Deși este un instrument puternic, aproape nimeni nu folosește CGroups direct, mai ales din cauza faptului că există instrumente care expun aceste caracteristici ale kernelului, cum ar fi Docker.
Alături de Docker trebuie menționată "Open Container Initiative", o specificație pentru containerizare, din cauza căreia Docker devine doar o implementare. În practică, toți furnizorii de soluții cloud folosesc predominant Docker. Există informații cum că Kubernetes ar fi în proces de a renunța treptat la Docker. Acest lucru se întâmplă ca urmare a implicării OCI (Open Container Initiative), care ar avea ca efect înlocuirea lui Docker (având la bază containerd) cu alte "container runtimes", de exemplu CRI-O. Fișierele de configurare Docker, utilizate de toată lumea, sunt și ele parte a OCI, prin urmare, nu există un impact major asupra aplicațiilor deja existente. Legat de acest subiect, Kata Containers (https://katacontainers.io/) prezintă interes și merită aprofundat.
Așadar, care este rolul Kubernetes în toata povestea asta? Nu este Docker suficient pentru a putea face deploy la containere și gestionare de resurse? Un răspuns scurt ar fi nu. Multe dintre arhitecturile serviciilor se bazează pe conceptul de micro-servicii sau soluția este compusă din diferite feluri de componente. Aceste componente au nevoie să poată comunica între ele, să gestioneze erorile, să actualizeze aspectele legate de securitate și așa mai departe. Gestionarea acestora la nivelul containerului este anevoioasă. Aici intervine Kubernetes ce permite definirea arhitecturii la un nivel mai înalt, și implicit un management bun al tuturor etapelor din ciclul de viață al unei soluții cum ar fi deploy, scaling și managementul erorilor. Concepte ca POD, Service, Job, Deployments permit o mai bună modularizare și împachetare a componentelor și taskurilor.
Cu toate că sunt numeroase documente și tutoriale online despre, mai mult sau mai puțin detaliate, expunem mai jos o privire de ansamblu asupra Kubernetes (k8s).
Este o grupare logică a mai multor containers care formează o singură unitate. POD e o construcție atomică care poate fi rulată într-un cluster. E important de reținut că POD-urile folosesc aceleași volume și rețea. Containerele din cadrul unui POD sunt tot timpul colocate și instalate împreună. Un pod este descris de către un fișier yaml.
Un Deployment oferă un mod declarativ pentru care descrie modul în care un POD este creat și gestionat. De exemplu, în acest mod se pot crea multiple instanțe ale aceluiași POD care rulează un serviciu. În trecut, serverele trebuiau să scaleze orizontal ca să poată îndeplini cerințele de redimensionare și trafic, oferind, în același timp, HA (high availability).
Este creat pentru a putea expune în afara unui cluster de Kubernetes o aplicație, acesta fiind, de asemenea, descris într-un fisier yaml.
Oferă o modalitate de a defini implementarea unui task prin unul sau mai multe poduri. Odată terminat acel task, resursele folosite sunt eliberate. Ex: ETL jobs, ML jobs etc.
Kubernetes permite definirea unor obiecte/resurse personalizate, prin apelul la fișierele yaml și prin crearea unor CustomResourceDefinition (CRD) pentru obiectele/resursele definite. Odată definite se pot crea multiple CR-uri (custom resources). CRD-urile sunt pentru CR ceea ce tipurile sunt pentru valori în limbajele de programare. Aceste concepte reprezintă fundația pentru Kube Operator pattern, parte care trebuie aprofundată dacă se dorește înțelegerea modului de funcționare a Kubernetes.
Există mai multe tipuri de resurse pentru Kubernetes: ReplicaSets, StatefulSets, DaemonSets, Secrets, ConfigMaps, Volumes, PersistentVolumeClaims etc. Acestea pot constitui la rândul lor, subiectul unui alt articol.
Cum se pot crea și gestiona toate aceste lucruri?
Folosind kubectl, un command line tool care se poate utiliza pentru a gestiona procesele din clusterul de Kubernetes. De exemplu, pentru a crea un POD ai nevoie de:
Un fisier .yaml care conține specificațiile POD-ului.
kubectl apply -f ./my-pod.yaml
kubectl get pods
Accesând clusterul de Kubernetes programatic. Acest lucru se poate face în moduri diferite:
Folosind Kube REST APIs.
Înarmați cu înțelegerea conceptelor din Kubernetes, putem să răspundem de ce este important în contextul AI. Dacă ne uităm la Machine Learning și AI, nu contează ce algoritm sau tehnici folosim, atâta timp cât nu le putem operaționaliza. Prin operaționalizare înțelegem următoarele:
Nevoia de resurse computaționale (CPU/GPU) pentru a putea rula algoritmii de Machine Learning/ Deep Learning training, Hyper Parameter Optimization, model selection etc. Aceste operații sunt costisitoare și necesită multiple CPU-uri, mult RAM. Recent, aceste execuții au început să valorifice și resurse de tipul GPU sau TPU. Dat fiind cerința mare și costul asociat acestor tipuri de resurse, este nevoie de un mod eficient de a planifica aceste execuții, de a le executa într-un mod controlat și nu, în ultimul rând, sigur.
Pe scurt, ca să poți operaționaliza AI, este nevoie de un mod de a putea aloca resurse de tip hardware și software.
Marii jucători din zona Cloud (Google, Amazon, IBM, Azure) expun, într-un fel sau altul, această funcționalitate. Ideea este simplă: un ML model este operaționalizat (deployed), fiind utilizat un API (REST/GRPC) pentru a face predicții în timp real. O arhitectură simplistă a unui serviciu de acest tip arată ca în Figura 2.
Figura 2
Putem menționa două situații:
Mai multe modele sunt colocate în același container. Prin urmare, aceste modele sunt încărcate și gestionate în memorie.
Proxy are rolul de transmite cererile către runtime-ul corect, realizând aceasta bazându-se pe metadata care este, de obicei, persistat într-o bază de date (cel mai probabil ETCD). De asemenea, poate îndeplini funcții de genul authentication (like OpenId Connect- token validation) și/sau autorizare.
Se poate observa că există mai multe runtimes care sunt materializate sub forma a mai multe POD-uri. Acest lucru înseamnă că putem folosi cu ușurință runtimes bazate pe Python, native(C/C++, RUST), JVM based, R și altele. Acest lucru este important având în vedere că peisajul frameworkurilor de ML este destul de mare și sunt utilizate diferite limbaje de programare, care la rândul lor au dependințe diferite.
În acest caz, lucrurile sunt un pic mai simple, dat fiind faptul ca ideea de bază este:
Citește datele din baza de date.
Se face o predicție pentru fiecare înregistrare din setul de date.
Odată ce jobul se termină, clusterul eliberează resursele folosite de procesele jobului.
Procedeul este similar cu cel de mai sus:
Se consumă evenimentele dintr-un stream de date (ex: Kafka, AMPQ).
Se face o predicție pentru fiecare înregistrare din setul de date.
Diferența dintre streaming și batch este că procesul nu se întrerupe automat. În cazul streamingului este nevoie de management, deoarece este un proces continuu. Din acest punct de vedere similar cu "Models as service".
AI înseamnă mai mult mai mult decât antrenare de model și executare de predicții. Aplicațiile AI folosesc multiple tehnici de machine learning pentru a rezolva probleme. Utilizatorii acestor aplicații nu văd și, de obicei, nu sunt interesați de ce se întâmplă în spatele cortinei. Ei sunt interesați doar de deciziile luate de către aplicație, decizii care încep să semene din ce în ce mai mult cu deciziile pe care le-ar lua o persoană reală. Kubernetes devine din ce în ce mai interesant pentru soluțiile de cloud publice și private ori pentru soluțiile enterprise din perspectiva CI/CD, scalare, monitorizare, costuri de operare, oferind rapid soluții solide pentru consumatori și cu costuri de exploatare minime.