Siit saate teada, kuidas mikroteenuseid juurutada. 1. osa. Spring Boot ja Docker

Siit saate teada, kuidas mikroteenuseid juurutada. 1. osa. Spring Boot ja Docker

Tere Habr.

Selles artiklis tahan rääkida oma kogemusest mikroteenustega katsetamiseks mõeldud õpikeskkonna loomisel. Kui õppisin iga uut tööriista, tahtsin seda alati proovida mitte ainult kohalikul masinal, vaid ka realistlikumates tingimustes. Seetõttu otsustasin luua lihtsustatud mikroteenuse rakenduse, mida saab hiljem kõikvõimalike huvitavate tehnoloogiatega "katta". Projekti põhinõue on selle maksimaalne funktsionaalne lähedus reaalsele süsteemile.

Algselt jagasin projekti loomise mitmeks etapiks:

  1. Looge kaks teenust – "taustaprogramm" (taustaprogramm) ja "lüüs" (lüüs), pakkige need dockeri kujutistesse ja seadistage need koos töötama

    Märksõnad: Java 11, Spring Boot, Docker, pildi optimeerimine

  2. Kubernetese konfiguratsiooni arendamine ja süsteemi juurutamine Google Kubernetes Engine'is

    Märksõnad: Kubernetes, GKE, ressursihaldus, automaatne skaleerimine, saladused

  3. Helm 3 abil diagrammi loomine klastrite paremaks haldamiseks

    Sildid: Helm 3, diagrammi juurutamine

  4. Jenkinsi ja konveieri seadistamine koodi automaatseks edastamiseks klastris

    Märksõnad: Jenkinsi konfiguratsioon, pluginad, eraldi seadistuste hoidla

Plaanin pühendada igale etapile eraldi artikli.

Selle artiklisarja keskmes pole mitte mikroteenuste kirjutamine, vaid see, kuidas need ühes süsteemis töötama panna. Kuigi kõik need asjad jäävad tavaliselt arendaja vastutusalast välja, on minu meelest siiski kasulik olla nendega kursis vähemalt 20% ulatuses (mis teatavasti annab 80% tulemusest). Mõned tingimusteta olulised teemad, nagu turvalisus, jäetakse sellest projektist välja, kuna autor ei saa vähe aru, et süsteem on loodud ainult isiklikuks kasutamiseks. Ootan igasuguseid arvamusi ja konstruktiivset kriitikat.

Mikroteenuste loomine

Teenused on kirjutatud Java 11-s, kasutades Spring Booti. Talitustevaheline suhtlus korraldatakse REST-i abil. Projekt sisaldab minimaalset arvu teste (et hiljem oleks Jenkinsis midagi testida). Teenuste lähtekood on saadaval GitHubis: tagaprogramm и Värav.

Iga teenuse oleku kontrollimiseks on nende sõltuvustesse lisatud vedruajam. See loob /actuator/health lõpp-punkti ja tagastab oleku 200, kui teenus on liikluse vastuvõtmiseks valmis, või probleemi korral oleku 504. Antud juhul on tegemist üsna fiktiivse kontrolliga, kuna teenused on väga lihtsad ja vääramatu jõu korral muutuvad need tõenäolisemalt täielikult kättesaamatuks kui jäävad osaliselt tööle. Kuid reaalsetes süsteemides võib ajam aidata probleemi diagnoosida enne, kui kasutajad hakkavad selle pärast võitlema. Näiteks kui andmebaasile juurdepääsul on probleeme, saame sellele automaatselt vastata, peatades katkise teenuse eksemplari päringute töötlemise.

Tagumine teenus

Taustateenus lihtsalt loendab ja tagastab aktsepteeritud taotluste arvu.

Kontrolleri kood:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/requests")
    public Long getRequestsCount() {
        return counter.incrementAndGet();
    }
}

Kontrolleri test:

@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"));
    }
}

Teenindusvärav

Lüüs edastab päringu taustateenusele, täiendades seda järgmise teabega:

  • lüüsi ID. Seda on vaja selleks, et serveri vastuse järgi oleks võimalik lüüsi ühte eksemplari teisest eristada
  • Mingi "saladus", mis mängib väga tähtsa parooli rolli (olulise küpsise krüpteerimisvõtme number)

Konfiguratsioon rakenduses application.properties:

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

Taustaadapter:

@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();
    }
}

Kontroller:

@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);
    }
}

Käivitamine:

Alustame taustaprogrammi:

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

Lüüsi käivitamine:

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

Kontrollime:

$ curl http://localhost:8080/
Number of requests 1 (gateway 38560358, secret "default-secret")

