Sužinokite, kaip įdiegti mikropaslaugas. 1 dalis. Spring Boot ir Docker

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:

  1. 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

  2. Kubernetes konfigūracijos kūrimas ir sistemos diegimas Google Kubernetes Engine

    Raktiniai žodžiai: Kubernetes, GKE, išteklių valdymas, automatinis mastelio keitimas, paslaptys

  3. Diagramos kūrimas naudojant „Helm 3“, kad būtų galima geriau valdyti klasterius

    Žymos: Helm 3, diagramos diegimas

  4. „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)

Konfigūracija programoje Application.properties:

backend.url=http://localhost:8081
instance.id=${random.int}
secret="default-secret"

Backend adapteris:

@Service
public class BackendAdapter {

    private static final String REQUESTS_ENDPOINT = "/requests";

    private final RestTemplate restTemplate;

    @Value("${backend.url}")
    private String backendUrl;

    public BackendAdapter(RestTemplateBuilder builder) {
        restTemplate = builder.build();
    }

    public String getRequests() {
        ResponseEntity<String> response = restTemplate.getForEntity(
backendUrl + REQUESTS_ENDPOINT, String.class);
        return response.getBody();
    }
}

Valdiklis:

@RestController
@RequiredArgsConstructor
public class EndpointController {

    private final BackendAdapter backendAdapter;

    @Value("${instance.id}")
    private int instanceId;

    @Value("${secret}")
    private String secret;

    @GetMapping("/")
    public String getRequestsCount() {
        return String.format("Number of requests %s (gateway %d, secret %s)", backendAdapter.getRequests(), instanceId, secret);
    }
}

Paleisti:

Pradedame backend:

./mvnw package -DskipTests
java -Dserver.port=8081 -jar target/microservices-backend-1.0.0.jar

Vartai paleidžiami:

./mvnw package -DskipTests
java -jar target/microservices-gateway-1.0.0.jar

Mes patikriname:

$ 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ų

Smalsiems čia yra geras straipsnis apie vaizdo mažinimo būdus. https://habr.com/ru/company/ruvds/blog/485650/.

Galutinis Docker failas:

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.

Vartai paleidžiami:

docker run -dit -p 80:8080 --env secret=my-real-secret --env BACKEND_URL=http://backend:8080/ --name gateway --network msvc-net microservices-gateway:1.0.0

Š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“.

Šaltinis: www.habr.com

Добавить комментарий