Léiert wéi Dir Mikroservicer ofsetzt. Deel 1. Fréijoer Boot an Docker

Léiert wéi Dir Mikroservicer ofsetzt. Deel 1. Fréijoer Boot an Docker

Hey Habr.

An dësem Artikel wëll ech iwwer meng Erfahrung schwätzen, e Léierëmfeld ze kreéieren fir mat Mikroservicer ze experimentéieren. Wann ech all neit Tool léieren, wollt ech et ëmmer probéieren net nëmmen op menger lokaler Maschinn, awer och a méi realistesche Konditiounen. Dofir hunn ech décidéiert eng vereinfacht Mikroservice-Applikatioun ze kreéieren, déi spéider mat all méiglechen interessanten Technologien "hänke kéint". D'Haaptfuerderung fir de Projet ass seng maximal funktionell Proximitéit zum realen System.

Am Ufank hunn ech d'Schafung vum Projet an e puer Schrëtt opgedeelt:

  1. Erstellt zwee Servicer - 'Backend' a 'Gateway', packt se an Docker-Biller a konfiguréiert se fir zesummen ze schaffen

    Schlësselwieder: Java 11, Spring Boot, Docker, Bildoptimiséierung

  2. Entwécklung vu Kubernetes Konfiguratioun an Deployment System am Google Kubernetes Engine

    Schlësselwieder: Kubernetes, GKE, Ressource Gestioun, Autoscaling, Geheimnisser

  3. Erstellt eng Diagramm mat Helm 3 fir méi effizient Clustermanagement

    Schlësselwieder: Helm 3, Chart Deployment

  4. Jenkins a Pipeline opsetzen fir automatesch Code an de Cluster ze liwweren

    Schlësselwieder: Jenkins Konfiguratioun, Plugins, getrennten Konfiguratiounsrepository

Ech plangen fir all Schrëtt en separaten Artikel ze widmen.

De Fokus vun dëser Serie vun Artikelen ass net wéi Dir Mikroservicer schreift, awer wéi se an engem eenzege System funktionnéieren. Wärend all dës Saachen normalerweis ausserhalb vun der Verantwortung vum Entwéckler sinn, mengen ech, et ass nach ëmmer nëtzlech fir op d'mannst 20% vertraut ze sinn (wat bekannt ass fir 80% vum Resultat auszemaachen). E puer absolut wichteg Themen, wéi d'Sécherheet, ginn aus dësem Projet ewechgelooss, well den Auteur wéineg doriwwer versteet; de System gëtt exklusiv fir perséinlech Notzung erstallt. Ech begréissen all Meenung a konstruktiv Kritik.

Erstellt Mikroservicer

D'Servicer goufen am Java 11 mat Spring Boot geschriwwe. Inter-Service Kommunikatioun gëtt mat REST organiséiert. De Projet wäert eng Minimum Unzuel vun Tester enthalen (sou datt et spéider eppes am Jenkins ze testen gëtt). De Quellcode fir d'Servicer ass verfügbar op GitHub: backend и Paart.

Fir den Zoustand vun jidderengem vun de Servicer z'iwwerpréiwen, gouf e Spring Actuator zu hirer Ofhängegkeet bäigefüügt. Et wäert en Endpunkt / Aktuator / Gesondheet erstellen an e Status vun 200 zréckginn wann de Service prett ass fir de Verkéier ze akzeptéieren, oder 504 am Fall vu Probleemer. An dësem Fall ass dëst eng zimlech fiktiv Scheck, well d'Servicer ganz einfach sinn, an ënner enger Aart vu Force Majeure si se méi wahrscheinlech komplett net verfügbar wéi deelweis operationell ze bleiwen. Awer an echte Systemer kann den Actuator hëllefen e Problem ze diagnostizéieren ier d'Benotzer ufänken drop ze schloen. Zum Beispill, wa Probleemer mam Zougang zu der Datebank entstinn, kënne mir automatesch op dëst reagéieren andeems d'Veraarbechtung vun Ufroe mat enger futtisse Instanz vum Service stoppen.