Kõik töötab. Tähelepanelik lugeja märgib, et miski ei takista meil taustaprogrammile otse juurde pääseda, lüüsist mööda minnes (http://localhost:8081/requests). Selle parandamiseks tuleb teenused ühendada üheks võrku ja ainult lüüs peaks väljastpoolt "välja paistma".
Samuti jagavad mõlemad teenused ühte failisüsteemi, toodavad vooge ja võivad ühel hetkel hakata üksteist segama. Oleks tore meie mikroteenused isoleerida. Seda on võimalik saavutada, levitades rakendusi erinevatel masinatel (palju raha, raske), kasutades virtuaalmasinaid (ressursimahukas, pikk käivitamine) või kasutades konteineriseerimist. Ootuspäraselt valime kolmanda variandi ja laevalaadija konteineriseerimise vahendina.

laevalaadija

Lühidalt, docker loob isoleeritud konteinerid, ühe rakenduse kohta. Dockeri kasutamiseks tuleb kirjutada Dockerfile – juhised rakenduse koostamiseks ja käitamiseks. Järgmisena saate luua pildi, laadida selle pildiregistrisse (nr. Dockerhub) ja juurutage oma mikroteenus ühe käsuga mis tahes dokkimiskeskkonnas.

dockerfile

Pildi üks olulisemaid omadusi on selle suurus. Kompaktne pilt laaditakse kaughoidlast kiiremini alla, võtab vähem ruumi ja teie teenus käivitub kiiremini. Igasugune pilt on üles ehitatud baaspildi põhjal ning soovitatav on valida kõige minimalistlikum variant. Hea võimalus on Alpine, täielik Linuxi distributsioon minimaalsete pakettidega.

Esmalt proovime kirjutada Dockerfile'i "otsmikule" (ma ütlen kohe, et see on halb viis, ärge seda tehke):

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"]

Siin kasutame oma projekti koostamiseks Alpi-põhist baaspilti koos juba installitud JDK-ga. ADD käsuga lisame pildile praeguse src kataloogi, märgime selle töötavaks (WORKDIR) ja alustame ehitamist. Käsk EXPOSE 8080 annab dockerile märku, et konteineris olev rakendus kasutab oma porti 8080 (see ei muuda rakendust väljastpoolt ligipääsetavaks, vaid võimaldab rakendusele juurde pääseda näiteks samast dokkimisvõrgu teisest konteinerist ).

Teenuste piltidena pakkimiseks peate käivitama käsud iga projekti juurtest:

docker image build . -t msvc-backend:1.0.0

Tulemuseks on 456 MB pilt (millest JDK põhipilt võttis 340 MB). Ja seda hoolimata asjaolust, et meie projekti klasse saab näpu otsas üles lugeda. Meie pildi suuruse vähendamiseks toimige järgmiselt.

  • Kasutame mitmeastmelist montaaži. Esimeses etapis ehitame projekti, teises etapis paigaldame JRE ja kolmandas etapis kopeerime selle kõik uude puhtasse Alpi pilti. Kokku jäävad lõplikule pildile ainult vajalikud komponendid.
  • Kasutame java modulariseerimist. Alates Java 9-st saate kasutada jlinki tööriista JRE loomiseks just vajalikest moodulitest

Uudishimulike jaoks on siin hea artikkel pildi vähendamise lähenemisviiside kohta. https://habr.com/ru/company/ruvds/blog/485650/.

Lõplik Dockeri fail:

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"]

Loome pildi uuesti ja selle tulemusel kaotas see 6 korda oma kaalust, moodustades 77 MB. Pole paha. Pärast seda saab valmis pildid üles laadida pildiregistrisse, et teie pildid oleksid Internetist allalaadimiseks saadaval.

Teenuste kaastöötamine Dockeris

Alustuseks peavad meie teenused olema samas võrgus. Dockeris on mitut tüüpi võrke ja me kasutame neist kõige primitiivsemat - silda, mis võimaldab võrgustada samas hostis töötavaid konteinereid. Looge võrk järgmise käsuga:

docker network create msvc-network

Järgmisena käivitage taustakonteiner nimega 'backend' pildiga microservices-backend:1.0.0:

docker run -dit --name backend --network msvc-net microservices-backend:1.0.0

Väärib märkimist, et sillavõrk pakub konteineritele nende nimede järgi karbist avastamisteenust. See tähendab, et taustateenus on dokkimisvõrgus saadaval aadressil http://backend:8080.

Lüüsi käivitamine:

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

Selles käsus näitame, et edastame oma hosti pordi 80 konteineri pordile 8080. Kasutame suvandeid env, et määrata keskkonnamuutujad, mida vedru automaatselt loetakse, ja alistada atribuudid failist application.properties.

Peale käivitamist helistame http://localhost/ ja veenduge, et kõik töötab, nagu eelmisel juhul.

Järeldus

Selle tulemusena lõime kaks lihtsat mikroteenust, pakkisime need dokkimiskonteineritesse ja käivitasime koos samas masinas. Saadud süsteemil on aga mitmeid puudusi:

  • Kehv veataluvus – kõik töötab meil ühes serveris
  • Kehv skaleeritavus – koormuse kasvades oleks tore lisateenuse eksemplarid automaatselt juurutada ja nende vahel koormust tasakaalustada
  • Käivitamise keerukus - pidime sisestama vähemalt 3 käsku ja teatud parameetritega (see kehtib ainult 2 teenuse kohta)

Ülaltoodud probleemide lahendamiseks on mitmeid lahendusi, nagu Docker Swarm, Nomad, Kubernetes või OpenShift. Kui kogu süsteem on Java keeles kirjutatud, võite vaadata Spring Cloudi (hea artikkel).

В järgmine osa Räägin sellest, kuidas ma Kubernetese seadistasin ja projekti Google Kubernetes Engine'is juurutasin.

Allikas: www.habr.com

Lisa kommentaar