Ketahui cara menggunakan perkhidmatan mikro. Bahagian 1. Spring Boot dan Docker

Ketahui cara menggunakan perkhidmatan mikro. Bahagian 1. Spring Boot dan Docker

Hai Habr.

Dalam artikel ini, saya ingin bercakap tentang pengalaman saya dalam mewujudkan persekitaran pembelajaran untuk bereksperimen dengan perkhidmatan mikro. Apabila saya mempelajari setiap alat baharu, saya sentiasa mahu mencubanya bukan sahaja pada mesin tempatan, tetapi juga dalam keadaan yang lebih realistik. Oleh itu, saya memutuskan untuk mencipta aplikasi perkhidmatan mikro yang dipermudahkan, yang kemudiannya boleh "dilindungi" dengan pelbagai jenis teknologi yang menarik. Keperluan utama untuk projek adalah kedekatan fungsi maksimumnya dengan sistem sebenar.

Pada mulanya, saya memecahkan penciptaan projek kepada beberapa langkah:

  1. Cipta dua perkhidmatan - 'backend' (backend) dan 'gateway' (gateway), bungkuskannya ke dalam imej docker dan sediakannya untuk berfungsi bersama

    Kata kunci: Java 11, Spring Boot, Docker, pengoptimuman imej

  2. Pembangunan konfigurasi Kubernetes dan penggunaan sistem dalam Google Kubernetes Engine

    Kata kunci: Kubernetes, GKE, pengurusan sumber, autoscaling, rahsia

  3. Mencipta carta dengan Helm 3 untuk pengurusan kluster yang lebih baik

    Teg: Helm 3, penggunaan carta

  4. Menyediakan Jenkins dan saluran paip untuk penghantaran kod automatik ke kluster

    Kata kunci: Konfigurasi Jenkins, pemalam, repositori konfigurasi berasingan

Saya merancang untuk menumpukan artikel berasingan untuk setiap langkah.

Tumpuan siri artikel ini bukanlah cara menulis perkhidmatan mikro, tetapi cara menjadikannya berfungsi dalam satu sistem. Walaupun semua perkara ini biasanya di luar tanggungjawab pembangun, saya rasa masih berguna untuk membiasakan diri dengan mereka sekurang-kurangnya 20% (yang, seperti yang anda tahu, memberikan 80% daripada hasilnya). Beberapa topik penting tanpa syarat, seperti keselamatan, akan ditinggalkan daripada projek ini, kerana penulis memahami sedikit tentang sistem ini dicipta semata-mata untuk kegunaan peribadi. Saya mengalu-alukan sebarang pendapat dan kritikan yang membina.

Mencipta perkhidmatan mikro

Perkhidmatan ini ditulis dalam Java 11 menggunakan Spring Boot. Interaksi antara perkhidmatan dianjurkan menggunakan REST. Projek ini akan memasukkan bilangan ujian minimum (supaya nanti ada sesuatu untuk diuji dalam Jenkins). Kod sumber untuk perkhidmatan tersedia di GitHub: hujung belakang ΠΈ Gerbang.

Untuk dapat menyemak status setiap perkhidmatan, Spring Actuator telah ditambahkan pada kebergantungan mereka. Ia akan mencipta titik akhir /actuator/kesihatan dan mengembalikan status 200 jika perkhidmatan sedia menerima trafik, atau 504 jika terdapat masalah. Dalam kes ini, ini adalah pemeriksaan yang agak rekaan, memandangkan perkhidmatannya sangat mudah, dan sekiranya berlaku force majeure, mereka lebih berkemungkinan menjadi tidak tersedia sepenuhnya daripada kekal beroperasi separa. Tetapi dalam sistem sebenar, Actuator boleh membantu mendiagnosis masalah sebelum pengguna mula bertarung mengenainya. Contohnya, jika terdapat masalah mengakses pangkalan data, kami boleh bertindak balas secara automatik dengan menghentikan permintaan pemprosesan dengan contoh perkhidmatan yang rosak.

