
Helló, Habr.
Ebben a cikkben szeretném megosztani tapasztalataimat a mikroszolgáltatásokkal való kísérletezéshez szükséges tanulási környezet létrehozásáról. Minden új eszköz megismerésekor mindig is szerettem volna kipróbálni nemcsak a helyi gépemen, hanem valósághűbb körülmények között is. Ezért úgy döntöttem, hogy létrehozok egy egyszerűsített mikroszolgáltatás alkalmazást, amelyet később "megspékelhetek" különféle érdekes technológiákkal. A projekt fő követelménye a lehető legnagyobb funkcionalitásbeli hasonlóság a valós rendszerhez.
Eredetileg a projekt létrehozását több lépésre osztottam:
Két szolgáltatás létrehozása — ‘backend’ és ‘gateway’, azok Docker-képekbe csomagolása és együttműködésük beállítása
Kulcsszavak: Java 11, Spring Boot, Docker, képkulcsoptimalizálás
Kulcsszavak: Kubernetes, GKE, erőforrás-kezelés, automatikus skálázás, titkok
Chart létrehozása a Helm 3 segítségével a klaszter hatékonyabb kezeléséhez
Kulcsszavak: Helm 3, chart telepítés
Jenkins beállítása és pipeline létrehozása a kód automatikus szállításához a klaszterbe
Kulcsszavak: Jenkins konfiguráció, pluginok, külön konfigurációs tároló
Minden lépéshez külön cikket tervezek írni.
E cikksorozat célja nem az, hogy mikroszolgáltatásokat írjak, hanem hogy azokat egyetlen rendszerben működésre bírjam. Bár ezek a dolgok általában a fejlesztői felelősségen kívül esnek, úgy gondolom, hogy hasznos ismerni őket legalább 20%-ban (ami, mint tudjuk, a hatékonyság 80%-át adja). Néhány, feltétlenül fontos téma, mint a biztonság biztosítása, kívül marad a projekt keretein, mivel a szerző ebben nem jártas; a rendszer kizárólag személyes használatra készült. Örömmel látom az észrevételeket és az építő jellegű kritikát.
Mikroszolgáltatások létrehozása
A szolgáltatások Java 11-ben íródtak, Spring Boot használatával. A szolgáltatások közötti interakció REST segítségével kerül megszervezésre. A projekt minimális számú tesztet fog tartalmazni (hogy később legyen mit tesztelni a Jenkinsben). A szolgáltatások forráskódja elérhető a GitHub-on: és .
A szolgáltatások állapotának ellenőrzéséhez a Spring Actuatort hozzáadtuk az összeszott függőségekhez. Ez létrehozza az /actuator/health végpontot, és 200-as státuszt fog visszaadni, ha a szolgáltatás kész a forgalom fogadására, vagy 504-et, ha problémák merülnek fel. Ebben az esetben ez egy meglehetősen formális ellenőrzés, mivel a szolgáltatások nagyon egyszerűek, és ha valami váratlan történik, valószínűbb, hogy teljesen leállnak, mint hogy részlegesen működjenek. De valós rendszerekben az Actuator segíthet diagnosztizálni a problémát, mielőtt a felhasználók elkezdenék azt tapasztalni. Például, ha problémák merülnek fel az adatbázis elérésével, automatikusan reagálhatunk rá, megszüntetve a hibás szolgáltatás által kezelt kéréseket.
Backend Szolgáltatás
A backend szolgáltatás egyszerűen számolja és visszaadja a fogadott kérések számát.
Controller kód:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}Controller teszt:
@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 Szolgáltatás
A gateway a háttérszolgáltatásra irányítja a kéréseket, kiegészítve az alábbi információval:
- a gateway azonosítója. Szükséges, hogy az egyik gateway példány válaszából megkülönböztethessük a másiktól
- Egy "titok", amely nagyon fontos jelszóként fog működni (az értékes süti titkosító kulcsának száma)
Konfiguráció az application.properties fájlban:
backend.url=http://localhost:8081
instance.id=${random.int}
secret="default-secret"Adapter a backendhez való kapcsolódáshoz:
@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 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("Kérések száma: %s (gateway %d, titok %s)", backendAdapter.getRequests(), instanceId, secret);
}
}Indítás:
Backend indítása:
./mvnw package -DskipTests
java -Dserver.port=8081 -jar target/microservices-backend-1.0.0.jarGateway indítása:
./mvnw package -DskipTests
java -jar target/microservices-gateway-1.0.0.jarEllenőrizzük:
$ curl http://localhost:8080/
Kérések száma 1 (gateway 38560358, titkos "default-secret")Minden működik. A figyelmes olvasó észreveszi, hogy semmi nem akadályozza meg, hogy közvetlenül a backendhez forduljunk a gateway megkerülésével (). Ennek kijavításához a szolgáltatásokat egy hálózatba kell összekapcsolni, és csak a gateway-nek kell látszania kívül.
Ezenkívül mindkét szolgáltatás osztozik egy fájlrendszeren, szálakat generálnak, és egy adott pillanatban zavarhatják egymást. Jó lenne elkülöníteni a mikroszolgáltatásainkat. Ezt elérhetjük az alkalmazások különböző gépekre telepítésével (sok pénz, bonyolult), virtuális gépek használatával (erőforrásigényes, lassú indítás) vagy konténerizálás segítségével. Várhatóan a harmadik opciót választjuk, és mint konténerizáló eszközt.
Docker
Röviden, a Docker izolált konténereket hoz létre, egyet az egyes alkalmazásokhoz. A Docker használatához Dockerfile-t kell írni - egy utasítást az alkalmazás összeállításához és indításához. Ezután létrehozhatunk egy képet, feltölthetjük egy képtárba (№ ) és egy parancs segítségével telepíthetjük mikroszolgáltatásunkat bármelyik dockerizált környezetbe.
Dockerfile
A kép egyik legfontosabb jellemzője a mérete. A kompakt kép gyorsabban letölthető a távoli tárolóból, kevesebb helyet foglal el, és az Ön szolgáltatása gyorsabban elindul. Bármely kép egy alap képből épül fel, és ajánlott a lehető legminimalistább változatot választani. Jó választás az Alpine - egy teljes Linux disztribúció minimális csomagokkal.
Először próbáljunk meg egy Dockerfile-t "lóbálni" (azonnal mondom, hogy ez rossz módszer, ne csináld így):
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"]Itt egy Alpine alapú képet használunk, amely már telepítve van JDK-val a projektünk összeállításához. Az ADD parancs segítségével a jelenlegi src mappát hozzáadjuk a képhez, azt munkaként (WORKDIR) megjelöljük, és elindítjuk az összeállítást. Az EXPOSE 8080 parancs jelzi a dockernak, hogy az alkalmazás a konténerben az 8080-as portot fogja használni (ez nem teszi az alkalmazást elérhetővé kívülről, de lehetővé teszi, hogy például egy másik konténerből hozzáférjünk az alkalmazáshoz ugyanabban a docker hálózatban).
A szolgáltatások képekbe csomagolásához a projektek gyökeréből kell végrehajtani a parancsokat:
docker image build . -t msvc-backend:1.0.0Ennek eredményeként 456 MB méretű képet kapunk (ebből a JDK alapképe 340 MB-ot foglalt el). És mindezek mellett a projektünkben alig néhány osztály található. A kép méretének csökkentéséhez:
- Használjunk többlépcsős buildet. Az első lépésben összeállítjuk a projektet, a második lépésben telepítjük a JRE-t, a harmadik lépésben pedig mindezt át másoljuk egy új, tiszta Alpine képbe. Így végül csak a szükséges összetevők kerülnek a végső képbe.
- Használjuk a Java modularizálását. A Java 9-től kezdve a jlink eszközzel csak a szükséges modulokból hozhatunk létre JRE-t.
A kíváncsiaknak itt van egy jó cikk a képméret-csökkentési megközelítésekről. .
A végső 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 anshelen@yandex.ru"
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"]Újra létrehozzuk a képet, és végül 6-szor csökkentettük a méretét, 77 MB-ra. Nem rossz. Ezután a kész képeket feltölthetjük a képtárba, hogy az Ön képei elérhetők legyenek letöltésre az internetről.
Szolgáltatások együttfutása Dockerben.
Először is, szolgáltatásainknak egy hálózatban kell lenniük. A Dockerben többféle hálózattípus létezik, mi a legprimitívebb, a bridge-típust használjuk, amely lehetővé teszi a egy hoston futó konténerek hálózatba kötését. A következő parancs kiadásával létrehozzuk a hálózatot:
docker network create msvc-networkEzután elindítjuk a backend konténert 'backend' névvel a microservices-backend:1.0.0 képből:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0Érdemes megemlíteni, hogy a bridge-hálózat alapból service discovery-t biztosít a konténerek számára a neveik alapján. Tehát a backend szolgáltatás a Docker hálózaton belül a következő címen lesz elérhető: .
Gateway indítása:
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.0Ebben a csapatban megadjuk, hogy a gazdagépünk 80-as portját az 8080-as portjára irányítjuk a konténernek. Az env opciókat a környezeti változók beállítására használjuk, amelyeket a Spring automatikusan kiolvas és felülírja az application.properties fájlban található tulajdonságokat.
A futtatás után meghívjuk és meggyőződünk róla, hogy minden úgy működik, mint az előző alkalommal.
Zárás
Ennek eredményeként két egyszerű mikroszolgáltatást hoztunk létre, becsomagoltuk őket Docker konténerekbe és közösen futtattuk őket egy gépen. Azonban a rendszernek van néhány hátránya:
- Gyenge hibatűrés — minden az egy szerveren fut.
- Gyenge skálázhatóság — terhelés növekedésekor jó lenne automatikusan további szolgáltatásmintákat telepíteni és terhelést egyensúlyozni közöttük.
- Indítási nehézség — legalább 3 parancsot kellett bevinni, bizonyos paraméterekkel (ez csak 2 szolgáltatásra vonatkozik).
A fent említett problémák megoldására számos lehetőség létezik, mint például a Docker Swarm, Nomad, Kubernetes vagy OpenShift. Ha az egész rendszert Java nyelven írják, akkor érdemes megfontolni a Spring Cloud-ot ().
A elmondom, hogyan állítottam be a Kubernetest és telepítettem a projektet a Google Kubernetes Engine-re.
Forrás: habr.com
