Opi ottamaan mikropalvelut käyttöön. Osa 1. Spring Boot ja Docker

Opi ottamaan mikropalvelut käyttöön. Osa 1. Spring Boot ja Docker

Hei Habr.

Tässä artikkelissa haluan kertoa kokemuksestani oppimisympäristön luomisesta mikropalvelujen kokeilua varten. Kun opin jokaisen uuden työkalun, halusin aina kokeilla sitä paitsi paikallisella koneella myös realistisemmissa olosuhteissa. Siksi päätin luoda yksinkertaistetun mikropalvelusovelluksen, joka voidaan myöhemmin "peittää" kaikenlaisilla mielenkiintoisilla teknologioilla. Hankkeen päävaatimus on sen maksimaalinen toiminnallinen läheisyys todelliseen järjestelmään.

Aluksi jaoin projektin luomisen useisiin vaiheisiin:

  1. Luo kaksi palvelua - "backend" (backend) ja "gateway" (yhdyskäytävä), pakkaa ne Docker-kuviin ja määritä ne toimimaan yhdessä

    Avainsanat: Java 11, Spring Boot, Docker, kuvan optimointi

  2. Kubernetes-määritysten ja järjestelmän käyttöönoton kehittäminen Google Kubernetes Enginessä

    Avainsanat: Kubernetes, GKE, resurssienhallinta, automaattinen skaalaus, salaisuudet

  3. Kaavion luominen Helm 3:lla parantaaksesi klusterin hallintaa

    Tunnisteet: Helm 3, kaavion käyttöönotto

  4. Jenkinsin ja putkilinjan määrittäminen koodin automaattista toimittamista varten klusteriin

    Avainsanat: Jenkins-kokoonpano, lisäosat, erillinen konfiguraatiovarasto

Aion omistaa jokaiselle vaiheelle erillisen artikkelin.

Tämän artikkelisarjan painopiste ei ole mikropalvelujen kirjoittamisessa, vaan niiden saamisessa toimimaan yhdessä järjestelmässä. Vaikka kaikki nämä asiat ovat yleensä kehittäjän vastuun ulkopuolella, mielestäni on silti hyödyllistä tuntea ne vähintään 20% (joka, kuten tiedätte, antaa 80% tuloksesta). Jotkut ehdottoman tärkeät aiheet, kuten turvallisuus, jätetään pois tästä projektista, koska kirjoittaja ymmärtää vähän siitä, että tämä järjestelmä on luotu vain henkilökohtaiseen käyttöön. Otan mielelläni vastaan ​​kaikki mielipiteet ja rakentava kritiikki.

Mikropalveluiden luominen

Palvelut on kirjoitettu Java 11:llä Spring Bootilla. Palvelujen välinen vuorovaikutus järjestetään REST:n avulla. Projekti sisältää vähimmäismäärän testejä (jotta myöhemmin Jenkinsissä on jotain testattavaa). Palveluiden lähdekoodi on saatavilla GitHubista: tausta и Gateway.

Jotta kunkin palvelun tila voidaan tarkistaa, niiden riippuvuuksiin on lisätty jousitoimilaite. Se luo /actuator/health-päätepisteen ja palauttaa tilan 200, jos palvelu on valmis vastaanottamaan liikennettä, tai 504, jos on ongelma. Tässä tapauksessa kyseessä on melko fiktiivinen tarkistus, koska palvelut ovat hyvin yksinkertaisia ​​ja ylivoimaisen esteen sattuessa ne todennäköisesti jäävät kokonaan pois käytöstä kuin jäävät osittain toimiviksi. Mutta todellisissa järjestelmissä Actuator voi auttaa diagnosoimaan ongelman ennen kuin käyttäjät alkavat taistella siitä. Jos esimerkiksi tietokantaan pääsyssä on ongelmia, voimme vastata tähän automaattisesti pysäyttämällä pyyntöjen käsittelyn rikkinäisten palveluinstanssien kanssa.

Takapääpalvelu

Taustapalvelu yksinkertaisesti laskee ja palauttaa hyväksyttyjen pyyntöjen määrän.

Ohjaimen koodi:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

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

Palveluyhdyskäytävä

Yhdyskäytävä välittää pyynnön taustapalveluun täydentäen sitä seuraavilla tiedoilla:

  • yhdyskäytävän tunnus. Sitä tarvitaan, jotta yhdyskäytävän ilmentymä voidaan erottaa toisesta palvelimen vastauksen perusteella
  • Jokin "salaisuus", joka toimii erittäin tärkeänä salasanana (tärkeän evästeen salausavaimen numero)

Määritykset sovelluksessa application.properties:

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

Taustasovitin:

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

Ohjain:

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

Tuoda markkinoille:

Aloitamme taustaohjelman:

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

Yhdyskäytävän käynnistäminen:

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

tarkista:

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