Perkhidmatan hujung belakang

Perkhidmatan bahagian belakang hanya akan mengira dan mengembalikan bilangan permintaan yang diterima.

Kod pengawal:

@RestController
public class RequestsCounterController {

    private final AtomicLong counter = new AtomicLong();

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

Ujian pengawal:

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

Gerbang Perkhidmatan

Gerbang akan memajukan permintaan ke perkhidmatan bahagian belakang, menambahnya dengan maklumat berikut:

  • id pintu masuk. Ia diperlukan supaya mungkin untuk membezakan satu contoh get laluan daripada yang lain dengan respons pelayan
  • Beberapa "rahsia" yang akan memainkan peranan kata laluan yang sangat penting (bilangan kunci penyulitan kuki penting)

Konfigurasi dalam application.properties:

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

Penyesuai bahagian belakang:

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

Pengawal:

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

Pelancaran:

Kami memulakan bahagian belakang:

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

Memulakan pintu masuk:

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

Kami menyemak:

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

Semuanya berfungsi. Pembaca yang penuh perhatian akan menyedari bahawa tiada apa yang menghalang kami daripada mengakses bahagian belakang secara langsung, memintas pintu masuk (http://localhost:8081/requests). Untuk membetulkannya, perkhidmatan mesti digabungkan menjadi satu rangkaian, dan hanya pintu masuk harus "terlekat" di luar.
Juga, kedua-dua perkhidmatan berkongsi satu sistem fail, menghasilkan aliran dan pada satu ketika boleh mula mengganggu antara satu sama lain. Adalah baik untuk mengasingkan perkhidmatan mikro kami. Ini boleh dicapai dengan mengedarkan aplikasi pada mesin yang berbeza (banyak wang, sukar), menggunakan mesin maya (intensif sumber, permulaan yang lama), atau menggunakan kontena. Seperti yang dijangka, kami memilih pilihan ketiga dan buruh pelabuhan sebagai alat untuk kontena.

buruh pelabuhan

Pendek kata, docker mencipta bekas terpencil, satu bagi setiap aplikasi. Untuk menggunakan docker, anda perlu menulis Dockerfile - arahan untuk membina dan menjalankan aplikasi. Seterusnya, anda boleh membina imej, muat naik ke pendaftaran imej (No. Dockerhub) dan gunakan perkhidmatan mikro anda dalam mana-mana persekitaran dok dalam satu arahan.

Dockerfile

Salah satu ciri yang paling penting bagi imej ialah saiznya. Imej padat akan dimuat turun dengan lebih pantas daripada repositori jauh, menggunakan lebih sedikit ruang dan perkhidmatan anda akan bermula dengan lebih pantas. Mana-mana imej dibina berdasarkan imej asas, dan disyorkan untuk memilih pilihan yang paling minimalis. Pilihan yang baik ialah Alpine, pengedaran Linux lengkap dengan pakej minimum.

Mula-mula, mari cuba menulis Dockerfile "di dahi" (saya akan katakan dengan segera bahawa ini adalah cara yang tidak baik, jangan lakukannya):

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

Di sini kami menggunakan imej asas berasaskan Alpine dengan JDK telah dipasang untuk membina projek kami. Dengan arahan ADD, kami menambah direktori src semasa pada imej, tandakannya sebagai berfungsi (WORKDIR) dan mulakan binaan. Perintah EXPOSE 8080 memberi isyarat kepada pekerja pelabuhan bahawa aplikasi dalam bekas akan menggunakan port 8080nya (ini tidak akan menjadikan aplikasi itu boleh diakses dari luar, tetapi akan membenarkan aplikasi itu diakses, contohnya, dari bekas lain pada rangkaian docker yang sama) .

Untuk membungkus perkhidmatan ke dalam imej, anda perlu menjalankan arahan dari akar setiap projek:

docker image build . -t msvc-backend:1.0.0

Hasilnya ialah imej 456 MB (yang mana imej JDK asas menduduki 340 MB). Dan semuanya walaupun pada hakikatnya kelas dalam projek kami boleh dikira dengan jari. Untuk mengurangkan saiz imej kami:

  • Kami menggunakan pemasangan pelbagai langkah. Pada langkah pertama kami akan membina projek, pada langkah kedua kami akan memasang JRE, dan pada langkah ketiga kami akan menyalin semuanya ke dalam imej Alpine bersih yang baru. Secara keseluruhan, hanya komponen yang diperlukan akan berada dalam imej akhir.
  • Mari kita gunakan modularisasi java. Bermula dengan Java 9, anda boleh menggunakan alat jlink untuk mencipta JRE daripada hanya modul yang anda perlukan

Bagi yang ingin tahu, berikut adalah artikel yang bagus tentang pendekatan pengurangan imej. https://habr.com/ru/company/ruvds/blog/485650/.

Fail Docker Akhir:

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

Kami mencipta semula imej, dan akibatnya, ia kehilangan 6 kali beratnya, berjumlah 77 MB. Boleh tahan. Selepas itu, imej siap boleh dimuat naik ke pendaftaran imej supaya imej anda tersedia untuk dimuat turun dari Internet.

Perkhidmatan berjalan bersama di Docker

Sebagai permulaan, perkhidmatan kami mestilah berada pada rangkaian yang sama. Terdapat beberapa jenis rangkaian dalam docker, dan kami menggunakan yang paling primitif daripadanya - bridge, yang membolehkan anda merangkaian bekas yang berjalan pada hos yang sama. Buat rangkaian dengan arahan berikut:

docker network create msvc-network

Seterusnya, mulakan bekas bahagian belakang bernama 'backend' dengan imej microservices-backend:1.0.0:

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

Perlu diingat bahawa rangkaian jambatan menyediakan penemuan perkhidmatan luar kotak untuk kontena dengan nama mereka. Iaitu, perkhidmatan backend akan tersedia di dalam rangkaian docker di http://backend:8080.

Memulakan pintu masuk:

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

Dalam arahan ini, kami menunjukkan bahawa kami memajukan port 80 hos kami ke port 8080 kontena. Kami menggunakan pilihan env untuk menetapkan pembolehubah persekitaran yang akan dibaca secara automatik oleh spring dan mengatasi sifat daripada application.properties.

Selepas bermula, kami menghubungi http://localhost/ dan pastikan semuanya berfungsi, seperti dalam kes sebelumnya.

Kesimpulan

Hasilnya, kami mencipta dua perkhidmatan mikro ringkas, membungkusnya dalam bekas docker dan melancarkannya bersama-sama pada mesin yang sama. Sistem yang dihasilkan, bagaimanapun, mempunyai beberapa kelemahan:

  • Toleransi kesalahan yang lemah - semuanya berfungsi untuk kami pada satu pelayan
  • Kebolehskalaan yang lemah - apabila beban meningkat, adalah baik untuk menggunakan contoh perkhidmatan tambahan secara automatik dan mengimbangi beban antara mereka
  • Kerumitan pelancaran - kami perlu memasukkan sekurang-kurangnya 3 arahan, dan dengan parameter tertentu (ini hanya untuk 2 perkhidmatan)

Untuk menyelesaikan masalah di atas, terdapat beberapa penyelesaian seperti Docker Swarm, Nomad, Kubernetes atau OpenShift. Jika keseluruhan sistem ditulis dalam Java, anda boleh melihat ke arah Spring Cloud (artikel yang baik).

Π’ bahagian seterusnya Saya akan bercakap tentang cara saya menyediakan Kubernetes dan menggunakan projek ke Google Kubernetes Engine.

Sumber: www.habr.com

Tambah komen