Mikroservisləri necə yerləşdirməyi öyrənin. Hissə 1. Spring Boot və Docker

Mikroservisləri necə yerləşdirməyi öyrənin. Hissə 1. Spring Boot və Docker

Hey Habr.

Bu yazıda mən mikroservislərlə təcrübə aparmaq üçün öyrənmə mühiti yaratmaq təcrübəmdən danışmaq istəyirəm. Hər yeni aləti öyrənəndə mən həmişə onu təkcə yerli maşında deyil, həm də daha real şəraitdə sınamaq istəyirdim. Buna görə də, daha sonra hər cür maraqlı texnologiyalarla "örtülə" bilən sadələşdirilmiş mikroservis tətbiqi yaratmağa qərar verdim. Layihə üçün əsas tələb onun real sistemə maksimum funksional yaxınlığıdır.

Əvvəlcə layihənin yaradılmasını bir neçə mərhələyə böldüm:

  1. İki xidmət yaradın - "backend" (backend) və "gateway" (gateway), onları docker şəkillərinə yığın və birlikdə işləmək üçün quraşdırın

    Açar sözlər: Java 11, Spring Boot, Docker, image optimization

  2. Google Kubernetes Engine-də Kubernetes konfiqurasiyasının inkişafı və sistem yerləşdirilməsi

    Açar sözlər: Kubernetes, GKE, resurs idarəetməsi, avtomatik miqyaslama, sirlər

  3. Daha yaxşı klaster idarə edilməsi üçün Helm 3 ilə diaqram yaratmaq

    Teqlər: Helm 3, diaqramın yerləşdirilməsi

  4. Kodun klasterə avtomatik çatdırılması üçün Jenkins və boru kəmərinin qurulması

    Açar sözlər: Jenkins konfiqurasiyası, plaginlər, ayrıca konfiqurasiya deposu

Hər addıma ayrıca məqalə ayırmağı planlaşdırıram.

Bu məqalələr silsiləsinin diqqət mərkəzində mikroservislərin necə yazılması deyil, onların vahid sistemdə necə işləməsi lazımdır. Bütün bunlar, adətən, tərtibatçının məsuliyyətindən kənarda olsa da, məncə, onlarla ən azı 20% tanış olmaq hələ də faydalıdır (bu, bildiyiniz kimi, nəticənin 80% -ni verir). Bəzi qeyd-şərtsiz vacib mövzular, məsələn, təhlükəsizlik, bu layihədən kənarda qalacaq, çünki müəllif bu sistemin yalnız şəxsi istifadə üçün yaradıldığını çox az başa düşür. İstənilən rəyi və konstruktiv tənqidi alqışlayıram.

Mikroservislərin yaradılması

Xidmətlər Spring Boot istifadə edərək Java 11-də yazılmışdır. Xidmətlərarası qarşılıqlı əlaqə REST istifadə edərək təşkil edilir. Layihə minimum sayda testləri əhatə edəcək (sonradan Jenkins-də sınanacaq bir şey olması üçün). Xidmətlərin mənbə kodu GitHub-da mövcuddur: arxa uç и Gateway.

Xidmətlərin hər birinin vəziyyətini yoxlamaq üçün onların asılılıqlarına Yay Aktuatoru əlavə edilmişdir. O, /aktuator/sağlamlıq son nöqtəsi yaradacaq və əgər xidmət trafik qəbul etməyə hazırdırsa, 200 statusu, problem olduqda isə 504 statusu qaytaracaq. Bu halda, bu, kifayət qədər uydurma bir yoxlamadır, çünki xidmətlər çox sadədir və bəzi fors-major hallarında, qismən işləməkdənsə, tamamilə əlçatmaz olmaq ehtimalı daha yüksəkdir. Lakin real sistemlərdə Aktuator istifadəçilərin onunla mübarizə aparmağa başlamazdan əvvəl problemi aşkarlamağa kömək edə bilər. Məsələn, verilənlər bazasına daxil olmaqda problemlər yaranarsa, pozulmuş xidmət nümunəsi ilə sorğuların işlənməsini dayandırmaqla buna avtomatik cavab verə bilərik.

