Jifunze jinsi ya kupeleka huduma ndogo. Sehemu ya 1. Spring Boot na Docker

Jifunze jinsi ya kupeleka huduma ndogo. Sehemu ya 1. Spring Boot na Docker

Habari Habr.

Katika makala hii, nataka kuzungumza juu ya uzoefu wangu katika kuunda mazingira ya kujifunza kwa majaribio na huduma ndogo. Nilipojifunza kila chombo kipya, siku zote nilitaka kujaribu sio tu kwenye mashine ya ndani, lakini pia katika hali halisi zaidi. Kwa hiyo, niliamua kuunda programu rahisi ya microservice, ambayo inaweza baadaye "kufunikwa" na kila aina ya teknolojia za kuvutia. Mahitaji makuu ya mradi ni ukaribu wake wa juu wa kazi kwa mfumo halisi.

Hapo awali, nilivunja uundaji wa mradi katika hatua kadhaa:

  1. Unda huduma mbili - 'backend' (backend) na 'lango' (lango), zipakie kwenye picha za kizimbani na uziweke ili kufanya kazi pamoja.

    Maneno muhimu: Java 11, Spring Boot, Docker, uboreshaji wa picha

  2. Ukuzaji wa usanidi wa Kubernetes na uwekaji wa mfumo katika Injini ya Google Kubernetes

    Maneno muhimu: Kubernetes, GKE, usimamizi wa rasilimali, kuongeza otomatiki, siri

  3. Kuunda chati kwa kutumia Helm 3 kwa usimamizi bora wa nguzo

    Lebo: Helm 3, uwekaji chati

  4. Kuweka Jenkins na bomba kwa uwasilishaji wa kiotomatiki wa nambari kwenye nguzo

    Maneno muhimu: usanidi wa Jenkins, programu-jalizi, hazina tofauti ya usanidi

Ninapanga kutoa nakala tofauti kwa kila hatua.

Mtazamo wa mfululizo huu wa makala sio jinsi ya kuandika microservices, lakini jinsi ya kuwafanya kufanya kazi katika mfumo mmoja. Ingawa mambo haya yote kwa kawaida huwa nje ya jukumu la msanidi programu, nadhani bado ni muhimu kuyafahamu angalau 20% (ambayo, kama unavyojua, hutoa 80% ya matokeo). Baadhi ya mada muhimu bila masharti, kama vile usalama, zitaachwa nje ya mradi huu, kwa kuwa mwandishi anaelewa kidogo kuhusu mfumo huu umeundwa kwa matumizi ya kibinafsi tu. Ninakaribisha maoni yoyote na ukosoaji wa kujenga.

Kujenga microservices

Huduma ziliandikwa katika Java 11 kwa kutumia Spring Boot. Mwingiliano wa huduma hupangwa kwa kutumia REST. Mradi huo utajumuisha idadi ya chini ya vipimo (ili baadaye kuna kitu cha kujaribu huko Jenkins). Nambari ya chanzo cha huduma inapatikana kwenye GitHub: nyuma ΠΈ Lango.

Ili kuweza kuangalia hali ya kila moja ya huduma, Kitendaji cha Spring kiliongezwa kwenye utegemezi wao. Itaunda sehemu ya mwisho / actuator/health na itarejesha hali ya 200 ikiwa huduma iko tayari kukubali trafiki, au 504 iwapo kutatokea matatizo. Katika kesi hii, hii ni ukaguzi wa uwongo, kwani huduma ni rahisi sana, na ikiwa kuna nguvu kubwa, kuna uwezekano mkubwa wa kutopatikana kabisa kuliko kubaki kufanya kazi kwa sehemu. Lakini katika mifumo halisi, Actuator inaweza kusaidia kutambua tatizo kabla ya watumiaji kuanza kuligonga. Kwa mfano, ikiwa kuna matatizo ya kufikia hifadhidata, tunaweza kujibu hili kiotomatiki kwa kusimamisha maombi ya kuchakata kwa mfano wa huduma iliyovunjika.

