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:
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
Parole chjave: Kubernetes, GKE, gestione di risorse, autoscaling, secreti
Crià un graficu cù Helm 3 per una megliu gestione di cluster
Tags: Helm 3, implementazione di carta
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)
$ 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
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.
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.