
Sveiki, Habr.
Å ajÄ rakstÄ es vÄlos dalÄ«ties ar savu pieredzi mÄcÄ«bu vides izveidÄ eksperimentiem ar mikroservisiem. MÄcoties katru jauno rÄ«ku, man vienmÄr ir bijusi vÄlÄÅ”anÄs to izmÄÄ£inÄt ne tikai vietÄjÄ maŔīnÄ, bet arÄ« reÄlistiskÄkos apstÄkļos. TÄpÄc es nolÄmu izveidot vienkÄrÅ”u mikroservisu lietotni, kuru vÄlÄk varÄÅ”u "apgÄdÄt" ar dažÄdÄm interesantÄm tehnoloÄ£ijÄm. Galvenais projekta prasÄ«ba ir maksimÄla funkcionalitÄte reÄlÄs sistÄmas tuvumÄ.
SÄkotnÄji es sadalÄ«ju projekta radīŔanu vairÄkos posmos:
Izveidot divus servisus ā 'backend' un 'gateway', iepakot tos Docker attÄlos un konfigurÄt to savstarpÄjo darbu.
AtslÄgvÄrdi: Java 11, Spring Boot, Docker, attÄla optimizÄcija
AtslÄgvÄrdi: Kubernetes, GKE, resursu pÄrvaldÄ«ba, automÄtiskÄ mÄrogÄÅ”ana, slepenie dati
Äarta izveide ar Helm 3, lai efektÄ«vÄk pÄrvaldÄ«tu klasteri
AtslÄgvÄrdi: Helm 3, Äarta izvietoÅ”ana
Jenkins konfigurÄcija un pipelines iestatīŔana automÄtiskai koda piegÄdei klasterÄ«
AtslÄgvÄrdi: Jenkins konfigurÄcija, spraudÅi, atseviŔķs konfigurÄciju repozitorijs
Katram solim es plÄnoju veltÄ«t atseviŔķu rakstu.
Å Ä« rakstu cikla mÄrÄ·is nav stÄstÄ«t, kÄ rakstÄ«t mikroservisus, bet gan kÄ likt tiem strÄdÄt vienotÄ sistÄmÄ. Lai gan visi Å”ie aspekti parasti ir Ärpus izstrÄdÄtÄju atbildÄ«bas, es domÄju, ka tomÄr ir noderÄ«gi bÅ«t vismaz 20% ar tiem iepazinuÅ”amies (kas, kÄ zinÄms, sniedz 80% rezultÄta). Dažas bez Å”aubÄm svarÄ«gas tÄmas, piemÄram, droŔības nodroÅ”inÄÅ”ana, Ŕī projekta ietvaros netiks apskatÄ«tas, jo autors tam maz ko saprot; sistÄma tiek izveidota tikai personÄ«gai lietoÅ”anai. Es būŔu pateicÄ«gs par jebkÄdÄm atsauksmÄm un konstruktÄ«vu kritiku.
Mikroservisu izveide
Servisi tika rakstÄ«ti, izmantojot Java 11 ar Spring Boot. Starpservisu mijiedarbÄ«ba tika organizÄta, izmantojot REST. Projekts ietvers minimÄlo testu skaitu (lai vÄlÄk bÅ«tu, ko testÄt Jenkins). TÄ«kla kods ir pieejams GitHub: un .
Lai pÄrbaudÄ«tu katra pakalpojuma stÄvokli, tam tika pievienots Spring Actuator. Tas izveidos endpointu /actuator/health un atgriezÄ«s 200 statusu, ja pakalpojums ir gatavs pieÅemt satiksmi, vai 504 gadÄ«jumÄ, ja ir problÄmas. Å ajÄ gadÄ«jumÄ tas ir diezgan fikcionÄla pÄrbaude, jo pakalpojumi ir ļoti vienkÄrÅ”i, un kÄdÄ ÄrkÄrtas situÄcijÄ tie drÄ«zÄk kļūs pilnÄ«gi nepieejami, nevis saglabÄs daļÄju darbÄ«bu. Bet reÄlÄs sistÄmÄs Actuator var palÄ«dzÄt diagnosticÄt problÄmu pirms tÄs sÄk skart lietotÄjus. PiemÄram, ja rodas problÄmas ar piekļuvi DB, mÄs varÄsim automÄtiski reaÄ£Ät, pÄrtraucot apstrÄdÄt pieprasÄ«jumus ar bojÄtu pakalpojuma eksemplÄru.
Backend pakalpojums
Backend pakalpojums vienkÄrÅ”i saskaitÄ«s un atgriezÄ«s saÅemto pieprasÄ«jumu skaitu.
Kontroliera kods:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}Tests kontrolierim:
@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"));
}
}Gateway pakalpojums
Gateway pÄradresÄs pieprasÄ«jumu uz backend pakalpojumu, papildinot to ar sekojoÅ”o informÄciju:
- gateway id. Tas ir nepiecieÅ”ams, lai varÄtu pÄc servera atbildes atŔķirt vienu gateway eksemplÄru no otra
- KÄds "noslÄpums", kas bÅ«s ļoti svarÄ«ga parole (Å”ifrÄÅ”anas atslÄgas numurs svarÄ«gai sÄ«kdatnei)
KonfigurÄcija failÄ application.properties:
backend.url=http://localhost:8081
instance.id=${random.int}
secret="default-secret"Adapteris saziÅai ar 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 response = restTemplate.getForEntity(
backendUrl + REQUESTS_ENDPOINT, String.class);
return response.getBody();
}
}Kontrolieris:
@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("PieprasÄ«jumu skaits %s (gateway %d, noslÄpums %s)", backendAdapter.getRequests(), instanceId, secret);
}
}PalaiŔana:
UzsÄkam backend:
. /mvnw package -DskipTests
java -Dserver.port=8081 -jar target/microservices-backend-1.0.0.jarUzsÄkam gateway:
. /mvnw package -DskipTests
java -jar target/microservices-gateway-1.0.0.jarPÄrbaudÄm:
$ curl http://localhost:8080/
PieprasÄ«jumu skaits 1 (vÄrteja 38560358, slepenais "default-secret")Viss strÄdÄ. VÄrÄ«gs lasÄ«tÄjs pamanÄ«s, ka nekas netraucÄ tieÅ”i piekļūt aizmugurÄjai daļai, apejot vÄrteju (). Lai to labotu, pakalpojumiem jÄbÅ«t apvienotiem vienÄ tÄ«klÄ, un Ärpuses "jÄspÄ«d" tikai vÄrtejai.
Abiem pakalpojumiem ir arÄ« kopÄ«ga failu sistÄma, kas rada procesus, un vienlaikus tie var traucÄt viens otram. BÅ«tu labi izolÄt mÅ«su mikroservisus. To var panÄkt, izmantojot dažÄdas maŔīnas (daudz naudas, sarežģīti), izmantojot virtuÄlÄs maŔīnas (resursu intensÄ«vi, ilgs palaiÅ”anas laiks) vai, izmantojot konteinerizÄciju. KÄ gaidÄ«ts, izvÄlamies treÅ”o variantu un kÄ konteinerizÄcijas rÄ«ku.
Docker
ÄŖsi sakot, Docker rada izolÄtus konteinerus, pa vienam katrai lietojumprogrammÄm. Lai izmantotu Docker, ir jÄuzraksta Dockerfile ā instrukcija, kÄ izveidot un palaist lietojumprogrammu. PÄc tam varÄsiet izveidot attÄlu, augÅ”upielÄdÄt to attÄlu reÄ£istrÄ (ā ) un vienÄ komandÄ izvÄrst savu mikroservisu jebkurÄ dockerizÄtÄ vidÄ.
Dockerfile
Viena no svarÄ«gÄkajÄm attÄla Ä«paŔībÄm ir tÄ izmÄrs. KompaktÄks attÄls ÄtrÄk tiks palaists no attÄlÄ repozitorija, aizÅems mazÄk vietas, un jÅ«su pakalpojums ÄtrÄk startÄs. Ikviens attÄls tiek veidots, pamatojoties uz bÄzes attÄlu, un ieteicams izvÄlÄties vismazÄkÄs iespÄjamÄs variants. Laba izvÄle ir Alpine ā pilnÄ«gs Linux distribÅ«cija ar minimÄlo pakotÅu skaitu.
SÄksim ar Dockerfile rakstīŔanu "no malas" (uzreiz saku, ka tÄ ir slikta pieeja, nerakstiet tÄ):
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"]Å eit mÄs izmantojam bÄzes attÄlu, kas balstÄ«ts uz Alpine ar jau uzstÄdÄ«tu JDK, lai izveidotu mÅ«su projektu. Ar komandu ADD pievienojam attÄlam paÅ”reizÄjo direktoriju src, nosakÄm to par darba direktoriju (WORKDIR) un uzsÄkam bÅ«vniecÄ«bu. Komanda EXPOSE 8080 informÄ Docker, ka lietojumprogramma konteinerÄ izmantos tÄ 8080 portu (tas nepadarÄ«s lietojumprogrammu pieejamu no Ärpuses, bet ļaus piekļūt lietojumprogrammai, piemÄram, no cita konteineru tajÄ paÅ”Ä Docker tÄ«klÄ).
Lai iepakotu pakalpojumus attÄlos, ir jÄizpilda komandas no katra projekta saknes:
docker image build . -t msvc-backend:1.0.0RezultÄtÄ iegÅ«stam attÄlu, kura izmÄrs ir 456 MB (no tiem pamata attÄls JDK 340 aizÅÄma MB). Un tas viss, neskatoties uz to, ka mÅ«su projektÄ ir skaitÄmi pirkstiÅiem. Lai samazinÄtu mÅ«su attÄla izmÄru:
- Izmantojam daudzsolÄ«gu bÅ«ves metodi. PirmajÄ solÄ« apkoposim projektu, otrajÄ instalÄsim JRE, bet treÅ”ajÄ solÄ« visu to pÄrsÅ«tÄ«sim uz jaunu, tÄ«ru Alpine attÄlu. TÄdÄjÄdi gala attÄlÄ nonÄks tikai nepiecieÅ”amie komponenti.
- Izmantosim Java modularizÄciju. SÄkot ar Java 9, izmantojot rÄ«ku jlink, var izveidot JRE tikai no vajadzÄ«gajiem moduļiem.
ZiÅkÄrÄ«gajiem, Å”eit ir laba raksts par pieejÄm attÄlu izmÄru samazinÄÅ”anai. .
Gala 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"]PÄrveidojam attÄlu, un tas galu galÄ ir samazinÄjies par 6 reizÄm, sasniedzot 77 MB. Labi. PÄc tam gatavos attÄlus var augÅ”upielÄdÄt attÄlu reÄ£istrÄ, lai jÅ«su attÄli bÅ«tu pieejami lejupielÄdei no interneta.
Kopīga pakalpojumu palaiŔana Docker
Vispirms mÅ«su pakalpojumiem jÄatrodas vienÄ tÄ«klÄ. DockerÄ« ir vairÄki tÄ«klu veidi, un mÄs izmantojam vienkÄrÅ”Äko no tiem ā bridge, kas ļauj apvienot tÄ«klÄ konteinerus, kas palaisti uz viena hosta. Izveidosim tÄ«klu ar sekojoÅ”o komandu:
docker network create msvc-networkTÄlÄk palaidÄ«sim backend konteineru ar nosaukumu ābackendā ar attÄlu microservices-backend:1.0.0:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0JÄatzÄ«mÄ, ka bridge-tÄ«kls nodroÅ”ina pakalpojumu atklÄÅ”anu konteineriem pÄc to nosaukumiem. Tas ir, backend pakalpojums bÅ«s pieejams iekÅ”pusÄ Docker tÄ«kla adresÄ .
UzsÄkam gateway:
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Å ajÄ komandÄ mÄs norÄdÄm, ka pÄrsÅ«tÄm mÅ«su hosta 80. portu uz konteineru 8080. portu. Env parametri tiek izmantoti, lai iestatÄ«tu vides mainÄ«gus, kurus automÄtiski nolasÄ«s Spring un pÄrrakstÄ«s Ä«paŔības no application.properties.
PÄc palaiÅ”anas izsaucam un pÄrliecinÄmies, ka viss darbojas kÄ iepriekÅ”ÄjÄ reizÄ.
SecinÄjums
Galu galÄ mÄs izveidojÄm divus vienkÄrÅ”us mikroservisus, iesaiÅojÄm tos Docker konteineros un palaidÄm kopÄ vienÄ maŔīnÄ. TomÄr iegÅ«tajai sistÄmai ir vairÄki trÅ«kumi:
- Slikta pieejamÄ«ba ā viss darbojas uz viena servera
- Slikta mÄrogojamÄ«ba ā palielinoties slodzei, bÅ«tu labi automÄtiski izvietot papildu pakalpojumu instancÄm un lÄ«dzsvarot slodzi starp tÄm
- Sarežģīts palaiÅ”ana ā mums bija jÄievada vismaz 3 komandas, turklÄt ar noteiktiem parametriem (tas tikai 2 pakalpojumiem)
Lai novÄrstu iepriekÅ” minÄtÄs problÄmas, ir vairÄkas risinÄjumu iespÄjas, piemÄram, Docker Swarm, Nomad, Kubernetes vai OpenShift. Ja visa sistÄma tiks rakstÄ«ta Java, var skatÄ«ties uz Spring Cloud ().
No es pastÄstīŔu par to, kÄ es konfigurÄju Kubernetes un izvietoju projektu Google Kubernetes Engine.
Avots: habr.com