Huduma ya nyuma ya mwisho

Huduma ya nyuma itahesabu tu na kurudisha idadi ya maombi yaliyokubaliwa.

Msimbo wa kidhibiti:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Mtihani wa kidhibiti:

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

Lango la Huduma

Lango litatuma ombi kwa huduma ya nyuma, likiongezea na habari ifuatayo:

  • kitambulisho cha lango. Inahitajika ili iwezekanavyo kutofautisha mfano mmoja wa lango kutoka kwa mwingine kwa majibu ya seva
  • Baadhi ya "siri" ambayo itachukua jukumu la nenosiri muhimu sana (idadi ya ufunguo wa usimbuaji wa kidakuzi muhimu)

Usanidi katika maombi.sifa:

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

Adapta ya nyuma:

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

Kidhibiti:

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

Uzinduzi:

Tunaanza backend:

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

Kuanzisha lango:

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

Tunaangalia:

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

Kila kitu kinafanya kazi. Msomaji makini atagundua kuwa hakuna kitu kinachotuzuia kupata njia ya nyuma moja kwa moja, kupita lango (http://localhost:8081/requests) Ili kurekebisha hili, huduma lazima ziunganishwe kwenye mtandao mmoja, na lango pekee linapaswa "kutoka nje" nje.
Pia, huduma zote mbili zinashiriki mfumo mmoja wa faili, hutoa mito na kwa wakati mmoja inaweza kuanza kuingilia kati. Itakuwa nzuri kutenga huduma zetu ndogo. Hili linaweza kupatikana kwa kusambaza programu kwenye mashine tofauti (fedha nyingi, ngumu), kwa kutumia mashine pepe (zinazotumia rasilimali nyingi, kuanza kwa muda mrefu), au kutumia uwekaji vyombo. Kama inavyotarajiwa, tunachagua chaguo la tatu na Docker kama chombo cha kuweka vyombo.

Docker

Kwa kifupi, docker huunda vyombo vilivyotengwa, moja kwa kila programu. Ili kutumia docker, unahitaji kuandika Dockerfile - maagizo ya kujenga na kuendesha programu. Ifuatayo, unaweza kuunda picha, kuipakia kwenye sajili ya picha (Na. Dockerhub) na upeleke huduma ndogo yako katika mazingira yoyote ya kizimbani kwa amri moja.

Dockerfile

Moja ya sifa muhimu zaidi za picha ni ukubwa wake. Picha ndogo itapakuliwa haraka kutoka kwa hifadhi ya mbali, kuchukua nafasi kidogo, na huduma yako itaanza haraka. Picha yoyote imejengwa kwa misingi ya picha ya msingi, na inashauriwa kuchagua chaguo la minimalistic zaidi. Chaguo nzuri ni Alpine, usambazaji kamili wa Linux na vifurushi vidogo.

Kwanza, hebu tujaribu kuandika Dockerfile "kwenye paji la uso" (Nitasema mara moja kuwa hii ni njia mbaya, usiifanye):

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

Hapa tunatumia picha ya msingi ya Alpine na JDK tayari imesakinishwa ili kujenga mradi wetu. Kwa amri ya ADD, tunaongeza saraka ya sasa ya src kwenye picha, alama kuwa inafanya kazi (WORKDIR) na uanze kujenga. Amri ya EXPOSE 8080 inaashiria kwa docker kwamba programu kwenye kontena itatumia bandari yake 8080 (hii haitafanya programu kufikiwa kutoka nje, lakini itaruhusu programu kufikiwa, kwa mfano, kutoka kwa chombo kingine kwenye mtandao huo wa kizimbani. )

Kufunga huduma katika picha, unahitaji kuendesha amri kutoka kwa mzizi wa kila mradi:

docker image build . -t msvc-backend:1.0.0

Matokeo yake ni picha ya 456 MB (ambayo picha ya msingi ya JDK ilichukua 340 MB). Na wote licha ya ukweli kwamba madarasa katika mradi wetu yanaweza kuhesabiwa kwa kidole. Ili kupunguza saizi ya picha yetu:

  • Tunatumia mkusanyiko wa hatua nyingi. Katika hatua ya kwanza tutajenga mradi, katika hatua ya pili tutaweka JRE, na katika hatua ya tatu tutaiga yote kwenye picha mpya ya Alpine safi. Kwa jumla, vipengele muhimu tu vitakuwa kwenye picha ya mwisho.
  • Wacha tutumie urekebishaji wa java. Kuanzia na Java 9, unaweza kutumia zana ya jlink kuunda JRE kutoka kwa moduli unazohitaji

Kwa wadadisi, hapa kuna nakala nzuri juu ya mbinu za kupunguza picha. https://habr.com/ru/company/ruvds/blog/485650/.

Faili ya Mwisho ya Docker:

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

Tuliunda upya picha, na hatimaye ikawa nyembamba mara 6, kiasi cha 77 MB. Sio mbaya. Baada ya hayo, picha zilizopangwa tayari zinaweza kupakiwa kwenye Usajili wa picha ili picha zako zipatikane kwa kupakuliwa kutoka kwenye mtandao.

Huduma za kushirikiana katika Docker

Kuanza, huduma zetu lazima ziwe kwenye mtandao sawa. Kuna aina kadhaa za mitandao kwenye Docker, na tunatumia ya zamani zaidi - daraja, ambayo hukuruhusu kuweka vyombo vya mtandao vinavyoendesha kwenye mwenyeji mmoja. Wacha tuunde mtandao na amri ifuatayo:

docker network create msvc-network

Ifuatayo, anza kontena la nyuma linaloitwa 'backend' na huduma ndogo-nyuma: picha ya 1.0.0:

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

Ni vyema kutambua kwamba mtandao wa daraja hutoa ugunduzi wa huduma nje ya boksi kwa vyombo kwa majina yao. Hiyo ni, huduma ya nyuma itapatikana ndani ya mtandao wa Docker kwa http://backend:8080.

Kuanzisha lango:

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

Katika amri hii tunaonyesha kwamba tunasambaza bandari 80 ya mwenyeji wetu kwenye bandari 8080 ya kontena. Tunatumia chaguo za env kuweka vigeu vya mazingira ambavyo vitasomwa kiotomatiki kufikia masika na kubatilisha sifa kutoka kwa application.properties.

Baada ya kuanza, tunaita http://localhost/ na hakikisha kuwa kila kitu kinafanya kazi, kama katika kesi iliyopita.

Hitimisho

Matokeo yake, tuliunda microservices mbili rahisi, tukazifunga kwenye vyombo vya docker na kuzizindua pamoja kwenye mashine moja. Mfumo unaosababishwa, hata hivyo, una idadi ya hasara:

  • Uvumilivu mbaya wa makosa - kila kitu kinatufanyia kazi kwenye seva moja
  • Ubora mbaya - mzigo unapoongezeka, itakuwa nzuri kupeleka kiotomati huduma za ziada na kusawazisha mzigo kati yao.
  • Ugumu wa uzinduzi - tulihitaji kuingiza angalau amri 3, na kwa vigezo fulani (hii ni kwa huduma 2 tu)

Ili kurekebisha shida zilizo hapo juu, kuna suluhisho kadhaa kama vile Docker Swarm, Nomad, Kubernetes au OpenShift. Ikiwa mfumo mzima umeandikwa katika Java, unaweza kuangalia kuelekea Spring Cloud (makala nzuri).

Π’ sehemu inayofuata Nitazungumza kuhusu jinsi nilivyoanzisha Kubernetes na kusambaza mradi kwa Google Kubernetes Engine.

Chanzo: mapenzi.com

Kuongeza maoni