Sužinokite, kaip įdiegti mikropaslaugas. 1 dalis. Spring Boot ir Docker
Sveiki, Habr.
Šiame straipsnyje noriu papasakoti apie savo patirtį kuriant mokymosi aplinką eksperimentuoti su mikropaslaugomis. Kai išmokau kiekvieną naują įrankį, visada norėjau jį išbandyti ne tik vietinėje mašinoje, bet ir tikroviškesnėmis sąlygomis. Todėl nusprendžiau sukurti supaprastintą mikroserviso aplikaciją, kurią vėliau galima „uždengti“ visokiomis įdomiomis technologijomis. Pagrindinis reikalavimas projektui yra maksimalus jo funkcinis artumas realiai sistemai.
Iš pradžių projekto kūrimą suskaidžiau į kelis etapus:
Sukurkite dvi paslaugas – „backend“ (backend) ir „gateway“ (šliuzai), supakuokite jas į „Docker“ vaizdus ir nustatykite, kad jos veiktų kartu.
Raktiniai žodžiai: Java 11, Spring Boot, Docker, vaizdo optimizavimas
Raktiniai žodžiai: Kubernetes, GKE, išteklių valdymas, automatinis mastelio keitimas, paslaptys
Diagramos kūrimas naudojant „Helm 3“, kad būtų galima geriau valdyti klasterius
Žymos: Helm 3, diagramos diegimas
„Jenkins“ ir vamzdyno nustatymas automatiniam kodo pristatymui į klasterį
Raktiniai žodžiai: Jenkins konfigūracija, papildiniai, atskira konfigūracijų saugykla
Kiekvienam žingsniui planuoju skirti atskirą straipsnį.
Šios straipsnių serijos dėmesys sutelkiamas ne į tai, kaip parašyti mikropaslaugas, o kaip priversti jas veikti vienoje sistemoje. Nors už visus šiuos dalykus dažniausiai neprisiima kūrėjo atsakomybė, manau, vis tiek pravartu su jais susipažinti bent 20% (kas, kaip žinia, duoda 80% rezultato). Kai kurios besąlygiškai svarbios temos, tokios kaip saugumas, šiame projekte bus išbrauktos, nes autorius mažai supranta, kad ši sistema sukurta tik asmeniniam naudojimui. Laukiu bet kokios nuomonės ir konstruktyvios kritikos.
Mikropaslaugų kūrimas
Paslaugos buvo parašytos Java 11 naudojant Spring Boot. Tarnybų sąveika organizuojama naudojant REST. Į projektą bus įtrauktas minimalus testų skaičius (kad vėliau būtų ką išbandyti Jenkinse). Paslaugų šaltinio kodas yra „GitHub“: backend и Vartai.
Kad būtų galima patikrinti kiekvienos paslaugos būseną, prie jų priklausomybių buvo pridėta spyruoklinė pavara. Jis sukurs /actuator/health galutinį tašką ir grąžins 200 būseną, jei paslauga yra pasirengusi priimti srautą, arba 504, jei kyla problemų. Šiuo atveju tai yra gana fiktyvus patikrinimas, nes paslaugos yra labai paprastos, o įvykus nenugalimos jėgos aplinkybėms, jos greičiausiai taps visiškai nepasiekiamos nei iš dalies veiks. Tačiau tikrose sistemose pavara gali padėti diagnozuoti problemą prieš vartotojams pradedant dėl jos kovoti. Pavyzdžiui, jei kyla problemų pasiekiant duomenų bazę, galime automatiškai į tai atsakyti sustabdydami užklausų apdorojimą, kai paslaugos egzempliorius sugenda.
Galinė paslauga
Užpakalinė paslauga tiesiog suskaičiuos ir grąžins priimtų užklausų skaičių.
Valdiklio kodas:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}
Valdiklio testas:
@WebMvcTest(RequestsCounterController.class)
public class RequestsCounterControllerTests {
@Autowired
private MockMvc mockMvc;
@Test
public void firstRequest_one() throws Exception {
mockMvc.perform(get("/requests"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().string("1"));
}
}
Paslaugų vartai
Vartai persiųs užklausą užpakalinei paslaugai, papildydami ją šia informacija:
vartai ID. Jis reikalingas tam, kad pagal serverio atsakymą būtų galima atskirti vieną šliuzo egzempliorių nuo kito
Kažkokia „paslaptis“, kuri atliks labai svarbaus slaptažodžio vaidmenį (svarbaus slapuko šifravimo rakto numeris)
$ curl http://localhost:8080/
Number of requests 1 (gateway 38560358, secret "default-secret")
Viskas veikia. Dėmesingas skaitytojas pastebės, kad niekas netrukdo mums tiesiogiai pasiekti užpakalinės programos, apeinant šliuzą (http://localhost:8081/requests). Kad tai išspręstų, paslaugos turi būti sujungtos į vieną tinklą, o tik šliuzas turi „išlipti“ išorėje.
Be to, abi paslaugos dalijasi viena failų sistema, sukuria srautus ir vienu metu gali pradėti trukdyti viena kitai. Būtų gerai atskirti mūsų mikroservisus. Tai galima pasiekti paskirstant programas skirtingose mašinose (daug pinigų, sudėtinga), naudojant virtualias mašinas (reikali resursai, ilgas paleidimas) arba naudojant konteinerizavimą. Kaip ir tikėtasi, pasirenkame trečią variantą ir dokininkas kaip konteinerizavimo įrankis.
dokininkas
Trumpai tariant, dokeris sukuria atskirus konteinerius, po vieną kiekvienai programai. Norėdami naudoti docker, turite parašyti Dockerfile - programos kūrimo ir paleidimo instrukcijas. Tada galite sukurti vaizdą, įkelti jį į vaizdų registrą (Nr. „DockerHub“) ir viena komanda įdiekite savo mikropaslaugą bet kurioje dokerinėje aplinkoje.
dockerfile
Viena iš svarbiausių vaizdo savybių yra jo dydis. Kompaktiškas vaizdas bus greičiau atsisiunčiamas iš nuotolinės saugyklos, užims mažiau vietos ir jūsų paslauga bus pradėta greičiau. Bet koks vaizdas kuriamas remiantis pagrindiniu įvaizdžiu, todėl rekomenduojama pasirinkti minimalistinį variantą. Geras pasirinkimas yra „Alpine“, pilnas „Linux“ platinimas su minimaliais paketais.
Pirma, pabandykime parašyti Dockerfile "ant kaktos" (iš karto pasakysiu, kad tai yra blogas būdas, nedarykite to):
FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine
ADD . /src
WORKDIR /src
RUN ./mvnw package -DskipTests
EXPOSE 8080
ENTRYPOINT ["java","-jar","target/microservices-gateway-1.0.0.jar"]
Kurdami projektą naudojame Alpių pagrindu sukurtą bazinį vaizdą su jau įdiegtu JDK. Su komanda ADD į atvaizdą įtraukiame esamą src katalogą, pažymime jį kaip veikiantį (WORKDIR) ir pradedame kurti. Komanda EXPOSE 8080 signalizuoja dokeriui, kad konteineryje esanti programa naudos savo prievadą 8080 (dėl to programa nebus pasiekiama iš išorės, bet leis pasiekti programą, pavyzdžiui, iš kito konteinerio tame pačiame dokų tinkle).
Norėdami supakuoti paslaugas į vaizdus, turite paleisti komandas iš kiekvieno projekto šaknies:
docker image build . -t msvc-backend:1.0.0
Rezultatas yra 456 MB vaizdas (iš kurio pagrindinis JDK vaizdas užėmė 340 MB). Ir viskas nepaisant to, kad mūsų projekto klases galima suskaičiuoti ant piršto. Norėdami sumažinti vaizdo dydį:
Mes naudojame kelių pakopų surinkimą. Pirmuoju žingsniu sukursime projektą, antrame žingsnyje įdiegsime JRE, o trečiu žingsniu nukopijuosime visa tai į naują švarų Alpių vaizdą. Iš viso galutiniame paveikslėlyje bus tik būtini komponentai.
Pasinaudokime java moduliavimu. Pradėdami nuo „Java 9“, galite naudoti „jlink“ įrankį, kad sukurtumėte JRE tik iš jums reikalingų modulių
FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine as builder
ADD . /src
WORKDIR /src
RUN ./mvnw package -DskipTests
FROM alpine:3.10.3 as packager
RUN apk --no-cache add openjdk11-jdk openjdk11-jmods
ENV JAVA_MINIMAL="/opt/java-minimal"
RUN /usr/lib/jvm/java-11-openjdk/bin/jlink
--verbose
--add-modules
java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument
--compress 2 --strip-debug --no-header-files --no-man-pages
--release-info="add:IMPLEMENTOR=radistao:IMPLEMENTOR_VERSION=radistao_JRE"
--output "$JAVA_MINIMAL"
FROM alpine:3.10.3
LABEL maintainer="Anton Shelenkov [email protected]"
ENV JAVA_HOME=/opt/java-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"
COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
COPY --from=builder /src/target/microservices-backend-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
Atkuriame vaizdą ir dėl to jis prarado 6 kartus daugiau svorio, ty 77 MB. Neblogai. Po to paruoštus vaizdus galima įkelti į vaizdų registrą, kad jūsų vaizdus būtų galima atsisiųsti iš interneto.
Bendrai vykdomos paslaugos „Docker“.
Pirmiausia mūsų paslaugos turi būti tame pačiame tinkle. „Docker“ yra kelių tipų tinklai, ir mes naudojame primityviausią iš jų - tiltą, kuris leidžia tinkinti konteinerius, veikiančius tame pačiame pagrindiniame kompiuteryje. Sukurkite tinklą naudodami šią komandą:
docker network create msvc-network
Tada paleiskite užpakalinės dalies talpyklą, pavadintą „backend“, naudodami vaizdą „microservices-backend:1.0.0“:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0
Verta paminėti, kad tilto tinklas teikia konteinerių aptikimo paslaugą pagal jų pavadinimus. Tai reiškia, kad užpakalinė paslauga bus pasiekiama dokų tinkle adresu http://backend:8080.
Šioje komandoje nurodome, kad mūsų pagrindinio kompiuterio 80 prievadą persiunčiame į konteinerio 8080 prievadą. Naudojame env parinktis, norėdami nustatyti aplinkos kintamuosius, kurie bus automatiškai nuskaityti spyruoklėje, ir nepaisyti ypatybių iš application.properties.
Pradėję skambiname http://localhost/ ir įsitikinkite, kad viskas veikia, kaip ir ankstesniu atveju.
išvada
Dėl to sukūrėme dvi paprastas mikro paslaugas, supakavome jas į doko konteinerius ir kartu paleidome toje pačioje mašinoje. Tačiau sukurta sistema turi keletą trūkumų:
Prasta gedimų tolerancija – viskas mums veikia viename serveryje
Prastas mastelio keitimas – padidėjus apkrovai būtų malonu automatiškai įdiegti papildomus paslaugų egzempliorius ir subalansuoti apkrovą tarp jų
Paleidimo sudėtingumas - mums reikėjo įvesti bent 3 komandas ir su tam tikrais parametrais (tai taikoma tik 2 paslaugoms)
Norint išspręsti minėtas problemas, yra keletas sprendimų, tokių kaip Docker Swarm, Nomad, Kubernetes arba OpenShift. Jei visa sistema parašyta Java, galite žiūrėti į Spring Cloud (geras straipsnis).
В kita dalis Pakalbėsiu apie tai, kaip sukūriau „Kubernetes“ ir įdiegiau projektą „Google Kubernetes Engine“.