Amparate cumu implementà i microservizi. Part 1. Spring Boot è Docker

Amparate cumu implementà i microservizi. Part 1. Spring Boot è Docker

Hey Habr.

In questu articulu, vogliu parlà di a mo sperienza in a creazione di un ambiente di apprendimentu per sperimentà i microservizi. Quandu aghju amparatu ogni novu strumentu, aghju sempre vulutu pruvà micca solu nantu à a macchina lucale, ma ancu in cundizioni più realistichi. Per quessa, aghju decisu di creà una applicazione di microserviziu simplificata, chì dopu pò esse "coperta" cù ogni tipu di tecnulugia interessanti. U requisitu principalu per u prughjettu hè a so vicinanza massima funziunale à u sistema reale.

Inizialmente, aghju spartutu a creazione di u prugettu in parechji passi:

  1. Crea dui servizii - "backend" (backend) è "gateway" (gateway), imballate in l'imaghjini docker è cunfigurateli per travaglià inseme

    Parole chjave: Java 11, Spring Boot, Docker, ottimisazione di l'imagine

  2. Sviluppu di cunfigurazione Kubernetes è implementazione di u sistema in Google Kubernetes Engine

    Parole chjave: Kubernetes, GKE, gestione di risorse, autoscaling, secreti

  3. Crià un graficu cù Helm 3 per una megliu gestione di cluster

    Tags: Helm 3, implementazione di carta

  4. Configurazione di Jenkins è pipeline per a spedizione automatica di codice à u cluster

    Parole chjave: cunfigurazione Jenkins, plugins, repository di cunfigurazione separata

Pensu di dedicà un articulu separatu à ogni passu.

L'enfasi di sta serie d'articuli ùn hè micca cumu scrive i microservizi, ma cumu fà travaglià in un solu sistema. Ancu s'è tutte queste cose sò generalmente fora di a rispunsabilità di u sviluppatore, pensu chì hè sempre utile per esse familiarizatu cù elli almenu 20% (chì, cum'è sapete, dà 80% di u risultatu). Certi temi senza cundizzioni impurtanti, cum'è a sicurità, seranu lasciati fora di stu prughjettu, postu chì l'autore capisce pocu di stu sistema hè creatu solu per usu persunale. Aghju accoltu ogni opinione è critica constructiva.

Creazione di microservizi

I servizii sò stati scritti in Java 11 cù Spring Boot. L'interazione interservice hè urganizata cù REST. U prughjettu includerà un numeru minimu di teste (per chì dopu ci hè qualcosa per pruvà in Jenkins). U codice fonte per i servizii hè dispunibule nantu à GitHub: backend и Gateway.

Per pudè verificà l'estatus di ognuna di i servizii, un attuatore di primavera hè statu aghjuntu à e so dependenze. Crearà un endpoint /actuator/health è torna un statutu 200 se u serviziu hè prontu à accettà u trafficu, o 504 se ci hè un prublema. In questu casu, questu hè un cuntrollu piuttostu fittizio, postu chì i servizii sò assai simplici, è in casu di forza maiò, sò più prubabile di diventà cumplettamente indisponibili chè esse parzialmente operativi. Ma in i sistemi veri, l'Attuatore pò aiutà à diagnosticà un prublema prima chì l'utilizatori cumincianu à cumbatte. Per esempiu, s'ellu ci sò prublemi à accede à a basa di dati, pudemu risponde automaticamente à questu, cessendu e dumande di trasfurmazioni cù un esempiu di serviziu rottu.

U serviziu di back-end

U serviziu di backend cuntarà solu è restituverà u numeru di richieste accettate.

Codice di cuntrollu:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Test di cuntrollu:

@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

U gateway trasmetterà a dumanda à u serviziu di backend, cumplementendu cù l'infurmazioni seguenti:

  • gateway id. Hè necessariu cusì chì hè pussibule distingue una istanza di a porta di l'altru da a risposta di u servitore
  • Qualchì "secretu" chì ghjucà u rolu di una password assai impurtante (numeru di a chjave di criptografia di una cookie impurtante)

Configurazione in application.properties:

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

Adattatore backend:

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

Lanciamentu:

Cuminciamu u backend:

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

Accuminciari a porta:

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

Verificate:

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

