Ikasi mikrozerbitzuak zabaltzen. 1. zatia. Spring Boot eta Docker
Kaixo Habr.
Artikulu honetan, nire esperientziari buruz hitz egin nahi dut mikrozerbitzuekin esperimentatzeko ikasteko ingurune bat sortzean. Tresna berri bakoitza ikastean, beti probatu nahi izan dut nire tokiko makinan ez ezik, baldintza errealagoetan ere. Hori dela eta, mikrozerbitzuen aplikazio sinplifikatu bat sortzea erabaki nuen, gerora teknologia interesgarri guztiekin "zintzilikatu" ahal izateko. Proiektuaren baldintza nagusia sistema errealarekiko gehieneko hurbiltasun funtzionala da.
Hasieran, proiektuaren sorrera hainbat urratsetan banatu nuen:
Sortu bi zerbitzu: 'backend' eta 'gateway', ontziratu docker irudietan eta konfiguratu elkarrekin lan egiteko
Gako-hitzak: Java 11, Spring Boot, Docker, irudien optimizazioa
Urrats bakoitzari artikulu bana eskaintzeko asmoa dut.
Artikulu sorta honen ardatza ez da mikrozerbitzuak nola idatzi, sistema bakar batean funtzionatzea baizik. Gauza hauek guztiak garatzailearen arduratik kanpo egon ohi diren arren, uste dut oraindik ere erabilgarria dela haiek gutxienez % 20 ezagutzea (emaitzaren % 80 hartzen duela jakina). Erabat garrantzitsuak diren gai batzuk, hala nola, segurtasuna, proiektu honetatik kanpo geratuko dira, egileak ezer gutxi ulertzen baitu honi buruz; sistema erabilera pertsonalerako soilik sortzen ari da. Pozten ditut edozein iritzi eta kritika eraikitzaile.
Mikrozerbitzuak sortzea
Zerbitzuak Java 11n idatzi ziren Spring Boot erabiliz. Zerbitzuen arteko komunikazioa REST erabiliz antolatzen da. Proiektuak gutxieneko proba kopuru bat barne hartuko du (gero Jenkinsen probatzeko zerbait egongo da). Zerbitzuen iturburu kodea GitHub-en dago eskuragarri: backend ΠΈ Pasabidea.
Zerbitzu bakoitzaren egoera egiaztatu ahal izateko, Spring Actuator bat gehitu zitzaion haien menpekotasunari. Amaiera-puntua /eragintzailea/osasuna sortuko du eta 200 egoera itzuliko du zerbitzua trafikoa onartzeko prest badago, edo 504 arazoak izanez gero. Kasu honetan, fikziozko egiaztapena da, zerbitzuak oso sinpleak baitira, eta ezinbesteko kasuren baten ondorioz guztiz erabilgarri ez egotea litekeena da partzialki funtzionatzen jarraitzea baino. Baina benetako sistemetan, Erabiltzaileak arazo bat diagnostikatzen lagun dezake erabiltzaileak jotzen hasi aurretik. Adibidez, datu-baserako sarbidearekin arazoak sortzen badira, horri automatikoki erantzuteko gai izango gara eskaerak prozesatzeari utziz zerbitzuaren instantzia hautsi batekin.
Backend zerbitzua
Backend zerbitzuak onartutako eskaera kopurua zenbatu eta itzuliko du.
Kontrolagailuaren kodea:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}
Kontrolagailuaren proba:
@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"));
}
}
Pasabide zerbitzua
Pasabideak backend zerbitzura helaraziko du eskaera, informazio honekin osatuz:
atebidearen id. Beharrezkoa da zerbitzariaren erantzunaren arabera atebidearen instantzia bat beste batetik bereizteko
Pasahitz oso garrantzitsu baten papera beteko duen "sekretu" jakin bat (cookie garrantzitsu bat enkriptatzeko gako-zenbakia)
$ curl http://localhost:8080/
Number of requests 1 (gateway 38560358, secret "default-secret")
Dena dabil. Irakurle adiak ohartuko da ezerk ez digula eragozten backend-era zuzenean sartzea, atebidea saihestuz (http://localhost:8081/requests). Hori konpontzeko, zerbitzuak sare batean konbinatu behar dira, eta atebidea bakarrik kanpoan "irten" behar da.
Gainera, bi zerbitzuek fitxategi-sistema bera partekatzen dute, hariak sortzen dituzte eta une batean elkar oztopatzen has daitezke. Polita litzateke gure mikrozerbitzuak isolatzea. Hori lortu daiteke aplikazioak makina ezberdinetan banatuz (diru asko, zailak), makina birtualak erabiliz (baliabide asko behar dituztenak, abiarazte luzea) edo edukiontzien bidez. Espero bezala, hirugarren aukera aukeratzen dugu eta Docker edukiontzirako tresna gisa.
Docker
Laburbilduz, Docker-ek edukiontzi isolatuak sortzen ditu, bat aplikazio bakoitzeko. Docker erabiltzeko, Dockerfile bat idatzi behar duzu - aplikazioa eraikitzeko eta exekutatzeko argibideak. Ondoren, irudia eraiki dezakezu, irudien erregistrora igo (Zk. Dockerhub) eta inplementatu zure mikrozerbitzua dokeratutako edozein ingurunetan komando bakarrean.
Dockerfile
Irudi baten ezaugarri garrantzitsuenetako bat bere tamaina da. Irudi trinko batek azkarrago deskargatuko du urruneko biltegi batetik, leku gutxiago hartuko du eta zure zerbitzua azkarrago hasiko da. Edozein irudi oinarrizko irudi batean oinarrituta eraikitzen da, eta aukerarik minimalistena aukeratzea gomendatzen da. Aukera ona da Alpine, pakete gutxien dituen Linux banaketa osoa.
Lehenik eta behin, saia gaitezen Dockerfile bat "buruz" idazten (berehala esango dut hori modu txarra dela, ez egin):
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"]
Hemen Alpine oinarritutako oinarrizko irudi bat erabiltzen ari gara dagoeneko instalatuta dagoen JDK-a gure proiektua eraikitzeko. ADD komandoa erabiliz, uneko src direktorioa gehitzen dugu irudiari, funtzionatzen duen moduan markatzen dugu (WORKDIR) eta eraikitzen hasten dugu. EXPOSE 8080 komandoak docker-ri adierazten dio edukiontziko aplikazioak bere 8080 ataka erabiliko duela (horrela ez da aplikazioa kanpotik eskuragarri egongo, baina aplikazioa atzitzeko aukera emango du, adibidez, docker sare bereko beste edukiontzi batetik ).
Zerbitzuak irudietan paketatzeko, proiektu bakoitzaren errotik komandoak exekutatu behar dituzu:
docker image build . -t msvc-backend:1.0.0
Ondorioz, 456 MB-ko irudia lortzen dugu (horietatik oinarrizko JDK 340 irudiak MB hartzen zuen). Eta dena gure proiektuko klaseak hatz bakarrean zenbatu daitezkeen arren. Gure irudiaren tamaina murrizteko:
Urrats anitzeko muntaia erabiltzen dugu. Lehenengo urratsean proiektua muntatuko dugu, bigarrenean JRE instalatuko dugu, eta hirugarren urratsean hau guztia Alpetar irudi garbi berri batean kopiatuko dugu. Guztira, azken irudiak beharrezko osagaiak baino ez ditu izango.
Erabili dezagun java modularizazioa. Java 9tik hasita, jlink tresna erabil dezakezu JRE bat sortzeko behar dituzun moduluetatik soilik
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"]
Irudia birsortu genuen, eta azkenean 6 aldiz meheagoa bihurtu zen, 77 MB-koa. Ez dago gaizki. Ondoren, amaitutako irudiak irudien erregistrora igo daitezke, zure irudiak Internetetik deskargatzeko eskuragarri egon daitezen.
Docker-en zerbitzuak elkarrekin exekutatu
Hasteko, gure zerbitzuek sare berean egon behar dute. Docker-en hainbat sare mota daude, eta horietako primitiboena erabiltzen dugu: zubia, ostalari berean exekutatzen diren edukiontziak saretzeko aukera ematen duena. Sortu dezagun sare bat komando honekin:
docker network create msvc-network
Ondoren, abiarazi dezagun 'backend' izeneko backend edukiontzi bat microservices-backend:1.0.0 irudiarekin:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0
Azpimarratzekoa da zubi-sareak edukiontziak kutxatik kanpo aurkitzea eskaintzen duela haien izenekin. Hau da, backend zerbitzua Docker sarearen barruan egongo da eskuragarri http://backend:8080.
Komando honetan gure ostalariaren 80 ataka edukiontziaren 8080 atakara birbidaltzen ari garela adierazten dugu. Env aukerak erabiltzen ditugu Spring-ek automatikoki irakurriko diren ingurune-aldagaiak ezartzeko eta application.properties-eko propietateak gainidazteko.
Abiarazi ondoren, deitu http://localhost/ eta ziurtatu dena funtzionatzen duela, aurreko kasuan bezala.
Ondorioa
Ondorioz, bi mikrozerbitzu sinple sortu genituen, docker edukiontzietan paketatu eta elkarrekin abiarazi genituen makina berean. Sortutako sistemak, ordea, desabantaila batzuk ditu:
Akatsen tolerantzia eskasa - dena zerbitzari batean funtzionatzen du
Eskalagarritasun eskasa - karga handitzen den heinean, ona izango litzateke automatikoki zerbitzu-instantzia gehigarriak zabaltzea eta haien arteko karga orekatzea.
Abiarazi konplexutasuna - gutxienez 3 komando sartu behar genituen, parametro jakin batzuekin (hau 2 zerbitzutarako bakarrik da)
Aurreko arazoak konpontzeko, hainbat irtenbide daude, hala nola Docker Swarm, Nomad, Kubernetes edo OpenShift. Sistema osoa Javan idatzita badago, Spring Cloud aldera begiratu dezakezu (artikulu ona).
Π hurrengo zatia Kubernetes konfiguratu eta Google Kubernetes Engine-en proiektua nola zabaldu dudan kontatuko dizut.