
Tere, Habr.
Selles artiklis tahan rÀÀkida oma kogemusest Ă”ppekeskkonna loomisel mikroteenustega eksperimenteerimiseks. Iga uue tööriista Ă”ppimisel on mul alati olnud soov proovida seda mitte ainult kohaliku arvuti peal, vaid ka realistlikumates tingimustes. SeetĂ”ttu otsustasin luua lihtsustatud mikroteenuste rakenduse, mida saab hiljem "kaunistada" erinevate huvitavate tehnoloogiatega. Projekti peamine nĂ”ue on selle maksimaalne funktsionaalne lĂ€hedus reaalsele sĂŒsteemile.
Algselt jagasin projekti loomise mitmeks sammuks:
Luua kaks teenust - 'tagapÔhi' (backend) ja 'vÀrav' (gateway), pakkida need Docker piltidesse ja seadistada nende koostöö.
MÀrksÔnad: Java 11, Spring Boot, Docker, pildi optimeerimine.
MÀrksÔnad: Kubernetes, GKE, ressursside haldamine, automaatne skaleerimine, saladused.
Charti loomine Helm 3 abil, et klastrit tÔhusamalt hallata.
MÀrksÔnad: Helm 3, chart'i juurutamine.
Jenkins'i ja toru seadistamine koodi automaatseks edastamiseks klastri.
MÀrksÔnad: Jenkins'i konfiguratsioon, pistikprogrammid, eraldi konfiguratsioonide hoidla.
Igaastmele plaanin pĂŒhendada eraldi artikli.
Selle artikli tsĂŒkli suund on pigem selles, kuidas saada mikroteenused töötama ĂŒhtses sĂŒsteemis, mitte kuidas neid kirjutada. Kuigi kĂ”ik need asjad jÀÀvad tavaliselt arendaja vastutusalast vĂ€lja, arvan, et on ikkagi kasulik olla nendega tuttav vĂ€hemalt 20% ulatuses (mis, nagu teada, annab 80% tulemusest). MĂ”ned tingimata olulised teemad, nagu julgeoleku tagamine, jÀÀvad selle projekti kontekstist vĂ€lja, kuna autor ei tea selles valdkonnas palju, sĂŒsteem luuakse ainult isiklikuks kasutamiseks. Ootan hea meelega igasuguseid arvamusi ja konstruktiivset kriitikat.
Mikroteenuste loomine
Teenused on kirjutatud Java 11 abil kasutades Spring Boot'i. Teenuste vaheline suhtlemine on korraldatud REST'i abil. Projekt sisaldab minimaalset arvu teste (et hiljem oleks, mida Jenkins'is testida). Teenuste lÀhtekood on saadaval GitHub'is: ja .
Kasutades Spring Actuator'i, saame jĂ€lgida iga teenuse seisundit. See loob lĂ”pp-punkti /actuator/health ja tagastab 200 staatuse, kui teenus on valmis liiklust vastu vĂ”tma, vĂ”i 504, kui esinevad probleemid. Antud juhul on see pigem vale kontroll, kuna teenused on vĂ€ga lihtsad ja erakorraliste olukordade korral vĂ”ivad nad olla tĂ€ielikult kĂ€ttesaamatud, mitte osaliselt funktsioneerivad. Kuid reaalsetes sĂŒsteemides aitab Actuator tuvastada probleeme enne, kui kasutajad nendega kokku puutuvad. NĂ€iteks, kui tekivad andmebaasi juurdepÀÀsuga seotud probleemid, saame automaatselt reageerida, lĂ”petades vigase teenuse töötamise pĂ€ringute töötlemiseks.
Tagasiside teenus
Tagasiside teenus loendab ja edastab vastuvÔetud pÀringute arvu.
Kontrolleri kood:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}Kontrolleri 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"));
}
}LÀbipÀÀsupunkti teenus
LÀbipÀÀsupunkt suunab pÀringu tagasiside teenusele, lisades sellele jÀrgmise teabe:
- lĂ€bipÀÀsupunkti ID. See on vajalik, et serveri vastusest eristada ĂŒhte lĂ€bipÀÀsupunkti teisest
- Mingi "salajane" vÀÀrtus, mis toimib vĂ€ga olulise paroolina (salajase kĂŒpsise krĂŒptimisvĂ”tme numbrina)
Seadistus failis application.properties:
backend.url=http://localhost:8081
instance.id=${random.int}
secret="default-secret"Adapter tagasiside teenusega ĂŒhendamiseks:
@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 response = restTemplate.getForEntity(
backendUrl + REQUESTS_ENDPOINT, String.class);
return response.getBody();
}
}Kontroller:
@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("PÀringute arv %s (lÀbipÀÀsupunkt %d, salajane %s)", backendAdapter.getRequests(), instanceId, secret);
}
}PĂ€rast seda
KĂ€ivitame tagasiside teenuse:
./mvnw package -DskipTests
java -Dserver.port=8081 -jar target/microservices-backend-1.0.0.jarKÀivitame lÀbipÀÀsupunkt:
./mvnw package -DskipTests
java -jar target/microservices-gateway-1.0.0.jarKontrollime:
$ curl http://localhost:8080/
Number of requests 1 (gateway 38560358, secret "default-secret")KĂ”ik töötab. TĂ€helepanelik lugeja mĂ€rkab, et meil ei ole midagi, mis takistaks pöörduda tagalihtrisse otse, mööda vĂ€ravat (). Selle parandamiseks peavad teenused olema ĂŒhendatud ĂŒhte vĂ”rku ning vĂ€lja peab âpaistmaâ vaid vĂ€rav.
Samuti jagavad mĂ”lemad teenused ĂŒhte failisĂŒsteemi, genereerivad piisavalt koormust ning vĂ”ivad ĂŒhel hetkel ĂŒksteist segama hakata. Oleks hea isoleerida meie mikroteenus. Seda saab saavutada rakenduste paigutamisega erinevatele masinatele (palju raha, keeruline), virtuaalmasinate kasutamisega (ressursimahukas, aeglane kĂ€ivitamine) vĂ”i konteineriseerimise abil. OotuspĂ€raselt valime kolmanda variandi ja konteineriseerimise tööriistana.
Docker
LĂŒhidalt öeldes loob Docker isoleeritud konteinerid, igaĂŒhe rakenduse jaoks eraldi. Dockerit kasutamiseks tuleb kirjutada Dockerfile â juhend rakenduse seadistamiseks ja kĂ€ivitamiseks. SeejĂ€rel saab luua pildi, laadida selle pildiregistrisse ( ) ja ĂŒhe kĂ€suga kĂ€ivitada oma mikroteenuse igas Dockerisse pandud keskkonnas.
Dockerfile
Ăks olulisemaid pildi omadusi on selle suurus. Kompaktne pilt laaditakse kaugelt hoidlast kiiremini alla, vĂ”tab vĂ€hem ruumi ja teie teenus kĂ€ivitub kiiremini. Iga pilt ehitatakse pĂ”hifoto alusel ja soovitatakse valida vĂ”imalikult minimalistlik variant. Hea valik on Alpine â tĂ€ielik Linuxi distributsioon minimaalsete pakettidega.
Alustuseks proovime kirjutada Dockerfile âotseseltâ (ĂŒtlen kohe, et see on halb viisipidamine, Ă€rge tehke niimoodi):
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"]Siin kasutame Alpine pÔhjal pÔhinevat pÔhifotot, millele on juba installitud JDK, et ehitada meie projekti. KÀsuga ADD lisame pildi praeguse katalooge src, mÀrkime selle töökohana (WORKDIR) ja kÀivitame ehituse. KÀsk EXPOSE 8080 annab Dockerile teada, et rakendus konteineris kasutab oma porti 8080 (see ei tee rakendust vÀljastpoolt kÀttesaadavaks, kuid vÔimaldab rakendusele pöörduda, nÀiteks teisest konteinerist samas Docker-vÔrgus).
Teenuste pildiks pakkimiseks peate kÀivitama kÀsud igas projekti juures:
docker image build . -t msvc-backend:1.0.0Tulemusena saame 456 MB suuruse pildi (millest baas JDK 340 hÔivas MB). Ja arvestades, et meie projektis on klasse hÔlpsasti loetavad. Et vÀhendada meie pildi suurust:
- Kasutame mitmeastmelist ehitust. Esimeses etapis kogume projekti, teises paigaldame JRE ja kolmandas kopeerime kÔik uude puhtasse Alpine pildi. LÔpptulemusena jÀÀvad finiƥipildis alles vaid vajalikud komponendid.
- Kasutame java modulariseerimist. Alates Java 9-st saab tööriista jlink abil luua JRE ainult vajalikest moodulitest.
Uurimiseks, siin on hea artikkel pildi suuruse vÀhendamise lÀhenemistest. .
LÔplik 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 anshelen@yandex.ru"
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"]Loodame pildi uuesti ja selle suurus on kokku 6 korda vĂ€iksem, ulatudes vaid 77 MB. Mitte paha. PĂ€rast valmis pilte saab registrisse ĂŒles laadida, et teie pildid oleksid internetist allalaadimiseks saadaval.
Teenuste koos kÀitamine Dockeris
Alustamiseks peavad meie teenused olema samas vĂ”rgus. Dockeris on saadaval mitu tĂŒĂŒpi vĂ”rgud ja meie kasutame kĂ”ige lihtsamat neist â bridge, mis vĂ”imaldab ĂŒhendada vĂ”rgusse samal hostil kĂ€ivitatud konteinerid. Loome vĂ”rgus jĂ€rgmise kĂ€suga:
docker network create msvc-networkSeejÀrel kÀivitame tagapoolt konteineri nimega 'backend' pildiga microservices-backend:1.0.0:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0Tuleb mÀrkida, et bridge-vÔrk pakub vÀlja teenuste avastamist konteinerite nimede jÀrgi. See tÀhendab, et tagapoolt teenus on Dockeri vÔrgus aadressil .
KÀivitame lÀbipÀÀsupunkt:
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.0Selles komandis mÀÀrame, et suuname hosti pordi 80 konteineri pordile 8080. Keskkonna valikutele kasutame, et seadistada keskkonnamuutujad, mis Springi poolt automaatselt vĂ€lja loetakse ning ĂŒletavad application.properties omadused.
PÀrast kÀivitamist kutsume esile ja veendume, et kÔik töötab nagu eelmisel korral.
KokkuvÔte
LĂ”puks oleme loonud kaks lihtsat mikroteenust, pakkides need Docker konteineritesse ja kĂ€ivitades need koos ĂŒhel masinal. Siiski on sellel sĂŒsteemil mitmeid puudusi:
- Halb talitlushĂ€irete taluvus â meil on kĂ”ik ĂŒhel serveril
- Halb skaleeritavus â kui koormus suureneb, oleks hea automaatselt kĂ€ivitada tĂ€iendavaid teenuse eksemplare ja jaotada koormust nende vahel
- KĂ€ivitamise keerulisus â vajame vĂ€hemalt 3 kĂ€sku, lisaks kindlate parameetritega (see ainult 2 teenuse jaoks)
Nende probleemide lahendamiseks on mitmeid lahendusi, nagu Docker Swarm, Nomad, Kubernetes vĂ”i OpenShift. Kui kogu sĂŒsteem on kirjutatud Java'sse, vĂ”iks vaadata Spring Cloudi poole ().
Uues ma rÀÀgin, kuidas ma seadistasin Kubernetes ja tÔin projekti Google Kubernetes Engine'i.
Allikas: habr.com
