Lærðu hvernig á að dreifa örþjónustum. Part 1. Spring Boot og Docker

Lærðu hvernig á að dreifa örþjónustum. Part 1. Spring Boot og Docker

Hæ Habr.

Í þessari grein vil ég segja frá reynslu minni af því að búa til námsumhverfi til að gera tilraunir með örþjónustur. Þegar ég lærði hvert nýtt verkfæri, langaði mig alltaf að prófa það, ekki aðeins á staðbundinni vél, heldur einnig við raunhæfari aðstæður. Þess vegna ákvað ég að búa til einfaldað smáþjónustuforrit, sem síðar er hægt að "hylja" með alls kyns áhugaverðri tækni. Helsta krafan fyrir verkefnið er hámarks virkni nálægð þess við raunverulegt kerfi.

Upphaflega skipti ég gerð verkefnisins í nokkur skref:

  1. Búðu til tvær þjónustur - 'backend' (backend) og 'gateway' (gátt), pakkaðu þeim inn í docker myndir og settu þær upp til að vinna saman

    Lykilorð: Java 11, Spring Boot, Docker, myndhagræðing

  2. Þróun á Kubernetes stillingum og uppsetningu kerfis í Google Kubernetes Engine

    Lykilorð: Kubernetes, GKE, auðlindastjórnun, sjálfvirk stærð, leyndarmál

  3. Að búa til töflu með Helm 3 fyrir betri klasastjórnun

    Merki: Helm 3, kortauppsetning

  4. Uppsetning Jenkins og leiðslu fyrir sjálfvirka afhendingu kóða til þyrpingarinnar

    Lykilorð: Jenkins stillingar, viðbætur, aðskilin stillingargeymsla

Ég ætla að verja sérstakri grein fyrir hvert skref.

Áherslan í þessari greinaröð er ekki hvernig á að skrifa örþjónustur, heldur hvernig á að láta þær virka í einu kerfi. Þrátt fyrir að allir þessir hlutir séu venjulega utan ábyrgðar framkvæmdaraðila, held ég að það sé samt gagnlegt að þekkja þá að minnsta kosti 20% (sem, eins og þú veist, gefur 80% af niðurstöðunni). Sum skilyrðislaust mikilvæg efni, eins og öryggi, verða sleppt í þessu verkefni, þar sem höfundur skilur lítið um þetta kerfi er eingöngu búið til til persónulegra nota. Ég fagna öllum skoðunum og uppbyggilegri gagnrýni.

Að búa til örþjónustur

Þjónustan var skrifuð í Java 11 með Spring Boot. Samskipti milli þjónustu eru skipulögð með því að nota REST. Verkefnið mun innihalda lágmarksfjölda prófa (svo að seinna sé eitthvað að prófa í Jenkins). Kóðinn fyrir þjónustuna er fáanlegur á GitHub: bakenda и Gátt.

Til að geta athugað stöðu hverrar þjónustu, hefur Spring Actuator verið bætt við ósjálfstæði þeirra. Það mun búa til /actuator/heilsuendapunkt og mun skila 200 stöðu ef þjónustan er tilbúin til að taka við umferð, eða 504 ef það er vandamál. Í þessu tilviki er þetta frekar uppdiktuð ávísun, þar sem þjónustan er mjög einföld og ef um óviðráðanlegar aðstæður er að ræða er líklegra að hún verði algjörlega ófáanleg en að hún haldist að hluta til. En í raunverulegum kerfum getur Actuator hjálpað til við að greina vandamál áður en notendur byrja að berjast um það. Til dæmis, ef það eru vandamál með aðgang að gagnagrunninum, getum við sjálfkrafa brugðist við því með því að stöðva vinnslu beiðna með biluðu þjónustutilviki.

Bakendaþjónusta

Bakendaþjónustan mun einfaldlega telja og skila fjölda samþykktra beiðna.

Stjórnarkóði:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Stjórnandi próf:

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

Þjónustugátt

Gáttin mun framsenda beiðnina til bakendaþjónustunnar og bæta henni við eftirfarandi upplýsingar:

  • auðkenni gáttar. Það er nauðsynlegt svo hægt sé að greina eitt tilvik af gáttinni frá öðru með svari netþjónsins
  • Einhver "leyndarmál" sem mun gegna hlutverki mjög mikilvægt lykilorð (númer dulkóðunarlykils mikilvægrar vafraköku)

Stillingar í application.properties:

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

Bakenda millistykki:

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

Stjórnandi:

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

Ræsa:

Við byrjum bakendann:

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

Að ræsa gáttina:

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

Við athugum:

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

