Scaling Scala: Cum să dockerizezi folosind Kubernetes

Publicat: 2022-03-11

Kubernetes este noul copil din bloc, promițând că va ajuta la implementarea aplicațiilor în cloud și le va scala mai rapid. Astăzi, atunci când dezvoltați o arhitectură de microservicii, este destul de standard să alegeți Scala pentru crearea de servere API.

Microserviciile înlocuiesc serverele back-end monolitice clasice cu mai multe servicii independente care comunică între ele și au propriile procese și resurse.

Dacă există o aplicație Scala în planurile dvs. și doriți să o scalați într-un nor, atunci sunteți la locul potrivit. În acest articol, voi arăta pas cu pas cum să luați o aplicație Scala generică și să implementez Kubernetes cu Docker pentru a lansa mai multe instanțe ale aplicației. Rezultatul final va fi o singură aplicație implementată ca mai multe instanțe și încărcare echilibrată de Kubernetes.

Toate acestea vor fi implementate prin simpla importare a kitului sursă Kubernetes în aplicația dvs. Scala. Vă rugăm să rețineți că kitul ascunde o mulțime de detalii complicate legate de instalare și configurare, dar este suficient de mic pentru a fi lizibil și ușor de înțeles dacă doriți să analizați ce face. Pentru simplitate, vom implementa totul pe mașina dvs. locală. Cu toate acestea, aceeași configurație este potrivită pentru o implementare în cloud a Kubernetes în lumea reală.

Scalați-vă aplicația Scala cu Kubernetes

Fii inteligent și dormi bine, scala-ți Docker-ul cu Kubernetes.
Tweet

Ce este Kubernetes?

Înainte de a intra în detaliile sângeroase ale implementării, să discutăm ce este Kubernetes și de ce este important.

Poate că ați auzit deja de Docker. Într-un fel, este o mașină virtuală ușoară.

Docker oferă avantajul implementării fiecărui server într-un mediu izolat, foarte asemănător cu o mașină virtuală autonomă, fără complexitatea gestionării unei mașini virtuale cu drepturi depline.

Din aceste motive, este deja unul dintre cele mai utilizate instrumente pentru implementarea aplicațiilor în cloud. O imagine Docker este destul de ușor și rapid de construit și de duplicat, mult mai ușor decât o mașină virtuală tradițională precum VMWare, VirtualBox sau XEN.

Kubernetes completează Docker, oferind un mediu complet pentru gestionarea aplicațiilor dockerizate. Folosind Kubernetes, puteți implementa, configura, orchestra, gestiona și monitoriza cu ușurință sute sau chiar mii de aplicații Docker.

Kubernetes este un instrument open source dezvoltat de Google și a fost adoptat de mulți alți furnizori. Kubernetes este disponibil nativ pe platforma cloud Google, dar alți furnizori l-au adoptat și pentru serviciile lor de cloud OpenShift. Poate fi găsit pe Amazon AWS, Microsoft Azure, RedHat OpenShift și chiar mai multe tehnologii cloud. Putem spune că este bine poziționat pentru a deveni un standard pentru implementarea aplicațiilor cloud.

Cerințe preliminare

Acum că am acoperit elementele de bază, haideți să verificăm dacă aveți instalat tot software-ul necesar. În primul rând, aveți nevoie de Docker. Dacă utilizați Windows sau Mac, aveți nevoie de Docker Toolbox. Dacă utilizați Linux, trebuie să instalați pachetul special furnizat de distribuția dvs. sau pur și simplu urmați instrucțiunile oficiale.

Vom codifica în Scala, care este un limbaj JVM. Aveți nevoie, desigur, de kitul de dezvoltare Java și instrumentul scala SBT instalat și disponibil în calea globală. Dacă sunteți deja un programator Scala, sunt șanse să aveți acele instrumente deja instalate.