Backend Xidməti

Backend xidməti sadəcə olaraq qəbul edilmiş sorğuların sayını hesablayacaq və qaytaracaq.

Nəzarətçi kodu:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Nəzarətçi testi:

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

Xidmət Gateway

Şlüz sorğunu aşağıdakı məlumatlarla tamamlayaraq backend xidmətinə yönləndirəcək:

  • gateway id. Şlüzün bir nümunəsini digərindən server cavabı ilə ayırd etmək mümkün olması üçün lazımdır.
  • Çox vacib bir parol rolunu oynayacaq bəzi "sirr" (vacib kukinin şifrələmə açarının sayı)

application.properties-də konfiqurasiya:

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

Arxa uç adapteri:

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

Nəzarətçi:

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

Başla:

Arxa tərəfə başlayırıq:

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

Şluzun işə salınması:

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

Yoxlayırıq:

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

Hər şey işləyir. Diqqətli oxucu qeyd edəcək ki, heç nə bizə şluzdan yan keçərək birbaşa arxa hissəyə daxil olmağa mane olmur (http://localhost:8081/requests). Bunu düzəltmək üçün xidmətlər bir şəbəkədə birləşdirilməlidir və yalnız şlüz kənardan "çıxmalıdır".
Həmçinin, hər iki xidmət bir fayl sistemini paylaşır, axınlar yaradır və bir anda bir-birinə müdaxilə etməyə başlaya bilər. Mikroservislərimizi təcrid etmək yaxşı olardı. Buna proqramları müxtəlif maşınlar arasında paylamaqla (çox pul, çətin), virtual maşınlardan (resurs tələb edən, uzun müddət işə salınma) və ya konteynerləşdirmədən istifadə etməklə nail olmaq olar. Gözlənildiyi kimi, üçüncü variantı seçirik və yükvuran konteynerləşdirmə vasitəsi kimi.

yükvuran

Qısacası, docker hər proqram üçün bir olan təcrid olunmuş konteynerlər yaradır. Docker-dən istifadə etmək üçün siz Dockerfile yazmalısınız - tətbiqi qurmaq və işə salmaq üçün təlimatlar. Sonra, bir şəkil yarada, onu şəkil reyestrinə yükləyə bilərsiniz (№ XNUMX). Dockerhub) və mikroservisinizi bir əmrlə istənilən dokerləşdirilmiş mühitdə yerləşdirin.

Docker faylı

Təsvirin ən vacib xüsusiyyətlərindən biri onun ölçüsüdür. Kompakt şəkil uzaq depodan daha sürətli yüklənəcək, daha az yer tutacaq və xidmətiniz daha sürətli başlayacaq. Hər hansı bir şəkil əsas təsvir əsasında qurulur və ən minimalist variantı seçmək tövsiyə olunur. Yaxşı seçim Alpine, minimal paketlərlə tam Linux paylanmasıdır.

Başlamaq üçün gəlin "alnına" Dockerfile yazmağa çalışaq (dərhal deyəcəyəm ki, bu pis yoldur, bunu etməyin):

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

Burada biz layihəmizi qurmaq üçün artıq quraşdırılmış JDK ilə Alp dağlarına əsaslanan əsas təsvirdən istifadə edirik. ADD əmri ilə şəklə cari src kataloqunu əlavə edirik, onu işləyir (WORKDIR) qeyd edirik və qurmağa başlayırıq. EXPOSE 8080 əmri dokerə siqnal verir ki, konteynerdəki tətbiq öz portu 8080-dən istifadə edəcək (bu, tətbiqi kənardan əlçatan etməyəcək, lakin tətbiqə, məsələn, eyni doker şəbəkəsindəki başqa konteynerdən daxil olmağa imkan verəcəkdir. ).

Xidmətləri şəkillərdə paketləmək üçün hər bir layihənin kökündən əmrlər işlətməlisiniz:

docker image build . -t msvc-backend:1.0.0

Nəticə 456 MB təsvirdir (onlardan əsas JDK təsviri 340 MB tutdu). Layihəmizdəki dərsləri barmaqla saymaq olar. Şəkilimizin ölçüsünü azaltmaq üçün:

  • Çox addımlı montajdan istifadə edirik. Birinci mərhələdə biz layihəni quracağıq, ikinci mərhələdə JRE-ni quraşdıracağıq və üçüncü mərhələdə hamısını yeni təmiz Alp təsvirinə köçürəcəyik. Ümumilikdə, son görüntüdə yalnız zəruri komponentlər olacaq.
  • Java-nın modullaşdırılmasından istifadə edək. Java 9-dan başlayaraq, yalnız sizə lazım olan modullardan JRE yaratmaq üçün jlink alətindən istifadə edə bilərsiniz

Maraqlılar üçün təsviri azaltma yanaşmaları haqqında yaxşı bir məqalə var. https://habr.com/ru/company/ruvds/blog/485650/.

Son Dockerfayl:

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

Şəkili yenidən yaradırıq və nəticədə o, çəkisini 6 dəfə itirərək 77 MB təşkil edir. Pis deyil. Bundan sonra, hazır şəkilləri şəkillər reyestrinə yükləmək olar ki, şəkilləriniz İnternetdən yükləmək üçün əlçatan olsun.

Docker-də birgə işləyən xidmətlər

Başlamaq üçün xidmətlərimiz eyni şəbəkədə olmalıdır. Docker-də şəbəkələrin bir neçə növü var və biz onların ən primitivindən - eyni hostda işləyən konteynerləri şəbəkələşdirməyə imkan verən körpüdən istifadə edirik. Aşağıdakı əmrlə şəbəkə yaradın:

docker network create msvc-network

Sonra, microservices-backend:1.0.0 şəkli ilə 'backend' adlı backend konteynerini işə salın:

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

Qeyd etmək lazımdır ki, körpü şəbəkəsi öz adları ilə konteynerlər üçün hazır xidmət kəşfini təmin edir. Yəni, backend xidməti docker şəbəkəsinin daxilində mövcud olacaq http://backend:8080.

Şluzun işə salınması:

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

Bu əmrdə biz hostumuzun 80 portunu konteynerin 8080 portuna yönləndirdiyimizi bildiririk. Yaz tərəfindən avtomatik oxunacaq və application.properties-dən xassələri ləğv edəcək mühit dəyişənlərini təyin etmək üçün env seçimlərindən istifadə edirik.

Başladıqdan sonra zəng edirik http://localhost/ və əvvəlki vəziyyətdə olduğu kimi hər şeyin işlədiyinə əmin olun.

Nəticə

Nəticədə biz iki sadə mikroservis yaratdıq, onları doker konteynerlərinə yığdıq və eyni maşında birlikdə işə saldıq. Bununla belə, ortaya çıxan sistemin bir sıra çatışmazlıqları var:

  • Zəif xətaya dözümlülük - hər şey bizim üçün bir serverdə işləyir
  • Zəif miqyaslılıq - yük artdıqda, əlavə xidmət nümunələrini avtomatik olaraq yerləşdirmək və onlar arasında yükü balanslaşdırmaq yaxşı olardı
  • Başlatmanın mürəkkəbliyi - ən azı 3 əmr daxil etməli idik və müəyyən parametrlərlə (bu yalnız 2 xidmət üçündür)

Yuxarıdakı problemləri həll etmək üçün Docker Swarm, Nomad, Kubernetes və ya OpenShift kimi bir sıra həllər var. Bütün sistem Java-da yazılıbsa, Spring Cloud-a baxa bilərsiniz (yaxşı məqalə).

В növbəti hissə Mən Kubernetes-i necə qurduğum və layihəni Google Kubernetes Engine-də yerləşdirdiyim barədə danışacağam.

Mənbə: www.habr.com

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