Kaikki toimii. Huomaavainen lukija huomaa, että mikään ei estä meitä pääsemästä suoraan taustajärjestelmään ohittaen yhdyskäytävän (http://localhost:8081/requests). Tämän korjaamiseksi palvelut on yhdistettävä yhdeksi verkkoksi ja vain yhdyskäytävän tulee "ulkoutua" ulkopuolelle.
Lisäksi molemmat palvelut jakavat yhden tiedostojärjestelmän, tuottavat streameja ja voivat alkaa häiritä toisiaan. Olisi mukavaa eristää mikropalvelumme. Tämä voidaan saavuttaa jakamalla sovelluksia eri koneille (paljon rahaa, vaikeaa), käyttämällä virtuaalikoneita (resurssiintensiivinen, pitkä käynnistys) tai käyttämällä konttia. Kuten odotettiin, valitsemme kolmannen vaihtoehdon ja Satamatyöläinen työkaluna konttien käsittelyyn.

Satamatyöläinen

Lyhyesti sanottuna Docker luo eristettyjä säiliöitä, yhden sovellusta kohden. Dockerin käyttämiseksi sinun on kirjoitettava Dockerfile - ohjeet sovelluksen rakentamiseen ja suorittamiseen. Seuraavaksi voit rakentaa kuvan, ladata sen kuvarekisteriin (nro. Dockerhub) ja ota mikropalvelusi käyttöön missä tahansa telakoidussa ympäristössä yhdellä komennolla.

Dockerfile

Yksi kuvan tärkeimmistä ominaisuuksista on sen koko. Kompakti kuva latautuu nopeammin etävarastosta, vie vähemmän tilaa ja palvelusi käynnistyy nopeammin. Mikä tahansa kuva rakennetaan peruskuvan perusteella, ja on suositeltavaa valita minimalistisin vaihtoehto. Hyvä vaihtoehto on Alpine, täydellinen Linux-jakelu minimaalisilla paketeilla.

Aluksi yritetään kirjoittaa Docker-tiedosto "otsaan" (sanoan heti, että tämä on huono tapa, älä tee sitä):

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

Tässä käytämme Alpine-pohjaista peruskuvaa, jossa JDK on jo asennettu projektin rakentamiseen. ADD-komennolla lisäämme kuvan nykyisen src-hakemiston, merkitsemme sen toimivaksi (WORKDIR) ja aloitamme rakentamisen. EXPOSE 8080 -komento ilmoittaa dockerille, että säiliössä oleva sovellus käyttää porttiaan 8080 (tämä ei tee sovellusta saatavaa ulkopuolelta, mutta mahdollistaa sovelluksen käytön esimerkiksi toisesta saman telakointiverkoston kontista ).

Jos haluat pakata palvelut kuviksi, sinun on suoritettava komennot kunkin projektin juuresta:

docker image build . -t msvc-backend:1.0.0

Tuloksena on 456 Mt:n kuva (josta JDK-peruskuva vei 340 Mt). Ja kaikki siitä huolimatta, että projektimme luokat voidaan laskea sormella. Voit pienentää kuvamme kokoa seuraavasti:

  • Käytämme monivaiheista kokoonpanoa. Ensimmäisessä vaiheessa rakennamme projektin, toisessa vaiheessa asennamme JRE:n ja kolmannessa vaiheessa kopioimme kaiken uuteen puhtaaseen Alpine-imagoon. Yhteensä vain tarvittavat komponentit ovat lopullisessa kuvassa.
  • Käytetään Java:n modularisointia. Java 9:stä alkaen voit käyttää jlink-työkalua JRE:n luomiseen vain tarvitsemistasi moduuleista

Uteliaisille tässä on hyvä artikkeli kuvanvähennysmenetelmistä. https://habr.com/ru/company/ruvds/blog/485650/.

Lopullinen Docker-tiedosto:

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

Luomme kuvan uudelleen, ja sen seurauksena se menetti 6 kertaa painoaan eli 77 Mt. Ei paha. Tämän jälkeen valmiita kuvia voidaan ladata kuvarekisteriin, jotta kuvat ovat ladattavissa Internetistä.

Yhteiskäyttöpalvelut Dockerissa

Aluksi palveluidemme on oltava samassa verkossa. Dockerissa on useita verkkotyyppejä, ja käytämme niistä alkeellisinta - siltaa, jonka avulla voit verkottaa samassa isännässä toimivia säiliöitä. Luo verkko seuraavalla komennolla:

docker network create msvc-network

Käynnistä seuraavaksi backend-säilö nimeltä "backend" microservices-backend:1.0.0-kuvalla:

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

On syytä huomata, että siltaverkko tarjoaa käyttövalmiin palvelun etsinnän konteille niiden nimien perusteella. Eli taustapalvelu on saatavilla telakointiverkon sisällä osoitteessa http://backend:8080.

Yhdyskäytävän käynnistäminen:

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

Tässä komennossa osoitamme, että välitämme isäntämme portin 80 kontin porttiin 8080. Käytämme env-asetuksia asettaaksemme ympäristömuuttujia, jotka jousi lukee automaattisesti ja jotka ohittavat ominaisuudet tiedostosta application.properties.

Aloittamisen jälkeen soitamme http://localhost/ ja varmista, että kaikki toimii, kuten edellisessä tapauksessa.

Johtopäätös

Lopputuloksena loimme kaksi yksinkertaista mikropalvelua, pakkasimme ne telakointisäiliöihin ja käynnistimme ne yhdessä samassa koneessa. Tuloksena olevalla järjestelmällä on kuitenkin useita haittoja:

  • Huono vikasietoisuus - kaikki toimii meillä yhdellä palvelimella
  • Huono skaalautuvuus - kuormituksen kasvaessa olisi mukava ottaa automaattisesti käyttöön lisäpalveluinstanssit ja tasapainottaa niiden välinen kuormitus
  • Käynnistyksen monimutkaisuus - meidän piti antaa vähintään 3 komentoa ja tietyillä parametreilla (tämä koskee vain kahta palvelua)

Yllä olevien ongelmien korjaamiseksi on olemassa useita ratkaisuja, kuten Docker Swarm, Nomad, Kubernetes tai OpenShift. Jos koko järjestelmä on kirjoitettu Javalla, voit katsoa kohti Spring Cloudia (hyvä artikkeli).

В seuraava osa Puhun siitä, kuinka otin Kubernetesin käyttöön ja otin projektin käyttöön Google Kubernetes Enginessä.

Lähde: will.com

Lisää kommentti