Tuttu travaglia. Un lettore attentu hà da nutà chì nunda ùn ci impedisce di accede direttamente à u backend, saltendu u gateway (http://localhost:8081/requests). Per riparà questu, i servizii devenu esse cumminati in una rete, è solu a porta d'accessu deve "stà fora" fora.
Inoltre, i dui servizii sparte un sistema di fugliale, pruducenu flussi è in un mumentu ponu cumincià à interferiscenu l'un l'altru. Saria bellu isolà i nostri microservizi. Questu pò esse rializatu da distribuzendu applicazioni in diverse macchine (assai soldi, difficiuli), utilizendu macchine virtuali (intensive di risorse, startup longu), o usendu containerizazione. Comu s'aspittava, scegliemu a terza opzione è Docker cum'è un strumentu di containerizazione.

Docker

In corta, docker crea cuntenituri isolati, unu per applicazione. Per utilizà docker, avete bisognu di scrive un Dockerfile - struzzioni per a custruzzione è l'esecuzione di l'applicazione. In seguitu, pudete custruisce l'imaghjini, caricallu à u registru di l'imaghjini (n. Dockerhub) è implementà u vostru microserviziu in ogni ambiente dockerized in un cumandamentu.

dockerfile

Una di e caratteristiche più impurtanti di una maghjina hè a so dimensione. Una maghjina compacta scaricarà più veloce da un repositoriu remotu, occupanu menu spaziu, è u vostru serviziu principiarà più veloce. Qualchese imagine hè custruitu nantu à a basa di l'imaghjini di basa, è hè cunsigliatu di sceglie l'opzione più minimalista. Una bona opzione hè Alpine, una distribuzione Linux cumpleta cù pacchetti minimi.

Per principià, pruvemu di scrive un Dockerfile "nantu à a fronte" (dicu subitu chì questu hè un modu cattivu, ùn fate micca):

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

Quì usemu una maghjina di basa basata in Alpine cù u JDK digià stallatu per custruisce u nostru prughjettu. Cù u cumandimu ADD, aghjustemu u cartulare src attuale à l'imaghjini, marcate cum'è travagliu (WORKDIR) è cumincianu a custruisce. U cumandamentu EXPOSE 8080 signala à docker chì l'applicazione in u cuntinuu utilizerà u so portu 8080 (questu ùn farà micca l'applicazione accessibile da l'esternu, ma permetterà l'accessu à l'applicazione, per esempiu, da un altru cuntainer in a listessa rete docker. ).

Per imballà i servizii in l'imaghjini, avete bisognu di eseguisce cumandamenti da a radica di ogni prughjettu:

docker image build . -t msvc-backend:1.0.0

U risultatu hè una maghjina di 456 MB (di quale l'imaghjini JDK di basa occupava 340 MB). È tuttu malgradu u fattu chì e classi in u nostru prughjettu pò esse cuntatu nantu à un dettu. Per riduce a dimensione di a nostra maghjina:

  • Avemu aduprà assemblea multi-passu. In u primu passu avemu da custruisce u prughjettu, in u sicondu passu avemu da stallà u JRE, è in u terzu passu avemu da copià tuttu in una nova maghjina pulita Alpine. In totale, solu i cumpunenti necessarii seranu in l'imaghjini finali.
  • Utilizemu a modularizazione di java. Partendu da Java 9, pudete aduprà l'uttellu jlink per creà un JRE da solu i moduli chì avete bisognu

Per i curiosi, quì hè un bonu articulu nantu à l'avvicinamenti di riduzzione di l'imaghjini. https://habr.com/ru/company/ruvds/blog/485650/.

Dockerfile finale:

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

Ricreemu l'imaghjini, è in u risultatu, hà persu 6 volte u so pesu, in quantu à 77 MB. Micca male. Dopu questu, l'imaghjini pronti ponu esse caricati in u registru di l'imaghjini per chì e vostre imagine sò dispunibuli per scaricà da Internet.

Servizi di co-running in Docker

Per principià, i nostri servizii devenu esse nantu à a stessa reta. Ci sò parechji tippi di rete in docker, è avemu aduprà u più primitivu di elli - ponte, chì permette di rete di cuntenituri in u stessu host. Crea una reta cù u cumandimu seguente:

docker network create msvc-network

In seguitu, cuminciate u cuntinuu backend chjamatu "backend" cù l'immagine microservices-backend: 1.0.0:

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

Hè da nutà chì a reta di u ponte furnisce una scuperta di serviziu fora di a scatula per i cuntenituri per i so nomi. Questu hè, u serviziu di backend serà dispunibule in a reta di docker à http://backend:8080.

Accuminciari a porta:

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

In questu cumandamentu, indichemu chì trasmettemu u portu 80 di u nostru òspite à u portu 8080 di u containeru. Utilizemu l'opzioni di l'env per stabilisce e variabili di l'ambienti chì saranu automaticamente leghjite da a primavera è annullà e proprietà da application.properties.

Dopu avè principiatu, chjamemu http://localhost/ è assicuratevi chì tuttu funziona, cum'è in u casu precedente.

cunchiusioni

In u risultatu, avemu creatu dui microservizi simplici, imballate in cuntenituri docker è lanciati inseme nantu à a stessa macchina. U sistema risultatu, però, hà una quantità di svantaghji:

  • Povera tolleranza à i difetti - tuttu funziona per noi in un servitore
  • Scarsa scalabilità - quandu a carica aumenta, saria bonu per implementà automaticamente istanze di serviziu supplementari è equilibrà a carica trà elli.
  • A cumplessità di u lanciu - avemu bisognu di entre almenu 3 cumandamenti, è cù certi parametri (questu hè solu per 2 servizii)

Per risolve i prublemi di sopra, ci sò una quantità di suluzioni cum'è Docker Swarm, Nomad, Kubernetes o OpenShift. Se tuttu u sistema hè scrittu in Java, pudete guardà versu Spring Cloud (bonu articulu).

В parte dopu Parlaraghju di cumu aghju stallatu Kubernetes è implementatu u prugettu à Google Kubernetes Engine.

Source: www.habr.com

Add a comment