Allt er að virka. Athugull lesandi mun taka eftir því að ekkert kemur í veg fyrir að við fáum aðgang að bakendanum beint og framhjá gáttinni (http://localhost:8081/requests). Til að laga þetta þarf að sameina þjónusturnar í eitt net og aðeins gáttin á að „staka út“ fyrir utan.
Einnig deila báðar þjónusturnar einu skráarkerfi, framleiða strauma og geta á einu augnabliki byrjað að trufla hvor aðra. Það væri gaman að einangra örþjónustur okkar. Þetta er hægt að ná með því að dreifa forritum á mismunandi vélar (mikið fé, erfitt), nota sýndarvélar (auðlindafrekar, langvarandi gangsetning) eða nota gámavæðingu. Eins og við var að búast veljum við þriðja valkostinn og Docker sem tæki til gámavæðingar.

Docker

Í stuttu máli, docker býr til einangruð ílát, einn í hverju forriti. Til að nota docker þarftu að skrifa Dockerfile - leiðbeiningar um að byggja og keyra forritið. Næst geturðu smíðað myndina, hlaðið henni upp í myndaskrána (nr. Dockerhub) og settu örþjónustuna þína í hvaða bryggjuumhverfi sem er í einni skipun.

Dockerfil

Eitt mikilvægasta einkenni myndar er stærð hennar. Samþjöppuð mynd mun hlaða niður hraðar frá fjarlægri geymslu, taka minna pláss og þjónustan þín byrjar hraðar. Hvaða mynd sem er er byggð á grunnmyndinni og mælt er með því að velja lægsta kostinn. Góður kostur er Alpine, heill Linux dreifing með lágmarkspökkum.

Til að byrja með skulum við reyna að skrifa Dockerfile "á ennið" (ég segi strax að þetta er slæm leið, ekki gera það):

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

Hér erum við að nota Alpine byggða grunnmynd með JDK þegar uppsett til að byggja verkefnið okkar. Með ADD skipuninni bætum við núverandi src möppu við myndina, merkjum hana sem vinnandi (WORKDIR) og byrjum bygginguna. EXPOSE 8080 skipunin gefur merki til hafnarstjóra að forritið í gámnum muni nota gátt sína 8080 (þetta mun ekki gera forritið aðgengilegt utan frá, heldur gerir það kleift að opna forritið, til dæmis frá öðrum gámi á sama tengikví. ).

Til að pakka þjónustu í myndir þarftu að keyra skipanir frá rót hvers verkefnis:

docker image build . -t msvc-backend:1.0.0

Niðurstaðan er 456 MB mynd (þar af tók grunn JDK myndin 340 MB). Og allt þrátt fyrir að hægt sé að telja bekkina í verkefninu okkar á fingri. Til að minnka stærð myndarinnar okkar:

  • Við notum fjölþrepa samsetningu. Í fyrsta skrefi munum við byggja verkefnið, í öðru skrefi munum við setja upp JRE og í þriðja skrefi munum við afrita það allt í nýja hreina Alpamynd. Alls verða aðeins nauðsynlegir hlutir á lokamyndinni.
  • Við skulum nota modularization Java. Frá og með Java 9 geturðu notað jlink tólið til að búa til JRE úr þeim einingum sem þú þarft

Fyrir fróðleiksfúsa er hér góð grein um aðferðir til að draga úr myndum. https://habr.com/ru/company/ruvds/blog/485650/.

Lokaskjal:

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

Við endurgerðum myndina og þar af leiðandi missti hún 6 sinnum þyngd sína, upp á 77 MB. Ekki slæmt. Eftir það er hægt að hlaða tilbúnum myndum inn í myndaskrána þannig að myndirnar þínar séu tiltækar til niðurhals af netinu.

Samstarfsþjónusta í Docker

Til að byrja með verður þjónusta okkar að vera á sama neti. Það eru til nokkrar gerðir netkerfa í docker og við notum frumstæðasta þeirra - bridge, sem gerir þér kleift að netkerfisgáma sem keyra á sama hýsil. Búðu til netkerfi með eftirfarandi skipun:

docker network create msvc-network

Næst skaltu byrja bakendagáminn sem heitir 'backend' með microservices-backend:1.0.0 myndinni:

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

Það er athyglisvert að brúarnetið veitir uppgötvun gáma úr kassanum með nöfnum þeirra. Það er, bakendaþjónustan verður fáanleg innan hafnarkerfisins kl http://backend:8080.

Að ræsa gáttina:

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

Í þessari skipun gefum við til kynna að við séum að senda höfn 80 á hýsil okkar til höfn 8080 í gámnum. Við notum env valkostina til að stilla umhverfisbreytur sem verða sjálfkrafa lesnar með vori og hnekkja eiginleikum frá application.properties.

Eftir að hafa byrjað hringjum við http://localhost/ og vertu viss um að allt virki eins og í fyrra tilvikinu.

Ályktun

Fyrir vikið bjuggum við til tvær einfaldar örþjónustur, pökkuðum þeim í hafnargáma og ræstum þær saman á sömu vélinni. Kerfið sem myndast hefur hins vegar ýmsa ókosti:

  • Lélegt bilanaþol - allt virkar fyrir okkur á einum netþjóni
  • Lélegur sveigjanleiki - þegar álagið eykst væri gaman að dreifa viðbótarþjónustutilvikum sjálfkrafa og jafna álagið á milli þeirra
  • Flækjustig ræsingarinnar - við þurftum að slá inn að minnsta kosti 3 skipanir og með ákveðnum breytum (þetta er aðeins fyrir 2 þjónustur)

Til að laga ofangreind vandamál eru til nokkrar lausnir eins og Docker Swarm, Nomad, Kubernetes eða OpenShift. Ef allt kerfið er skrifað í Java geturðu horft í átt að Spring Cloud (góð grein).

В næsta hluta Ég mun tala um hvernig ég setti upp Kubernetes og setti verkefnið á Google Kubernetes Engine.

Heimild: www.habr.com

Bæta við athugasemd