Dacă utilizați Windows sau Mac, Docker va crea în mod implicit o mașină virtuală numită default cu doar 1 GB de memorie, care poate fi prea mică pentru a rula Kubernetes. Din experiența mea, am avut probleme cu setările implicite. Vă recomand să deschideți GUI VirtualBox, să selectați mașina virtuală default și să schimbați memoria la cel puțin 2048 MB.

Setări de memorie VirtualBox

Aplicația de clusterizare

Instrucțiunile din acest tutorial se pot aplica oricărei aplicații sau proiecte Scala. Pentru ca acest articol să aibă ceva „carne” la care să lucrez, am ales un exemplu folosit foarte des pentru a demonstra un microserviciu REST simplu în Scala, numit Akka HTTP. Vă recomand să încercați să aplicați kitul sursă la exemplul sugerat înainte de a încerca să îl utilizați în aplicația dvs. Am testat kitul cu aplicația demo, dar nu pot garanta că nu vor exista conflicte cu codul dvs.

Deci, mai întâi, începem prin a clona aplicația demo:

 git clone https://github.com/theiterators/akka-http-microservice

Apoi, testați dacă totul funcționează corect:

 cd akka-http-microservice sbt run

Apoi, accesați http://localhost:9000/ip/8.8.8.8 și ar trebui să vedeți ceva ca în imaginea următoare:

Microserviciul HTTP Akka rulează

Adăugarea kitului sursă

Acum, putem adăuga kitul sursă cu ceva magie Git:

 git remote add ScalaGoodies https://github.com/sciabarra/ScalaGoodies git fetch --all git merge ScalaGoodies/kubernetes

Cu asta, aveți demo, inclusiv kitul sursă și sunteți gata să încercați. Sau puteți chiar să copiați și să lipiți codul de acolo în aplicația dvs.

Odată ce ați îmbinat sau copiat fișierele din proiectele dvs., sunteți gata să începeți.

Pornește Kubernetes

Odată ce ați descărcat kitul, trebuie să descarcăm binarul kubectl necesar, rulând:

 bin/install.sh

Acest program de instalare este suficient de inteligent (sperăm) pentru a descărca binarul kubectl corect pentru OSX, Linux sau Windows, în funcție de sistemul dvs. Rețineți, programul de instalare a funcționat pe sistemele pe care le dețin. Vă rugăm să raportați orice problemă, astfel încât să pot repara kitul.

Odată ce ați instalat binarul kubectl , puteți porni întregul Kubernetes în Docker local. Doar alerga:

 bin/start-local-kube.sh

Prima dată când este rulată, această comandă va descărca imaginile întregii stive Kubernetes și un registru local necesar pentru a vă stoca imaginile. Poate dura ceva timp, așa că vă rugăm să aveți răbdare. De asemenea, rețineți că are nevoie de acces direct la internet. Dacă sunteți în spatele unui proxy, va fi o problemă, deoarece kitul nu acceptă proxy-uri. Pentru a o rezolva, trebuie să configurați instrumente precum Docker, curl și așa mai departe pentru a utiliza proxy-ul. Este destul de complicat încât recomand să obțineți un acces temporar nerestricționat.

Presupunând că ați putut descărca totul cu succes, pentru a verifica dacă Kubernetes funcționează bine, puteți tasta următoarea comandă:

 bin/kubectl get nodes

Răspunsul așteptat este:

 NAME STATUS AGE 127.0.0.1 Ready 2m

Rețineți că vârsta poate varia, desigur. De asemenea, deoarece pornirea Kubernetes poate dura ceva timp, poate fi necesar să invocați comanda de câteva ori înainte de a vedea răspunsul. Dacă nu primiți erori aici, felicitări, aveți Kubernetes în funcțiune pe mașina dvs. locală.

Dockerizarea aplicației dvs. Scala

Acum că aveți Kubernetes în funcțiune, vă puteți implementa aplicația în el. Pe vremuri, înainte de Docker, trebuia să implementezi un server întreg pentru a rula aplicația. Cu Kubernetes, tot ce trebuie să faceți pentru a vă implementa aplicația este:

  • Creați o imagine Docker.
  • Introduceți-l într-un registru de unde poate fi lansat.
  • Lansați instanța cu Kubernetes, care va prelua imaginea din registry.

