Learje hoe't jo mikrotsjinsten kinne ynsette. Diel 1. Spring Boot en Docker

Learje hoe't jo mikrotsjinsten kinne ynsette. Diel 1. Spring Boot en Docker

Hoi Habr.

Yn dit artikel wol ik prate oer myn ûnderfining yn it meitsjen fan in learomjouwing foar eksperimintearjen mei mikrotsjinsten. Doe't ik elk nij ark learde, woe ik it altyd net allinich besykje op 'e lokale masine, mar ek yn mear realistyske omstannichheden. Dêrom haw ik besletten om in ferienfâldige mikroservice-applikaasje te meitsjen, dy't letter kin wurde "bedekt" mei allerhanne nijsgjirrige technologyen. De wichtichste eask foar it projekt is syn maksimale funksjonele tichtby it echte systeem.

Yn earste ynstânsje bruts ik de skepping fan it projekt yn ferskate stappen:

  1. Meitsje twa tsjinsten - 'backend' (backend) en 'gateway' (gateway), pak se yn dockerôfbyldings en set se yn om gear te wurkjen

    Keywords: Java 11, Spring Boot, Docker, ôfbyldingsoptimalisaasje

  2. Untwikkeling fan Kubernetes-konfiguraasje en systeemynset yn Google Kubernetes Engine

    Keywords: Kubernetes, GKE, resource management, autoscaling, geheimen

  3. In diagram meitsje mei Helm 3 foar better klusterbehear

    Tags: Helm 3, diagram ynset

  4. Jenkins en pipeline ynstelle foar automatyske levering fan koade nei it kluster

    Keywords: Jenkins-konfiguraasje, plugins, apart konfiguraasjebewarplak

Ik plan om in apart artikel te wijen oan elke stap.

De fokus fan dizze searje artikels is net hoe't jo mikrotsjinsten skriuwe, mar hoe't jo se yn ien systeem kinne wurkje. Hoewol al dizze dingen meastentiids bûten de ferantwurdlikens fan 'e ûntwikkelder lizze, tink ik dat it noch altyd nuttich is om har op syn minst 20% bekend te meitsjen (wat, lykas jo witte, 80% fan it resultaat jouwe). Guon ûnbedoeld wichtige ûnderwerpen, lykas feiligens, sille út dit projekt wurde ferlitten, om't de skriuwer net folle begrypt dat dit systeem allinich foar persoanlik gebrûk is makke. Ik wolkom alle mieningen en konstruktive krityk.

It meitsjen fan mikrotsjinsten

De tsjinsten waarden skreaun yn Java 11 mei Spring Boot. Interservice ynteraksje wurdt organisearre mei REST. It projekt sil in minimum oantal tests befetsje (sadat der letter wat te testen is yn Jenkins). De boarnekoade foar de tsjinsten is beskikber op GitHub: efterkant и Poarte.

Om de status fan elk fan 'e tsjinsten te kontrolearjen, is in Spring Actuator tafoege oan har ôfhinklikens. It sil in / actuator / sûnens einpunt meitsje en in 200-status weromjaan as de tsjinst ree is om ferkear te akseptearjen, of in 504 as d'r in probleem is. Yn dit gefal is dit in nochal fiktive kontrôle, om't de tsjinsten heul ienfâldich binne, en yn gefal fan wat oermacht binne se mear kâns om folslein net beskikber te wurden dan foar in part operasjoneel te bliuwen. Mar yn echte systemen kin Actuator helpe om in probleem te diagnostearjen foardat brûkers deroer begjinne te fjochtsjen. Bygelyks, as der problemen binne mei tagong ta de databank, kinne wy ​​​​dêrop automatysk reagearje troch it ferwurkjen fan fersiken te stopjen mei in brutsen tsjinsteksimplaar.

Back-end tsjinst

De backend-tsjinst sil gewoan it oantal akseptearre oanfragen telle en weromjaan.

Controller koade:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

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

Service Gateway

De poarte sil it fersyk trochstjoere nei de backend-tsjinst, oanfoljende it mei de folgjende ynformaasje:

  • poarte id. It is nedich sadat it mooglik is om ien eksimplaar fan 'e poarte te ûnderskieden fan in oar troch de serverantwurd
  • Guon "geheim" dat sil spylje de rol fan in hiel wichtich wachtwurd (nûmer fan de fersifering kaai fan in wichtich koekje)

Konfiguraasje yn application.properties:

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

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

Controller:

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

Launch:

Wy begjinne de efterkant:

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

De poarte begjinne:

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

Wy kontrolearje:

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

