Իմացեք, թե ինչպես տեղադրել միկրոծառայություններ: Մաս 1. Spring Boot and Docker

Իմացեք, թե ինչպես տեղադրել միկրոծառայություններ: Մաս 1. Spring Boot and Docker

Հե՜յ Հաբր։

Այս հոդվածում ես ուզում եմ խոսել միկրոսերվիսների հետ փորձարկումների համար ուսումնական միջավայր ստեղծելու իմ փորձի մասին: Երբ ես սովորում էի յուրաքանչյուր նոր գործիք, ես միշտ ցանկանում էի փորձել այն ոչ միայն տեղական մեքենայի վրա, այլև ավելի իրատեսական պայմաններում: Ուստի որոշեցի ստեղծել պարզեցված միկրոսերվիս հավելված, որը հետագայում կարող է «ծածկվել» ամենատարբեր հետաքրքիր տեխնոլոգիաներով։ Ծրագրի հիմնական պահանջը դրա առավելագույն ֆունկցիոնալ հարևանությունն է իրական համակարգին:

Սկզբում ես բաժանեցի նախագծի ստեղծումը մի քանի փուլերի.

  1. Ստեղծեք երկու ծառայություն՝ «backend» (backend) և «gateway» (gateway), փաթեթավորեք դրանք դոկերի պատկերների մեջ և կարգավորեք դրանք միասին աշխատելու համար։

    Բանալի բառեր՝ Java 11, Spring Boot, Docker, պատկերի օպտիմալացում

  2. Kubernetes-ի կոնֆիգուրացիայի և համակարգի տեղակայման մշակում Google Kubernetes Engine-ում

    Բանալի բառեր՝ Kubernetes, GKE, ռեսուրսների կառավարում, ավտոմատ մասշտաբավորում, գաղտնիքներ

  3. Հելմ 3-ի հետ աղյուսակի ստեղծում՝ կլաստերի ավելի լավ կառավարման համար

    Պիտակներ՝ Ղեկավար 3, գծապատկերի տեղակայում

  4. Jenkins-ի և խողովակաշարի կարգավորում՝ կլաստերին կոդի ավտոմատ առաքման համար

    Հիմնաբառեր՝ Jenkins կոնֆիգուրացիա, պլագիններ, առանձին կազմաձևերի պահեստ

Ես նախատեսում եմ յուրաքանչյուր քայլին առանձին հոդված նվիրել։

Այս հոդվածաշարի կիզակետը ոչ թե այն է, թե ինչպես գրել միկրոծառայություններ, այլ ինչպես ստիպել դրանք աշխատել մեկ համակարգում: Չնայած այս բոլոր բաները սովորաբար դուրս են ծրագրավորողի պատասխանատվությունից, կարծում եմ, դեռ օգտակար է դրանց ծանոթ լինել առնվազն 20%-ով (որը, ինչպես գիտեք, տալիս է արդյունքի 80%-ը): Որոշ անվերապահորեն կարևոր թեմաներ, ինչպիսիք են անվտանգությունը, դուրս կմնան այս նախագծից, քանի որ հեղինակը քիչ բան է հասկանում, որ այս համակարգը ստեղծվել է բացառապես անձնական օգտագործման համար: Ես ողջունում եմ ցանկացած կարծիք և կառուցողական քննադատություն։

Միկրոծառայությունների ստեղծում

Ծառայությունները գրվել են Java 11-ում՝ օգտագործելով Spring Boot: Միջսերվիսային փոխգործակցությունը կազմակերպվում է REST-ի միջոցով: Նախագիծը կներառի նվազագույն թվով թեստեր (որպեսզի հետագայում Ջենքինսում փորձարկելու բան լինի): Ծառայությունների սկզբնական կոդը հասանելի է GitHub-ում. backend и Դարպաս.

Ծառայություններից յուրաքանչյուրի կարգավիճակը ստուգելու համար նրանց կախվածություններին ավելացվել է Spring Actuator: Այն կստեղծի /ակտուատոր/առողջության վերջնակետ և կվերադարձնի 200 կարգավիճակ, եթե ծառայությունը պատրաստ է ընդունել տրաֆիկը, կամ 504, եթե խնդիր կա: Տվյալ դեպքում սա բավականին ֆիկտիվ ստուգում է, քանի որ ծառայությունները շատ պարզ են, և ինչ-որ ֆորսմաժորային իրավիճակի դեպքում դրանք ավելի հավանական է դառնում ամբողջովին անհասանելի, քան մասամբ գործելու: Սակայն իրական համակարգերում Actuator-ը կարող է օգնել ախտորոշել խնդիրը, նախքան օգտատերերը կսկսեն պայքարել դրա շուրջ: Օրինակ, եթե տվյալների շտեմարան մուտք գործելու հետ կապված խնդիրներ կան, մենք կարող ենք ավտոմատ կերպով պատասխանել դրան՝ դադարեցնելով անսարք ծառայության օրինակով հարցումների մշակումը:

Հետևի ծառայություն

Backend ծառայությունը պարզապես կհաշվի և կվերադարձնի ընդունված հարցումների քանակը:

Վերահսկիչի կոդը.

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Վերահսկիչի թեստ.

@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-ը հարցումը կուղարկի backend ծառայությանը՝ այն լրացնելով հետևյալ տեղեկատվությամբ.

  • դարպասի ID. Դա անհրաժեշտ է, որպեսզի սերվերի պատասխանով հնարավոր լինի տարբերակել դարպասի մի օրինակը մյուսից
  • Որոշ «գաղտնիք», որը կխաղա շատ կարևոր գաղտնաբառի դեր (կարևոր թխուկի գաղտնագրման բանալի համարը)

Կազմաձևումը application.properties-ում.

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

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<String> response = restTemplate.getForEntity(
backendUrl + REQUESTS_ENDPOINT, String.class);
        return response.getBody();
    }
}

Վերահսկիչ:

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

Գործարկել:

Մենք սկսում ենք հետին պլանը.

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

Դարպասի մեկնարկը.

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

Մենք ստուգում ենք.

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

Ամեն ինչ աշխատում է։ Ուշադիր ընթերցողը նկատում է, որ ոչինչ չի խանգարում մեզ ուղղակիորեն մուտք գործել հետին պլան՝ շրջանցելով դարպասը (http://localhost:8081/requests) Դա շտկելու համար ծառայությունները պետք է միավորվեն մեկ ցանցի մեջ, և միայն դարպասը պետք է «դուրս գա»:
Բացի այդ, երկու ծառայություններն էլ կիսում են մեկ ֆայլային համակարգ, արտադրում են հոսքեր և մի պահ կարող են սկսել խանգարել միմյանց: Լավ կլինի մեկուսացնել մեր միկրոսերվիսները: Դրան կարելի է հասնել տարբեր մեքենաների վրա հավելվածներ բաշխելու միջոցով (շատ գումար, դժվար), վիրտուալ մեքենաներ օգտագործելով (ռեսուրսների ինտենսիվ, երկար գործարկում) կամ կոնտեյներացման միջոցով: Ինչպես և սպասվում էր, մենք ընտրում ենք երրորդ տարբերակը և դոկեր որպես կոնտեյներացման գործիք:

դոկեր

Մի խոսքով, docker-ը ստեղծում է մեկուսացված կոնտեյներներ՝ մեկ հավելվածի համար: Docker-ն օգտագործելու համար հարկավոր է գրել Dockerfile՝ հավելվածը կառուցելու և գործարկելու հրահանգներ: Հաջորդը, դուք կարող եք կառուցել պատկերը, վերբեռնել այն պատկերի ռեեստրում (No. Dockerhub) և տեղադրեք ձեր միկրոսերվիսը ցանկացած դոկերացված միջավայրում մեկ հրամանով:

dockerfile

Պատկերի ամենակարևոր բնութագրիչներից մեկը դրա չափն է: Կոմպակտ պատկերն ավելի արագ կներբեռնվի հեռավոր պահոցից, ավելի քիչ տեղ կզբաղեցնի, և ձեր ծառայությունն ավելի արագ կսկսվի: Ցանկացած պատկեր կառուցված է բազային պատկերի հիման վրա, և խորհուրդ է տրվում ընտրել առավել մինիմալիստական ​​տարբերակը։ Լավ տարբերակ է Alpine-ը, Linux-ի ամբողջական բաշխում՝ նվազագույն փաթեթներով:

Նախ, եկեք փորձենք գրել Dockerfile «ճակատին» (ես անմիջապես կասեմ, որ սա վատ միջոց է, մի արեք):

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

Այստեղ մենք օգտագործում ենք Alpine հիմնված բազային պատկերը JDK-ով արդեն տեղադրված մեր նախագիծը կառուցելու համար: ADD հրամանով նկարին ավելացնում ենք ընթացիկ src գրացուցակը, այն նշում ենք որպես աշխատող (WORKDIR) և սկսում build-ը։ EXPOSE 8080 հրամանը ազդանշան է տալիս դոկերին, որ կոնտեյների հավելվածը կօգտագործի իր 8080 պորտը (սա հավելվածը հասանելի չի դարձնի դրսից, բայց թույլ կտա հավելվածին մուտք գործել, օրինակ, նույն դոկեր ցանցի մեկ այլ կոնտեյներից: )

Ծառայությունները պատկերների մեջ փաթեթավորելու համար դուք պետք է գործարկեք հրամաններ յուրաքանչյուր նախագծի արմատից.

docker image build . -t msvc-backend:1.0.0

Արդյունքը 456 ՄԲ պատկեր է (որից բազային JDK պատկերը զբաղեցրել է 340 ՄԲ): Եվ ամեն ինչ, չնայած այն հանգամանքին, որ մեր նախագծի դասերը կարելի է մատի վրա հաշվել: Մեր պատկերի չափը նվազեցնելու համար՝

  • Մենք օգտագործում ենք բազմաքայլ հավաքում: Առաջին քայլում մենք կկառուցենք նախագիծը, երկրորդ քայլում մենք կտեղադրենք JRE-ն, իսկ երրորդ քայլում մենք կպատճենենք այն ամենը նոր մաքուր Alpine պատկերի մեջ: Ընդհանուր առմամբ, վերջնական պատկերում կլինեն միայն անհրաժեշտ բաղադրիչները։
  • Եկեք օգտագործենք java-ի մոդուլյարացումը։ Սկսած Java 9-ից, դուք կարող եք օգտագործել jlink գործիքը JRE ստեղծելու համար հենց ձեզ անհրաժեշտ մոդուլներից

Հետաքրքրասերների համար ահա լավ հոդված պատկերի կրճատման մոտեցումների մասին: https://habr.com/ru/company/ruvds/blog/485650/.

Վերջնական 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 [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"]

Մենք վերստեղծում ենք պատկերը, և արդյունքում այն ​​կորցրել է իր քաշը 6 անգամ՝ կազմելով 77 ՄԲ։ Վատ չէ։ Դրանից հետո պատրաստի պատկերները կարող են վերբեռնվել պատկերների ռեեստր, որպեսզի ձեր պատկերները հասանելի լինեն ինտերնետից ներբեռնելու համար:

Docker-ում համատեղ իրականացվող ծառայություններ

Սկզբից մեր ծառայությունները պետք է լինեն նույն ցանցում: Docker-ում կան մի քանի տեսակի ցանցեր, և մենք օգտագործում ենք դրանցից ամենապրիմիտիվը՝ կամուրջը, որը թույլ է տալիս ցանցավորել նույն հոսթի վրա աշխատող կոնտեյներները։ Ստեղծեք ցանց հետևյալ հրամանով.

docker network create msvc-network

Այնուհետև սկսեք «backend» անունով հետնամասի կոնտեյները microservices-backend:1.0.0 պատկերով.

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

Հարկ է նշել, որ կամուրջների ցանցն ապահովում է բեռնարկղերի հայտնաբերում իրենց անվանումներով: Այսինքն, backend ծառայությունը հասանելի կլինի դոկերի ցանցի ներսում՝ ժամը http://backend:8080.

Դարպասի մեկնարկը.

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

Այս հրամանում մենք նշում ենք, որ մենք փոխանցում ենք մեր հոսթի 80-րդ նավահանգիստը կոնտեյների 8080 նավահանգիստ: Մենք օգտագործում ենք env տարբերակները շրջակա միջավայրի փոփոխականներ սահմանելու համար, որոնք ավտոմատ կերպով կկարդան գարնանը և կվերացնեն հավելվածի հատկությունները:

Սկսելուց հետո զանգում ենք http://localhost/ և համոզվեք, որ ամեն ինչ աշխատում է, ինչպես նախորդ դեպքում:

Ամփոփում

Արդյունքում մենք ստեղծեցինք երկու պարզ միկրոսերվիսներ, դրանք փաթեթավորեցինք դոկեր կոնտեյներներում և միասին գործարկեցինք նույն մեքենայի վրա: Ստացված համակարգը, սակայն, ունի մի շարք թերություններ.

  • Սխալների վատ հանդուրժողականություն - մեզ համար ամեն ինչ աշխատում է մեկ սերվերի վրա
  • Վատ մասշտաբայնություն. երբ բեռը մեծանում է, լավ կլինի ավտոմատ կերպով տեղակայել լրացուցիչ սպասարկման օրինակներ և հավասարակշռել բեռը դրանց միջև
  • Գործարկման բարդությունը. մեզ անհրաժեշտ էր մուտքագրել առնվազն 3 հրաման և որոշակի պարամետրերով (սա միայն 2 ծառայության համար է)

Վերոնշյալ խնդիրները շտկելու համար կան մի շարք լուծումներ, ինչպիսիք են Docker Swarm, Nomad, Kubernetes կամ OpenShift: Եթե ​​ամբողջ համակարգը գրված է Java-ով, կարող եք նայել դեպի Spring Cloud (լավ հոդված).

В հաջորդ մասը Ես կխոսեմ այն ​​մասին, թե ինչպես ստեղծեցի Kubernetes-ը և տեղակայեցի նախագիծը Google Kubernetes Engine-ում:

Source: www.habr.com

Добавить комментарий