Din fericire, este mult mai puțin complicat decât arată, mai ales dacă utilizați instrumentul de compilare SBT, așa cum o fac mulți.

În kit, am inclus două fișiere care conțineau toate definițiile necesare pentru a crea o imagine capabilă să ruleze aplicațiile Scala, sau cel puțin ceea ce este necesar pentru a rula demo-ul Akka HTTP. Nu pot garanta că va funcționa cu alte aplicații Scala, dar este un bun punct de plecare și ar trebui să funcționeze pentru multe configurații diferite. Fișierele de căutat pentru construirea imaginii Docker sunt:

 docker.sbt project/docker.sbt

Să aruncăm o privire la ce se află în ele. Fișierul project/docker.sbt conține comanda pentru a importa pluginul sbt-docker :

 addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0")

Acest plugin gestionează construirea imaginii Docker cu SBT pentru dvs. Definiția Docker este în fișierul docker.sbt și arată astfel:

 imageNames in docker := Seq(ImageName("localhost:5000/akkahttp:latest")) dockerfile in docker := { val jarFile: File = sbt.Keys.`package`.in(Compile, packageBin).value val classpath = (managedClasspath in Compile).value val mainclass = mainClass.in(Compile, packageBin).value.getOrElse(sys.error("Expected exactly one main class")) val jarTarget = s"/app/${jarFile.getName}" val classpathString = classpath.files.map("/app/" + _.getName) .mkString(":") + ":" + jarTarget new Dockerfile { from("anapsix/alpine-java:8") add(classpath.files, "/app/") add(jarFile, jarTarget) entryPoint("java", "-cp", classpathString, mainclass) } }

Pentru a înțelege pe deplin sensul acestui fișier, trebuie să cunoașteți Docker suficient de bine pentru a înțelege acest fișier de definiție. Cu toate acestea, nu intrăm în detaliile fișierului de definiție Docker, deoarece nu trebuie să îl înțelegeți bine pentru a construi imaginea.

Frumusețea utilizării SBT pentru construirea imaginii Docker este aceea
SBT se va ocupa de colectarea tuturor fișierelor pentru dvs.

Rețineți că clasa este generată automat de următoarea comandă:

 val classpath = (managedClasspath in Compile).value

În general, este destul de complicat să aduni toate fișierele JAR pentru a rula o aplicație. Folosind SBT, fișierul Docker va fi generat cu add(classpath.files, "/app/") . În acest fel, SBT colectează toate fișierele JAR pentru dvs. și construiește un Dockerfile pentru a vă rula aplicația.

Celelalte comenzi adună piesele lipsă pentru a crea o imagine Docker. Imaginea va fi construită folosind o imagine existentă APT pentru a rula programe Java ( anapsix/alpine-java:8 , disponibil pe internet în Docker Hub). Alte instrucțiuni sunt adăugarea altor fișiere pentru a rula aplicația. În cele din urmă, specificând un punct de intrare, îl putem rula. De asemenea, rețineți că numele începe cu localhost:5000 intenționat, deoarece localhost:5000 este locul unde am instalat registry în scriptul start-kube-local.sh .

Construirea imaginii Docker cu SBT

Pentru a construi imaginea Docker, puteți ignora toate detaliile fișierului Docker. Trebuie doar să tastați:

 sbt dockerBuildAndPush

sbt-docker va construi apoi o imagine Docker pentru dvs., descărcând de pe internet toate piesele necesare, apoi va împinge într-un registru Docker care a fost pornit anterior, împreună cu aplicația Kubernetes în localhost . Așadar, tot ce aveți nevoie este să așteptați puțin mai mult pentru a vă avea imaginea gătită și gata.

Rețineți, dacă întâmpinați probleme, cel mai bun lucru de făcut este să resetați totul la o stare cunoscută, rulând următoarele comenzi:

 bin/stop-kube-local.sh bin/start-kube-local.sh