Alles wurket. In oandachtige lêzer sil opmerke dat neat ús foarkomt om direkt tagong te krijen ta de backend, troch de poarte te omgean (http://localhost:8081/requests). Om dit te reparearjen, moatte de tsjinsten wurde kombineare yn ien netwurk, en allinich de poarte moat "útstekke" bûten.
Ek beide tsjinsten diele ien bestânsysteem, produsearje streamen en kinne op ien momint begjinne mei elkoar te bemuoien. It soe moai wêze om ús mikrotsjinsten te isolearjen. Dit kin berikt wurde troch it fersprieden fan applikaasjes op ferskate masines (in soad jild, lestich), mei help fan firtuele masines (boarne-yntinsive, lange opstarten), of mei help fan containerization. As ferwachte, wy kieze de tredde opsje en Havenarbeider as in helpmiddel foar containerization.

Havenarbeider

Koartsein, docker makket isolearre konteners, ien per applikaasje. Om docker te brûken, moatte jo in Dockerfile skriuwe - ynstruksjes foar it bouwen en útfieren fan de applikaasje. Dêrnei kinne jo de ôfbylding bouwe, it uploade nei it ôfbyldingsregister (nr. Dockerhub) en ynsette jo mikrotsjinst yn elke dockerisearre omjouwing yn ien kommando.

dockerfile

Ien fan 'e wichtichste skaaimerken fan in ôfbylding is har grutte. In kompakte ôfbylding sil rapper downloade fan in opslach op ôfstân, minder romte ynnimme, en jo tsjinst sil rapper begjinne. Elke ôfbylding is boud op basis fan 'e basisôfbylding, en it is oan te rieden om de meast minimalistyske opsje te kiezen. In goede opsje is Alpine, in folsleine Linux-distribúsje mei minimale pakketten.

Om te begjinnen, litte wy besykje in Dockerfile "op 'e foarholle" te skriuwen (ik sil daliks sizze dat dit in minne manier is, doch it net):

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

Hjir brûke wy in Alpine basearre basisôfbylding mei de JDK al ynstalleare om ús projekt te bouwen. Mei it kommando ADD foegje wy de hjoeddeistige src-map ta oan de ôfbylding, markearje it as wurkjen (WORKDIR) en begjinne de bou. It kommando EXPOSE 8080 sinjalearret oan docker dat de applikaasje yn 'e kontener syn poarte 8080 sil brûke (dit sil de applikaasje net fan bûten tagonklik meitsje, mar lit de applikaasje tagong wurde, bygelyks fan in oare kontener op itselde docker netwurk ).

Om tsjinsten yn bylden te pakken, moatte jo kommando's útfiere fan 'e root fan elk projekt:

docker image build . -t msvc-backend:1.0.0

It resultaat is in 456 MB-ôfbylding (wêrfan de basis JDK-ôfbylding 340 MB besette). En dit alles nettsjinsteande it feit dat de klassen yn ús projekt op in finger teld wurde kinne. Om de grutte fan ús ôfbylding te ferminderjen:

  • Wy brûke multi-stap gearkomste. Yn 'e earste stap sille wy it projekt bouwe, yn' e twadde stap sille wy de JRE ynstallearje, en yn 'e tredde stap sille wy it allegear kopiearje yn in nije skjinne Alpine-ôfbylding. Yn totaal sille allinich de nedige komponinten yn 'e definitive ôfbylding wêze.
  • Litte wy de modularisaasje fan java brûke. Begjinnend mei Java 9, kinne jo it jlink-ark brûke om in JRE te meitsjen fan allinich de modules dy't jo nedich binne

Foar de nijsgjirrige is hjir in goed artikel oer oanpak fan ôfbyldingsreduksje. https://habr.com/ru/company/ruvds/blog/485650/.

Finale Dockerfile:

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

Wy meitsje it byld op 'e nij, en as gefolch hat it 6 kear syn gewicht ferlern, dat is 77 MB. Net min. Dêrnei kinne klearmakke ôfbyldings opladen wurde nei it ôfbyldingsregister, sadat jo ôfbyldings te downloaden binne fan it ynternet.

Co-running tsjinsten yn Docker

Om te begjinnen moatte ús tsjinsten op itselde netwurk wêze. D'r binne ferskate soarten netwurken yn docker, en wy brûke de meast primitive fan har - brêge, wêrtroch jo konteners kinne netwurkje dy't op deselde host rinne. Meitsje in netwurk mei it folgjende kommando:

docker network create msvc-network

Begjin dêrnei de backend-container mei de namme 'backend' mei de microservices-backend: 1.0.0-ôfbylding:

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

It is de muoite wurdich op te merken dat it brêgenetwurk út 'e doaze tsjinst ûntdekking leveret foar konteners troch har nammen. Dat is, de backend-tsjinst sil beskikber wêze binnen it docker-netwurk by http://backend:8080.

De poarte begjinne:

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

Yn dit kommando jouwe wy oan dat wy poarte 80 fan ús host trochstjoere nei poarte 8080 fan 'e kontener. Wy brûke de env opsjes foar in set omjouwingsfariabelen dy't automatysk wurde lêzen troch maitiid en oerskriuwe eigenskippen fan application.properties.

Nei it starten belje wy http://localhost/ en soargje derfoar dat alles wurket, lykas yn it foarige gefal.

konklúzje

As gefolch hawwe wy twa ienfâldige mikrotsjinsten makke, se ferpakt yn docker-konteners en lansearre se tegearre op deselde masine. It resultearjende systeem hat lykwols in oantal neidielen:

  • Min skuldtolerânsje - alles wurket foar ús op ien tsjinner
  • Slechte skalberens - as de lading ferheget, soe it moai wêze om automatysk ekstra tsjinstynstânsjes yn te setten en de lading dertusken te balansearjen
  • De kompleksiteit fan 'e lansearring - wy moasten op syn minst 3 kommando's ynfiere, en mei bepaalde parameters (dit is allinich foar 2 tsjinsten)

Om de boppesteande problemen te reparearjen, binne d'r in oantal oplossingen lykas Docker Swarm, Nomad, Kubernetes of OpenShift. As it hiele systeem yn Java skreaun is, kinne jo nei Spring Cloud sjen (goed artikel).

В folgjende diel Ik sil prate oer hoe't ik Kubernetes ynstelle en it projekt ynset op Google Kubernetes Engine.

Boarne: www.habr.com

Add a comment