Ismerje meg a mikroszolgáltatások üzembe helyezését. 1. rész. Spring Boot és Docker

Ismerje meg a mikroszolgáltatások üzembe helyezését. 1. rész. Spring Boot és Docker

Szia Habr.

Ebben a cikkben a mikroszolgáltatásokkal való kísérletezéshez szükséges tanulási környezet létrehozásával kapcsolatos tapasztalataimról szeretnék beszélni. Amikor minden új eszközt megtanultam, nem csak a helyi gépen szerettem volna kipróbálni, hanem reálisabb körülmények között is. Ezért úgy döntöttem, hogy létrehozok egy egyszerűsített mikroszolgáltatási alkalmazást, amelyet később mindenféle érdekes technológiával "be lehet borítani". A projekt fő követelménye a valós rendszerhez való maximális funkcionális közelség.

Kezdetben a projekt létrehozását több lépésre bontottam:

  1. Hozzon létre két szolgáltatást – 'backend' (backend) és 'gateway' (gateway), csomagolja docker képekbe, és állítsa be őket, hogy működjenek együtt

    Kulcsszavak: Java 11, Spring Boot, Docker, képoptimalizálás

  2. Kubernetes konfiguráció és rendszertelepítés fejlesztése a Google Kubernetes Engine-ben

    Kulcsszavak: Kubernetes, GKE, erőforrás-kezelés, automatikus skálázás, titkok

  3. Diagram készítése a Helm 3 segítségével a jobb fürtkezelés érdekében

    Címkék: Helm 3, diagram telepítés

  4. A Jenkins és a folyamat beállítása a kódnak a fürtbe történő automatikus kézbesítéséhez

    Kulcsszavak: Jenkins konfiguráció, bővítmények, külön konfigurációs tároló

Minden lépésnek külön cikket tervezek szentelni.

Ennek a cikksorozatnak a középpontjában nem a mikroszolgáltatások megírása áll, hanem az, hogyan tegyük őket egyetlen rendszerben működőképessé. Bár ezek a dolgok általában kívül esnek a fejlesztő felelősségén, mégis hasznosnak tartom, ha legalább 20%-ban ismerkedsz velük (ami, mint tudod, az eredmény 80%-át adja). Néhány feltétel nélkül fontos téma, mint például a biztonság, kimarad ebből a projektből, mivel a szerző keveset ért arról, hogy ez a rendszer kizárólag személyes használatra készült. Minden véleményt és építő kritikát szívesen fogadok.

Mikroszolgáltatások létrehozása

A szolgáltatásokat Java 11-ben írták a Spring Boot segítségével. A szolgálatok közötti interakció a REST segítségével szerveződik. A projekt tartalmazni fog egy minimális számú tesztet (hogy később legyen mit tesztelni Jenkinsben). A szolgáltatások forráskódja elérhető a GitHubon: backend и Átjáró.

Annak érdekében, hogy ellenőrizni lehessen az egyes szolgáltatások állapotát, egy rugós működtetőt adtunk hozzá a függőségeikhez. Létrehoz egy /actuator/health végpontot, és 200-as állapotot ad vissza, ha a szolgáltatás készen áll a forgalom fogadására, vagy 504-et, ha probléma van. Jelen esetben ez egy meglehetősen fiktív ellenőrzés, mivel a szolgáltatások nagyon egyszerűek, és valamilyen vis maior esetén nagyobb valószínűséggel válnak teljesen elérhetetlenné, mintsem részben működjenek. A valós rendszerekben azonban az Actuator segíthet a probléma diagnosztizálásában, mielőtt a felhasználók elkezdenének küzdeni vele. Például, ha problémák merülnek fel az adatbázis elérésével, akkor automatikusan reagálhatunk erre úgy, hogy leállítjuk a meghibásodott szolgáltatáspéldány esetén a kérések feldolgozását.

Hátsó szolgáltatás

A háttérszolgáltatás egyszerűen megszámolja és visszaadja az elfogadott kérések számát.

Vezérlő kód:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Vezérlő teszt:

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

Service Gateway

Az átjáró továbbítja a kérést a háttérszolgáltatásnak, kiegészítve azt a következő információkkal:

  • átjáró azonosítója. Erre azért van szükség, hogy a szerver válasza alapján meg lehessen különböztetni az átjáró egyik példányát a másiktól
  • Valami "titok", amely egy nagyon fontos jelszó szerepét tölti be (egy fontos cookie titkosítási kulcsának száma)

Konfiguráció az application.properties-ben:

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

Háttér adapter:

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

Vezérlő:

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

Dob:

Elindítjuk a hátteret:

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

Az átjáró indítása:

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

Ellenőrizzük:

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