Aceste comenzi ar trebui să oprească toate containerele și să le repornească corect pentru a pregăti registrul pentru a primi imaginea construită și împinsă de sbt .

Pornirea serviciului în Kubernetes

Acum că aplicația este ambalată într-un container și introdusă într-un registru, suntem gata să o folosim. Kubernetes folosește linia de comandă și fișierele de configurare pentru a gestiona clusterul. Deoarece liniile de comandă pot deveni foarte lungi și pot, de asemenea, să reproducă pașii, folosesc fișierele de configurare aici. Toate mostrele din kitul sursă sunt în folderul kube .

Următorul nostru pas este să lansăm o singură instanță a imaginii. O imagine care rulează se numește, în limbajul Kubernetes, un pod . Deci, să creăm un pod invocând următoarea comandă:

 bin/kubectl create -f kube/akkahttp-pod.yml

Acum puteți inspecta situația cu comanda:

 bin/kubectl get pods

Ar trebui sa vezi:

 NAME READY STATUS RESTARTS AGE akkahttp 1/1 Running 0 33s k8s-etcd-127.0.0.1 1/1 Running 0 7d k8s-master-127.0.0.1 4/4 Running 0 7d k8s-proxy-127.0.0.1 1/1 Running 0 7d

Starea poate fi diferită, de exemplu, „ContainerCreating”, poate dura câteva secunde înainte de a deveni „Running”. De asemenea, puteți obține un alt statut, cum ar fi „Eroare”, dacă, de exemplu, uitați să creați imaginea înainte.

De asemenea, puteți verifica dacă podul dumneavoastră rulează cu comanda:

 bin/kubectl logs akkahttp

