ProHoster > Blog > yönetim > Mikro hizmetleri dağıtmayı öğrenme. Bölüm 1. Spring Boot ve Docker
Mikro hizmetleri dağıtmayı öğrenme. Bölüm 1. Spring Boot ve Docker
Merhaba Habr.
Bu yazıda mikro hizmetleri denemek için bir öğrenme ortamı oluşturma deneyimimden bahsetmek istiyorum. Her yeni aracı öğrenirken, onu sadece yerel makinemde değil, daha gerçekçi koşullarda da denemek istedim. Bu nedenle, daha sonra her türlü ilginç teknolojiyle "asılabilecek" basitleştirilmiş bir mikro hizmet uygulaması oluşturmaya karar verdim. Projenin temel gereksinimi, gerçek sisteme maksimum işlevsel yakınlıktır.
Başlangıçta projenin oluşturulmasını birkaç adıma ayırdım:
'Arka uç' ve 'ağ geçidi' olmak üzere iki hizmet oluşturun, bunları liman işçisi görüntülerine paketleyin ve birlikte çalışacak şekilde yapılandırın
Anahtar Kelimeler: Java 11, Spring Boot, Docker, görüntü optimizasyonu
Anahtar Kelimeler: Kubernetes, GKE, kaynak yönetimi, otomatik ölçeklendirme, sırlar
Daha verimli küme yönetimi için Helm 3'ü kullanarak bir grafik oluşturun
Anahtar Kelimeler: Dümen 3, grafik dağıtımı
Kümeye otomatik olarak kod teslim etmek için Jenkins ve işlem hattını ayarlama
Anahtar Kelimeler: Jenkins yapılandırması, eklentiler, ayrı yapılandırma deposu
Her adıma ayrı bir makale ayırmayı planlıyorum.
Bu yazı serisinin odak noktası mikro hizmetlerin nasıl yazılacağı değil, bunların tek bir sistemde nasıl çalıştırılacağıdır. Bunların hepsi genellikle geliştiricinin sorumluluğu dışında olsa da, yine de en az %20 oranında bunlara aşina olmanın faydalı olduğunu düşünüyorum (ki bunun sonucun %80'ini oluşturduğu biliniyor). Güvenlik gibi kesinlikle önemli olan bazı konular bu projenin dışında bırakılacaktır çünkü yazar bu konuda çok az şey anlıyor; sistem yalnızca kişisel kullanım için yaratılıyor. Her türlü görüşe ve yapıcı eleştiriye açığım.
Mikro hizmetler oluşturma
Hizmetler Java 11'de Spring Boot kullanılarak yazılmıştır. Servisler arası iletişim REST kullanılarak düzenlenir. Proje minimum sayıda test içerecektir (böylece daha sonra Jenkins'te test edilecek bir şeyler olacaktır). Hizmetlerin kaynak kodu GitHub'da mevcuttur: arka uç и geçit.
Her hizmetin durumunu kontrol edebilmek için bağımlılıklarına bir Yaylı Aktüatör eklendi. Bir uç nokta/aktüatör/sağlık oluşturacak ve hizmet trafiği kabul etmeye hazırsa 200, sorun olması durumunda 504 durumunu döndürecektir. Bu durumda, hizmetler çok basit olduğundan ve bazı mücbir sebepler altında kısmen çalışır durumda kalmaktansa tamamen kullanılamaz hale gelme olasılıkları daha yüksek olduğundan, bu oldukça hayali bir kontroldür. Ancak gerçek sistemlerde Actuator, kullanıcılar sorunu çözmeye başlamadan önce sorunu teşhis etmeye yardımcı olabilir. Örneğin, veritabanına erişimde sorunlar ortaya çıkarsa, hizmetin bozuk bir örneğiyle isteklerin işlenmesini durdurarak buna otomatik olarak yanıt verebileceğiz.
Arka uç hizmeti
Arka uç hizmeti yalnızca kabul edilen istekleri sayar ve sayısını döndürür.
Denetleyici kodu:
@RestController
public class RequestsCounterController {
private final AtomicLong counter = new AtomicLong();
@GetMapping("/requests")
public Long getRequestsCount() {
return counter.incrementAndGet();
}
}
Denetleyici 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"));
}
}
Ağ geçidi hizmeti
Ağ geçidi, isteği aşağıdaki bilgilerle destekleyerek arka uç hizmetine iletecektir:
ağ geçidi kimliği. Ağ geçidinin bir örneğinin sunucu yanıtıyla diğerinden ayırt edilebilmesi için gereklidir
Çok önemli bir şifre rolünü oynayacak belirli bir “sır” (önemli bir çerezin şifrelenmesi için anahtar numarası)
$ curl http://localhost:8080/
Number of requests 1 (gateway 38560358, secret "default-secret")
Her şey çalışıyor. Dikkatli okuyucu, ağ geçidini atlayarak arka uca doğrudan erişmemizi hiçbir şeyin engellemediğini fark edecektir (http://localhost:8081/requests). Bunu düzeltmek için hizmetlerin tek bir ağda birleştirilmesi ve yalnızca ağ geçidinin dışarıda "dışarı çıkması" gerekir.
Ayrıca her iki hizmet de aynı dosya sistemini paylaşır, iş parçacıkları oluşturur ve bir noktada birbirlerine müdahale etmeye başlayabilir. Mikro hizmetlerimizi izole etmek güzel olurdu. Bu, uygulamaları farklı makinelere dağıtarak (çok para gerektiren, zor), sanal makineler kullanarak (kaynak yoğun, uzun başlangıç) veya konteynerizasyon kullanarak başarılabilir. Beklendiği gibi üçüncü seçeneği seçiyoruz ve liman işçisi konteynerleştirme için bir araç olarak.
liman işçisi
Kısacası Docker, uygulama başına bir tane olmak üzere izole kaplar oluşturur. Docker'ı kullanmak için, uygulamayı oluşturma ve çalıştırma talimatlarını içeren bir Docker dosyası yazmanız gerekir. Daha sonra görüntüyü oluşturabilir, görüntü kayıt defterine yükleyebilirsiniz (No. Dockerhub) ve mikro hizmetinizi herhangi bir docker ortamında tek komutla dağıtın.
Dockerfile
Bir görselin en önemli özelliklerinden biri boyutudur. Kompakt bir görüntü uzak bir depodan daha hızlı indirilecek, daha az yer kaplayacak ve hizmetiniz daha hızlı başlayacaktır. Herhangi bir görüntü, temel bir görüntü temel alınarak oluşturulur ve en minimalist seçeneğin seçilmesi önerilir. İyi bir seçenek, minimum pakete sahip tam teşekküllü bir Linux dağıtımı olan Alpine'dir.
Öncelikle Dockerfile’ı “başa baş” yazmayı deneyelim (Bunun kötü bir yol olduğunu hemen söyleyeceğim, yapmayın):
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 projemizi oluşturmak için JDK'nın zaten kurulu olduğu Alpine tabanlı bir temel imaj kullanıyoruz. ADD komutunu kullanarak mevcut src dizinini image’a ekliyoruz, çalışıyor (WORKDIR) olarak işaretliyoruz ve build’i başlatıyoruz. EXPOSE 8080 komutu docker'a, konteynerdeki uygulamanın 8080 numaralı bağlantı noktasını kullanacağını bildirir (bu, uygulamayı dışarıdan erişilebilir hale getirmeyecek, ancak uygulamaya örneğin aynı docker ağındaki başka bir konteynerden erişilmesine izin verecektir) ).
Hizmetleri görüntüler halinde paketlemek için komutları her projenin kökünden çalıştırmanız gerekir:
docker image build . -t msvc-backend:1.0.0
Sonuç olarak, 456 MB boyutunda bir görüntü elde ediyoruz (bunun temel JDK 340 görüntüsü MB aldı). Ve hepsi projemizdeki sınıfların tek parmakla sayılabilmesine rağmen. Resmimizin boyutunu küçültmek için:
Çok adımlı montaj kullanıyoruz. İlk adımda projeyi birleştireceğiz, ikinci adımda JRE'yi kuracağız ve üçüncü adımda tüm bunları yeni, temiz bir Alpine görüntüsüne kopyalayacağız. Toplamda, son görüntü yalnızca gerekli bileşenleri içerecektir.
Java modülerleştirmesini kullanalım. Java 9'dan itibaren jlink aracını kullanarak yalnızca ihtiyacınız olan modüllerden bir JRE oluşturabilirsiniz.
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"]
Görüntüyü yeniden oluşturduk ve sonunda 6 kat daha ince hale gelerek 77 MB'a ulaştı. Fena değil. Daha sonra, bitmiş görüntüler, görüntü kayıt defterine yüklenebilir, böylece görüntüleriniz İnternet'ten indirilebilir hale gelir.
Docker'da hizmetleri birlikte çalıştırma
Başlangıç olarak hizmetlerimizin aynı ağ üzerinde olması gerekir. Docker'da çeşitli ağ türleri vardır ve biz bunların en ilkelini kullanırız; bu, aynı ana bilgisayarda çalışan kapsayıcıları ağ oluşturmanıza olanak tanır. Aşağıdaki komutla bir ağ oluşturalım:
docker network create msvc-network
Ardından, microservices-backend:1.0.0 görseliyle 'backend' adında bir arka uç kapsayıcısı başlatalım:
docker run -dit --name backend --network msvc-net microservices-backend:1.0.0
Köprü ağının, konteynerler için adlarına göre alışılmışın dışında hizmet keşfi sağladığını belirtmekte fayda var. Yani, arka uç hizmeti şu adreste Docker ağı içinde mevcut olacaktır: http://backend:8080.
Bu komutta hostumuzun 80 numaralı portunu Container’ın 8080 numaralı portuna ilettiğimizi belirtiyoruz. Spring tarafından otomatik olarak okunacak ortam değişkenlerini ayarlamak ve application.properties dosyasındaki özellikleri geçersiz kılmak için env seçeneklerini kullanırız.
Başlattıktan sonra arayın http://localhost/ ve önceki durumda olduğu gibi her şeyin çalıştığından emin olun.
Sonuç
Sonuç olarak iki basit mikro hizmet oluşturduk, bunları docker konteynerlerinde paketledik ve aynı makinede birlikte başlattık. Ancak ortaya çıkan sistemin bir takım dezavantajları vardır:
Zayıf hata toleransı - bizim için her şey tek bir sunucuda çalışıyor
Zayıf ölçeklenebilirlik - yük arttıkça, ek hizmet örneklerini otomatik olarak dağıtmak ve aralarındaki yükü dengelemek güzel olurdu
Başlatma karmaşıklığı - belirli parametrelerle en az 3 komut girmemiz gerekiyordu (bu yalnızca 2 hizmet içindir)
Yukarıdaki sorunları çözmek için Docker Swarm, Nomad, Kubernetes veya OpenShift gibi bir takım çözümler bulunmaktadır. Sistemin tamamı Java ile yazılmışsa Spring Cloud'a bakabilirsiniz (iyi makale).
В sonraki bölüm Sizlere Kubernetes'i nasıl kurduğumu ve projeyi Google Kubernetes Engine'e nasıl dağıttığımı anlatacağım.