Backend Service

De Backend Service wäert einfach d'Zuel vun akzeptéierten Ufroe zielen an zréckginn.

Controller Code:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Controller Test:

@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 Service

De Paart schéckt d'Ufro un de Backend-Service weider, ergänzt se mat der folgender Informatioun:

  • gateway id. Et ass néideg fir datt eng Instanz vum Paart vun engem aneren duerch d'ServerÄntwert ënnerscheet ka ginn
  • E gewësse "Geheimnis" dat d'Roll vun engem ganz wichtege Passwuert spillt (Schlësselnummer fir d'Verschlësselung vun engem wichtege Cookie)

Konfiguratioun an application.properties:

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

Adapter fir Kommunikatioun mam 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();
    }
}

Controller:

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

Start:

Loosst eis de Backend starten:

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

Loosst eis de Paart starten:

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

Mir iwwerpréiwen:

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

Alles funktionnéiert. Den opmerksamen Lieser bemierkt datt näischt eis verhënnert direkt op de Backend ze kommen, de Paart ëmgoen (http://localhost:8081/requests). Fir dëst ze fixéieren, mussen d'Servicer an engem Netz kombinéiert ginn, an nëmmen d'Paart soll dobausse "ausstecken".
Och béid Servicer deelen deeselwechte Dateiesystem, generéieren Threads, a kënnen op engem Punkt ufänken mateneen ze stéieren. Et wier flott eis Mikroservicer ze isoléieren. Dëst kann erreecht ginn andeems Dir Uwendungen iwwer verschidde Maschinnen verdeelt (vill Suen, schwéier), virtuell Maschinnen benotzt (Ressourceintensiv, laang Startup) oder Containeriséierung benotzt. Wéi erwaart, wielt mir déi drëtt Optioun an Docker als Instrument fir Containeriséierung.

Docker

Kuerz gesot, Docker erstellt isoléiert Container, een pro Applikatioun. Fir Docker ze benotzen, musst Dir eng Dockerfile schreiwen - Instruktioune fir d'Applikatioun ze bauen an ze lafen. Als nächst kënnt Dir d'Bild bauen, eropluede se an d'Bildregistrierung (Nr. Dockerhub) an installéiert Äre Mikroservice an all dockeriséierten Ëmfeld an engem Kommando.

dockerfile

Ee vun de wichtegste Charakteristiken vun engem Bild ass seng Gréisst. E kompakt Bild wäert méi séier vun engem Remote Repository eroflueden, manner Plaz ophuelen, an Äre Service fänkt méi séier un. All Bild gëtt op Basis vun engem Basisbild gebaut, an et ass recommandéiert déi minimalistesch Optioun ze wielen. Eng gutt Optioun ass Alpine, eng vollwäerteg Linux Verdeelung mat engem Minimum vu Packagen.

Als éischt, loosst eis probéieren en Dockerfile "head-on" ze schreiwen (ech soen direkt datt dëst e schlechte Wee ass, maacht et net):

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

Hei benotze mir en Alpine-baséiert Basisbild mat der JDK scho installéiert fir eise Projet ze bauen. Mat dem ADD Kommando addéiere mir den aktuellen src Verzeichnis an d'Bild, markéieren et als funktionnéiert (WORKDIR) a starten de Bau. Den EXPOSE 8080 Kommando signaliséiert dem Docker datt d'Applikatioun am Container säin Hafen 8080 benotzt (dëst wäert d'Applikatioun net vu baussen zougänglech maachen, awer erlaabt d'Applikatioun z'erreechen, zum Beispill vun engem anere Container am selwechte Docker-Netzwierk ).

Fir Servicer a Biller ze packen, musst Dir d'Befehle vun der Root vun all Projet ausféieren:

docker image build . -t msvc-backend:1.0.0

Als Resultat kréie mir e Bild vun 456 MB an der Gréisst (vun deem d'Basis JDK 340 Bild MB huet). An dat trotz der Tatsaach, datt d'Klassen an eisem Projet op engem Fanger gezielt kënne ginn. Fir d'Gréisst vun eisem Bild ze reduzéieren:

  • Mir benotzen Multi-Schrëtt Assemblée. Am éischte Schrëtt wäerte mir de Projet montéieren, an der zweeter installéiere mir de JRE, an am drëtte Schrëtt kopéiere mir dat alles an en neit proppert Alpine Bild. Am Ganzen wäert d'Finale Bild nëmmen déi néideg Komponente enthalen.
  • Loosst eis Java Modulariséierung benotzen. Vun Java 9 un, kënnt Dir de jlink Tool benotze fir e JRE aus nëmmen de Moduler ze kreéieren déi Dir braucht

Fir déi virwëtzeg, hei ass e gudden Artikel iwwer Approche fir Bildgréissten ze reduzéieren https://habr.com/ru/company/ruvds/blog/485650/.

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

Mir hunn d'Bild nei erstallt, an et huet um Enn 6 Mol d'Gewiicht verluer, wat op 77 MB bedroht. Net schlecht. Duerno kënnen déi fäerdeg Biller an d'Bildregistrierung eropgeluede ginn, sou datt Är Biller vum Internet verfügbar sinn.

Lafen Servicer zesummen am Docker

Fir unzefänken mussen eis Servicer um selwechten Netz sinn. Et gi verschidden Aarte vu Netzwierker am Docker, a mir benotzen déi primitivst vun hinnen - Bréck, wat Iech erlaabt Iech Container op dem selwechte Host ze vernetzen. Loosst eis en Netzwierk erstellen mat dem folgenden Kommando:

docker network create msvc-network

Als nächst, loosst eis e Backend Container mam Numm 'Backend' mam Image microservices-backend: 1.0.0 starten:

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

Et ass derwäert ze bemierken datt de Brécknetz Service Entdeckung aus der Këscht fir Container mat hiren Nimm ubitt. Dat ass, de Backend Service wäert am Docker Netzwierk verfügbar sinn http://backend:8080.

Loosst eis de Paart starten:

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

An dësem Kommando weisen mir datt mir den Hafen 80 vun eisem Host op den Hafen 8080 vum Container weiderginn. Mir benotzen env Optiounen fir Ëmfeld Variablen ze setzen déi automatesch vum Fréijoer gelies ginn an d'Eegeschafte vun application.properties iwwerschreiden.

Nom Start, rufft http://localhost/ a gitt sécher datt alles funktionnéiert, wéi am virege Fall.

Konklusioun

Als Resultat hu mir zwee einfache Mikroservicer erstallt, se an Docker Container verpackt an se zesummen op der selwechter Maschinn lancéiert. De resultéierende System huet awer eng Rei Nodeeler:

  • Schlecht Feeler Toleranz - alles funktionnéiert op engem Server fir eis
  • Schlecht Skalierbarkeet - wéi d'Laascht eropgeet, wier et flott fir automatesch zousätzlech Serviceinstanzen z'installéieren an d'Laascht tëscht hinnen ze balanséieren
  • Komplexitéit starten - mir hu misse mindestens 3 Kommandoen aginn, mat bestëmmte Parameteren (dëst ass nëmme fir 2 Servicer)

Fir déi uewe genannte Problemer ze léisen, ginn et eng Rei Léisunge wéi Docker Swarm, Nomad, Kubernetes oder OpenShift. Wann de ganze System op Java geschriwwe gëtt, kënnt Dir op Spring Cloud kucken (gudden Artikel).

В nächsten Deel Ech soen Iech iwwer wéi ech Kubernetes ageriicht hunn an de Projet op Google Kubernetes Engine ofgesat hunn.

Source: will.com

Setzt e Commentaire