Ar trebui să vedeți o ieșire care se termină cu ceva de genul acesta:

 [DEBUG] [05/30/2016 12:19:53.133] [default-akka.actor.default-dispatcher-5] [akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /0:0:0:0:0:0:0:0:9000

Acum aveți serviciul în funcțiune în interiorul containerului. Cu toate acestea, serviciul nu este încă accesibil. Acest comportament face parte din designul Kubernetes. Podul tău rulează, dar trebuie să îl expui în mod explicit. În caz contrar, serviciul este menit să fie intern.

Crearea unui serviciu

Crearea unui serviciu și verificarea rezultatului este o chestiune de executare:

 bin/kubectl create -f kube/akkahttp-service.yaml bin/kubectl get svc

Ar trebui să vezi așa ceva:

 NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE akkahttp-service 10.0.0.54 9000/TCP 44s kubernetes 10.0.0.1 <none> 443/TCP 3m

Rețineți că portul poate fi diferit. Kubernetes a alocat un port pentru serviciu și l-a pornit. Dacă utilizați Linux, puteți deschide direct browserul și tastați http://10.0.0.54:9000/ip/8.8.8.8 pentru a vedea rezultatul. Dacă utilizați Windows sau Mac cu Docker Toolbox, IP-ul este local pentru mașina virtuală care rulează Docker și, din păcate, este încă inaccesibil.

Vreau să subliniez aici că aceasta nu este o problemă a Kubernetes, mai degrabă este o limitare a Docker Toolbox, care, la rândul său, depinde de constrângerile impuse de mașinile virtuale precum VirtualBox, care acționează ca un computer într-un alt computer. Pentru a depăși această limitare, trebuie să creăm un tunel. Pentru a ușura lucrurile, am inclus un alt script care deschide un tunel pe un port arbitrar pentru a ajunge la orice serviciu pe care l-am implementat. Puteți tasta următoarea comandă:

 bin/forward-kube-local.sh akkahttp-service 9000

Rețineți că tunelul nu va rula în fundal, trebuie să țineți fereastra terminalului deschisă atâta timp cât aveți nevoie și să închideți când nu mai aveți nevoie de tunel. În timp ce tunelul rulează, puteți deschide: http://localhost:9000/ip/8.8.8.8 și în sfârșit puteți vedea aplicația rulând în Kubernetes.

Atingerea finală: scalare

Până acum am pus „pur și simplu” aplicația noastră în Kubernetes. Deși este o realizare interesantă, nu adaugă prea multă valoare implementării noastre. Suntem salvați de efortul de a încărca și instala pe un server și de a configura un server proxy pentru acesta.

Unde Kubernetes strălucește este în scalare. Puteți implementa două, zece sau o sută de instanțe ale aplicației noastre doar schimbând numărul de replici din fișierul de configurare. Deci hai sa o facem.

Vom opri un singur pod și vom începe o implementare. Deci, să executăm următoarele comenzi:

 bin/kubectl delete -f kube/akkahttp-pod.yml bin/kubectl create -f kube/akkahttp-deploy.yaml

Apoi, verificați starea. Din nou, puteți încerca de câteva ori, deoarece implementarea poate dura ceva timp:

 NAME READY STATUS RESTARTS AGE akkahttp-deployment-4229989632-mjp6u 1/1 Running 0 16s akkahttp-deployment-4229989632-s822x 1/1 Running 0 16s k8s-etcd-127.0.0.1 1/1 Running 0 6d k8s-master-127.0.0.1 4/4 Running 0 6d k8s-proxy-127.0.0.1 1/1 Running 0 6d

Acum avem două păstăi, nu una. Acest lucru se datorează faptului că în fișierul de configurare pe care l-am furnizat, există replica: 2 , cu două nume diferite generate de sistem. Nu intru în detaliile fișierelor de configurare, deoarece domeniul de aplicare al articolului este pur și simplu o introducere pentru programatorii Scala pentru a porni în Kubernetes.

Oricum, acum sunt două poduri active. Ceea ce este interesant este că serviciul este același ca înainte. Am configurat serviciul să echilibreze încărcarea între toate podurile etichetate akkahttp . Aceasta înseamnă că nu trebuie să redistribuim serviciul, dar putem înlocui instanța unică cu una replicată.

Putem verifica acest lucru lansând din nou proxy-ul (dacă sunteți pe Windows și l-ați închis):

 bin/forward-kube-local.sh akkahttp-service 9000

Apoi, putem încerca să deschidem două ferestre de terminal și să vedem jurnalele pentru fiecare pod. De exemplu, în primul tip:

 bin/kubectl logs -f akkahttp-deployment-4229989632-mjp6u

Și în al doilea tip:

 bin/kubectl logs -f akkahttp-deployment-4229989632-s822x

Desigur, editați linia de comandă în consecință cu valorile pe care le aveți în sistemul dumneavoastră.

Acum, încercați să accesați serviciul cu două browsere diferite. Ar trebui să vă așteptați să vedeți că cererile vor fi împărțite între mai multe servere disponibile, ca în imaginea următoare:

Kubernets în acțiune

Concluzie

Astăzi abia am zgâriat suprafața. Kubernetes oferă mult mai multe posibilități, inclusiv scalare și repornire automată, implementări incrementale și volume. În plus, aplicația pe care am folosit-o ca exemplu este foarte simplă, apatridă, diversele instanțe nu trebuie să se cunoască între ele. În lumea reală, aplicațiile distribuite trebuie să se cunoască între ele și trebuie să schimbe configurațiile în funcție de disponibilitatea altor servere. Într-adevăr, Kubernetes oferă un depozit de chei distribuit ( etcd ) pentru a permite diferitelor aplicații să comunice între ele atunci când sunt implementate noi instanțe. Cu toate acestea, acest exemplu este suficient de mic și simplificat pentru a vă ajuta să mergeți, concentrându-vă pe funcționalitățile de bază. Dacă urmați tutorialul, ar trebui să puteți obține un mediu de lucru pentru aplicația dvs. Scala pe mașina dvs. fără a fi confuz de un număr mare de detalii și să vă pierdeți în complexitate.

Legate de:
  • Dezvoltare pentru cloud în cloud: dezvoltare BigData cu Docker în AWS
  • K8s/Kubernetes: AWS vs. GCP vs. Azure
  • O comparație cu rețele de servicii Kubernetes