Minden működik. Egy figyelmes olvasó megjegyzi, hogy semmi sem akadályoz meg bennünket abban, hogy közvetlenül hozzáférjünk a háttérrendszerhez, megkerülve az átjárót (http://localhost:8081/requests). Ennek kijavításához a szolgáltatásokat egy hálózatba kell egyesíteni, és kívülről csak az átjáró "lógjon ki".
Ezenkívül mindkét szolgáltatás egy fájlrendszeren osztozik, adatfolyamokat hoz létre, és egy pillanat alatt interferálhatnak egymással. Jó lenne elkülöníteni a mikroszolgáltatásainkat. Ezt úgy érhetjük el, hogy alkalmazásokat terjesztünk különböző gépeken (sok pénz, nehéz), virtuális gépeket használunk (erőforrásigényes, hosszú indítás), vagy konténerezést alkalmazunk. A várakozásoknak megfelelően a harmadik lehetőséget választjuk és Dokkmunkás konténerezés eszközeként.

Dokkmunkás

Röviden, a docker izolált tárolókat hoz létre, alkalmazásonként egyet. A docker használatához meg kell írnia egy Dockerfile-t - utasításokat az alkalmazás felépítéséhez és futtatásához. Ezután összeállíthatja a képet, feltöltheti a képregisztrációba (No. Dockerhub), és egyetlen paranccsal telepítse a mikroszolgáltatást bármely dokkolós környezetben.

dockerfile

A kép egyik legfontosabb jellemzője a mérete. A kompakt kép gyorsabban töltődik le egy távoli adattárból, kevesebb helyet foglal el, és a szolgáltatás gyorsabban indul el. Bármely kép az alapkép alapján épül fel, és ajánlatos a legminimálisabb opciót választani. Jó lehetőség az Alpine, egy komplett Linux disztribúció minimális csomagokkal.

Először is próbáljunk meg egy Dockerfile-t írni "a homlokra" (azonnal mondom, hogy ez rossz út, ne tedd):

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

Itt egy Alpine alapú alapképet használunk a már telepített JDK-val a projektünk elkészítéséhez. Az ADD paranccsal hozzáadjuk az aktuális src könyvtárat a képhez, megjelöljük működőként (WORKDIR) és elindítjuk a buildet. Az EXPOSE 8080 parancs jelzi a dockernek, hogy a konténerben lévő alkalmazás a 8080-as portját fogja használni (ez nem teszi elérhetővé az alkalmazást kívülről, de lehetővé teszi az alkalmazás elérését például egy másik konténerről ugyanazon a docker hálózaton ).

A szolgáltatások képekbe csomagolásához parancsokat kell futtatnia az egyes projektek gyökeréből:

docker image build . -t msvc-backend:1.0.0

Az eredmény egy 456 MB-os kép (ebből az alap JDK-kép 340 MB-ot foglalt el). És mindez annak ellenére, hogy a projektünkben szereplő osztályok egy ujjon megszámolhatók. Képünk méretének csökkentése:

  • Többlépcsős összeszerelést alkalmazunk. Első lépésben megépítjük a projektet, második lépésben a JRE-t telepítjük, a harmadik lépésben pedig az egészet átmásoljuk egy új tiszta Alpine image-be. Összességében csak a szükséges alkatrészek lesznek a végső képen.
  • Használjuk a java modularizációját. A Java 9-től kezdve használhatja a jlink eszközt JRE létrehozására csak a szükséges modulokból

Az érdeklődők számára itt van egy jó cikk a képcsökkentési megközelítésekről. https://habr.com/ru/company/ruvds/blog/485650/.

Végső Docker-fájl:

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

Újra létrehoztuk a képet, és ennek eredményeként súlyának hatszorosát veszítette, ami 6 MB-ot jelent. Nem rossz. Ezt követően a kész képek feltölthetők a képnyilvántartásba, így a képei letölthetők az internetről.

Együttműködő szolgáltatások a Dockerben

Először is szolgáltatásainknak ugyanazon a hálózaton kell lenniük. A dockerben többféle hálózat létezik, és ezek közül a legprimitívebbet használjuk - a bridge-et, amely lehetővé teszi az ugyanazon a gazdagépen futó konténerek hálózati használatát. Hozzon létre egy hálózatot a következő paranccsal:

docker network create msvc-network

Ezután indítsa el a „backend” nevű háttértárolót a microservices-backend:1.0.0 képpel:

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

Érdemes megjegyezni, hogy a hídhálózat azonnali szolgáltatás-felderítést biztosít a konténerek nevük alapján. Vagyis a háttérszolgáltatás elérhető lesz a docker-hálózaton belül a címen http://backend:8080.

Az átjáró indítása:

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

Ebben a parancsban azt jelezzük, hogy a gazdagépünk 80-as portját a tároló 8080-as portjára továbbítjuk. Az env beállításokat használjuk a környezeti változók beállítására, amelyeket a rugó automatikusan beolvas, és felülbírálja az application.properties tulajdonságait.

Indítás után hívjuk http://localhost/ és győződjön meg arról, hogy minden működik, mint az előző esetben.

Következtetés

Ennek eredményeként két egyszerű mikroszolgáltatást hoztunk létre, ezeket docker konténerekbe csomagoltuk, és együtt indítottuk el ugyanazon a gépen. Az így létrejött rendszernek azonban számos hátránya van:

  • Gyenge hibatűrés - minden működik nekünk egy szerveren
  • Rossz skálázhatóság – ha a terhelés növekszik, jó lenne automatikusan telepíteni további szolgáltatáspéldányokat, és kiegyenlíteni a terhelést közöttük
  • Az indítás összetettsége - legalább 3 parancsot kellett megadnunk, és bizonyos paraméterekkel (ez csak 2 szolgáltatásra vonatkozik)

A fenti problémák megoldására számos megoldás létezik, például a Docker Swarm, a Nomad, a Kubernetes vagy az OpenShift. Ha az egész rendszer Java nyelven van írva, akkor a Spring Cloud felé nézhet (jó cikk).

В következő rész Beszélni fogok arról, hogyan állítottam be a Kuberneteset, és hogyan telepítettem a projektet a Google Kubernetes Engine-be.

Forrás: will.com

Hozzászólás