Spring Native di Google Cloud

1. Ringkasan

Dalam codelab ini, kita akan mempelajari project Spring Native, mem-build aplikasi yang menggunakannya, dan men-deploy-nya di Google Cloud.

Kami akan membahas komponennya, histori project terbaru, beberapa kasus penggunaan, dan tentu saja langkah-langkah yang diperlukan untuk menggunakannya dalam project Anda.

Project Spring Native saat ini berada dalam fase eksperimental. Jadi, sebaiknya mulai dengan konfigurasi tertentu. Namun, seperti yang diumumkan di SpringOne 2021, Spring Native akan diintegrasikan ke dalam Spring Framework 6.0 dan Spring Boot 3.0 dengan dukungan kelas satu, jadi ini adalah waktu yang tepat untuk melihat lebih dekat proyek a beberapa bulan sebelum dirilis.

Meskipun kompilasi tepat waktu telah dioptimalkan dengan sangat baik untuk hal-hal seperti proses yang berjalan lama, ada kasus penggunaan tertentu yang mana aplikasi yang dikompilasi sebelumnya berperforma lebih baik, yang akan kita bahas selama codelab.

Anda akan mempelajari cara

  • Menggunakan Cloud Shell
  • Mengaktifkan Cloud Run API
  • Membuat dan men-deploy aplikasi Spring Native
  • Men-deploy aplikasi seperti itu ke Cloud Run

Yang Anda butuhkan

Survey

Bagaimana Anda akan menggunakan tutorial ini?

Hanya membacanya Membacanya dan menyelesaikan latihan

Sejauh mana pengalaman Anda menggunakan Java?

Pemula Menengah Mahir

Bagaimana penilaian Anda terhadap pengalaman menggunakan layanan Google Cloud?

Pemula Menengah Mahir

2. Latar belakang

Project Spring Native memanfaatkan beberapa teknologi untuk memberikan performa aplikasi native kepada developer.

Untuk memahami Spring Native sepenuhnya, sebaiknya pahami beberapa teknologi komponen ini, apa yang mereka aktifkan untuk kita, dan bagaimana cara keduanya bekerja bersama di sini.

Kompilasi AOT

Saat developer menjalankan javac secara normal pada waktu kompilasi, kode sumber .java kita dikompilasi ke dalam file .class yang ditulis dalam bytecode. Byte ini hanya ditujukan untuk dipahami oleh Mesin Virtual Java, sehingga JVM harus menafsirkan kode ini di mesin lain agar kami dapat menjalankan kode.

Proses inilah yang memberi kita portabilitas tanda tangan Java - yang memungkinkan kita "menulis sekali dan menjalankannya di mana saja", tetapi mahal jika dibandingkan dengan menjalankan kode native.

Untungnya, sebagian besar implementasi JVM menggunakan kompilasi tepat waktu untuk mengurangi biaya penafsiran ini. Hal ini dilakukan dengan menghitung pemanggilan fungsi, dan jika dipanggil cukup sering untuk lulus nilai minimum ( 10.000 secara default), fungsi tersebut akan dikompilasi ke kode native pada waktu proses untuk mencegah interpretasi yang lebih mahal.

Kompilasi ahead-of-time mengambil pendekatan yang berlawanan, dengan mengompilasi semua kode yang dapat dijangkau ke dalam native executable pada waktu kompilasi. Hal ini mengorbankan portabilitas untuk efisiensi memori dan peningkatan performa lainnya selama runtime.

5042e8e62a05a27.png

Hal ini tentu saja merupakan kompromi dan tidak selalu sepadan. Namun, kompilasi AOT dapat bersinar dalam kasus penggunaan tertentu seperti:

  • Aplikasi dengan masa aktif singkat yang memerlukan waktu startup
  • Lingkungan dengan memori yang sangat terbatas tempat JIT mungkin terlalu mahal

Sebagai fakta menyenangkan, kompilasi AOT diperkenalkan sebagai fitur eksperimental di JDK 9, meskipun penerapan ini mahal untuk dipelihara, dan tidak pernah cukup menarik, sehingga secara diam-diam dihapus di Java 17 untuk developer, hanya dengan menggunakan GraalVM.

GraalVM

GraalVM adalah distribusi JDK open source yang sangat dioptimalkan yang menawarkan waktu startup yang sangat cepat, kompilasi gambar native AOT, dan kemampuan poliglot yang memungkinkan developer untuk menggabungkan beberapa bahasa menjadi satu aplikasi.

GraalVM berada dalam pengembangan aktif, mendapatkan kemampuan baru dan meningkatkan yang sudah ada sepanjang waktu, jadi saya mendorong developer untuk tetap mengikuti perkembangan.

Beberapa pencapaian terbaru adalah:

  • Output build gambar native baru yang mudah digunakan ( 2021-01-18)
  • Dukungan Java 17 ( 2022-01-18)
  • Mengaktifkan kompilasi multi-tingkat secara default untuk meningkatkan waktu kompilasi poliglot ( 20-04-2021)

Native Musim Semi

Singkatnya - Spring Native memungkinkan penggunaan compiler gambar native GraalVM untuk mengubah aplikasi Spring menjadi file yang dapat dieksekusi native.

Proses ini melibatkan melakukan analisis statis aplikasi Anda pada waktu kompilasi untuk menemukan semua metode dalam aplikasi Anda yang dapat dijangkau dari titik entri.

Ini pada dasarnya membuat konsep "tertutup" dari aplikasi Anda, di mana semua kode diasumsikan diketahui pada waktu kompilasi, dan tidak ada kode baru yang diizinkan untuk dimuat pada runtime.

Penting untuk diperhatikan bahwa pembuatan gambar native adalah proses intensif memori yang membutuhkan waktu lebih lama dari mengompilasi aplikasi biasa, dan menerapkan batasan pada aspek Java tertentu.

Dalam beberapa kasus, perubahan kode tidak diperlukan agar aplikasi dapat bekerja dengan Spring Native. Namun, beberapa situasi memerlukan konfigurasi native tertentu agar berfungsi dengan baik. Dalam situasi tersebut, Spring Native sering kali menyediakan Petunjuk Native untuk menyederhanakan proses ini.

3 Penyiapan/Prakerja

Sebelum mulai menerapkan Spring Native, kita perlu membuat dan men-deploy aplikasi untuk menetapkan dasar pengukuran performa yang dapat kita bandingkan dengan versi native nanti.

1. Membuat project

Kita akan memulai dengan mendapatkan aplikasi dari start.spring.io:

curl https://start.spring.io/starter.zip -d dependencies=web \
           -d javaVersion=11 \
           -d bootVersion=2.6.4 -o io-native-starter.zip

Aplikasi awal ini menggunakan Spring Boot 2.6.4, yang merupakan versi terbaru yang didukung oleh project native musim semi pada saat penulisan.

Perhatikan bahwa sejak rilis GraalVM 21.0.3, Anda juga dapat menggunakan Java 17 untuk sampel ini. Kami akan tetap menggunakan Java 11 untuk tutorial ini guna meminimalkan konfigurasi yang terkait.

Setelah membuat zip di command line, kita dapat membuat subdirektori untuk project dan mengekstrak folder di sana:

mkdir spring-native

cd spring-native

unzip ../io-native-starter.zip

2. Perubahan kode

Setelah project terbuka, kita akan dengan cepat menambahkan tanda kehidupan dan menampilkan performa Native Spring setelah menjalankannya.

Edit DemoApplication.java agar cocok dengan ini:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.time.Instant;

@RestController
@SpringBootApplication
public class DemoApplication {
    private static Instant startTime;
    private static Instant readyTime;

    public static void main(String[] args) {
        startTime = Instant.now();
                SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/")
    public String index() {
        return "Time between start and ApplicationReadyEvent: "
                + Duration.between(startTime, readyTime).toMillis()
                + "ms";
    }

    @EventListener(ApplicationReadyEvent.class)
    public void ready() {
                readyTime = Instant.now();
    }
}

Pada tahap ini, aplikasi dasar pengukuran kita siap digunakan, jadi silakan buat gambar dan jalankan secara lokal untuk mendapatkan gambaran tentang waktu startup sebelum kita mengonversinya menjadi aplikasi native.

Untuk membuat gambar:

mvn spring-boot:build-image

Anda juga dapat menggunakan docker images demo untuk mengetahui ukuran gambar dasar pengukuran: 6ecb403e9af1475e.png

Untuk menjalankan aplikasi:

docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT

3. Deploy aplikasi dasar pengukuran

Setelah memiliki aplikasi, kita akan men-deploy dan mencatat waktunya, yang akan kita bandingkan dengan waktu startup aplikasi native kita nanti.

Tergantung pada jenis aplikasi yang Anda buat, ada beberapa hosting yang berbeda.

Namun, karena contoh kami adalah aplikasi web yang sangat sederhana dan mudah, kita dapat membuat semuanya tetap sederhana dan mengandalkan Cloud Run.

Jika mengikuti di komputer Anda sendiri, pastikan Anda telah menginstal dan mengupdate alat gcloud CLI.

Jika Anda menggunakan Cloud Shell yang semuanya akan ditangani dan Anda dapat menjalankan yang berikut ini di direktori sumber:

gcloud run deploy

4. Konfigurasi Aplikasi

1. Mengonfigurasi repositori Maven kami

Karena project ini masih dalam tahap eksperimental, kita harus mengonfigurasi aplikasi agar dapat menemukan artefak eksperimental yang tidak tersedia di repositori pusat maven.

Ini akan melibatkan penambahan elemen berikut ke pom.xml kami, yang dapat Anda lakukan di editor pilihan Anda.

Tambahkan bagian repositori dan pluginRepositoryes berikut ke pom kami:

<repositories>
    <repository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>spring-release</id>
        <name>Spring release</name>
        <url>https://repo.spring.io/release</url>
    </pluginRepository>
</pluginRepositories>

2. Menambahkan dependensi

Selanjutnya, tambahkan dependensi native spring yang diperlukan untuk menjalankan aplikasi Spring sebagai image native. Catatan: langkah ini tidak diperlukan jika Anda menggunakan Gradle

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-native</artifactId>
        <version>0.11.2</version>
    </dependency>
</dependencies>

3. Menambahkan/mengaktifkan plugin kita

Sekarang tambahkan plugin AOT untuk meningkatkan kompatibilitas dan jejak gambar native ( Baca selengkapnya):

<plugins>
    <!-- ... -->
    <plugin>
        <groupId>org.springframework.experimental</groupId>
        <artifactId>spring-aot-maven-plugin</artifactId>
        <version>0.11.2</version>
        <executions>
            <execution>
                <id>generate</id>
                <goals>
                    <goal>generate</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

Sekarang kita akan mengupdate spring-boot-maven-plugin untuk mengaktifkan dukungan image native dan menggunakan builder paketo untuk mem-build image native:

<plugins>
    <!-- ... -->
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <image>
                <builder>paketobuildpacks/builder:tiny</builder>
                <env>
                    <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
                </env>
            </image>
        </configuration>
    </plugin>
</plugins>

Perhatikan bahwa gambar builder kecil hanyalah salah satu dari beberapa opsi. Ini adalah pilihan yang baik untuk kasus penggunaan kita karena sangat sedikit library dan utilitas tambahan, yang membantu meminimalkan permukaan serangan.

Jika misalnya Anda mem-build aplikasi yang memerlukan akses ke beberapa library C umum, atau belum yakin dengan persyaratan aplikasi, full-builder mungkin lebih cocok.

5. Mem-build dan Menjalankan aplikasi native

Setelah semuanya siap, kita akan dapat mem-build image dan menjalankan aplikasi native yang telah dikompilasi.

Sebelum menjalankan build, berikut beberapa hal yang perlu diingat:

  • Proses ini akan memerlukan waktu lebih lama daripada build reguler (beberapa menit) d420322893640701.png
  • Proses build ini dapat menggunakan banyak memori (beberapa gigabyte) cda24e1eb11fdbea.png
  • Proses build ini memerlukan daemon Docker untuk dapat dijangkau
  • Saat dalam contoh ini, kami menjalani proses secara manual, Anda juga dapat mengonfigurasi fase build untuk otomatis memicu profil build native.

Untuk membuat gambar:

mvn spring-boot:build-image

Setelah selesai dibuat, kita siap untuk melihat cara kerja aplikasi native.

Untuk menjalankan aplikasi:

docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT

Pada titik ini, kita berada di posisi yang tepat untuk melihat kedua sisi persamaan aplikasi native.

Kami telah memberikan sedikit waktu dan penggunaan memori tambahan pada waktu kompilasi, tetapi sebagai gantinya kami mendapatkan aplikasi yang dapat dimulai jauh lebih cepat, dan menghabiskan memori yang jauh lebih sedikit (bergantung pada beban kerja).

Jika menjalankan docker images demo untuk membandingkan ukuran gambar native dengan ukuran asli, kita dapat melihat pengurangan drastis:

e667f65a011c1328.png

Kita juga harus memperhatikan bahwa dalam kasus penggunaan yang lebih kompleks, ada modifikasi tambahan yang diperlukan untuk memberi tahu compiler AOT tentang apa yang akan dilakukan aplikasi Anda saat runtime. Karena alasan tersebut, beban kerja tertentu yang dapat diprediksi (seperti tugas batch) mungkin sangat cocok untuk hal ini, sementara tugas lainnya mungkin merupakan peningkatan yang lebih besar.

6. Men-deploy aplikasi native kami

Untuk men-deploy aplikasi ke Cloud Run, kita harus memasukkan gambar native ke pengelola paket seperti Artifact Registry.

1. Menyiapkan repositori Docker kami

Kita dapat memulai proses ini dengan membuat repositori:

gcloud artifacts repositories create native-image-docker-repo --repository-format=docker \
--location=us-central1 --description="Repository for our native images"

Selanjutnya, kita akan memastikan bahwa kita telah diautentikasi untuk mengirim ke registry baru kita.

Gcloud CLI dapat sedikit menyederhanakan proses tersebut:

gcloud auth configure-docker us-central1-docker.pkg.dev

2. Mengirim gambar ke Artifact Registry

Selanjutnya, kita akan memberi tag pada gambar:

export PROJECT_ID=$(gcloud config list --format 'value(core.project)')

docker tag  demo:0.0.1-SNAPSHOT \
us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2

Lalu, kita dapat menggunakan docker push untuk mengirimkannya ke Artifact Registry:

docker push us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2

3 Men-deploy ke Cloud Run

Sekarang kita siap untuk men-deploy image yang telah kita simpan di Artifact Registry ke Cloud Run:

gcloud run deploy --image us-central1-docker.pkg.dev/$PROJECT_ID/native-image-docker-repo/quickstart-image:tag2

Karena kita telah membangun dan men-deploy aplikasi sebagai gambar native, kita dapat merasa yakin bahwa aplikasi kita sudah memanfaatkan biaya infrastruktur dengan sangat baik saat dijalankan.

Jangan ragu untuk membandingkan waktu startup aplikasi dasar pengukuran kami dengan aplikasi native baru ini untuk Anda sendiri.

6dde63d35959b1bb.png

7. Ringkasan/Pembersihan

Selamat atas build dan deployment aplikasi Spring Native di Google Cloud.

Semoga tutorial ini mendorong Anda untuk lebih memahami project Spring Native dan mengingatnya, seandainya project ini memenuhi kebutuhan Anda di masa mendatang.

Opsional: Membersihkan dan/atau menonaktifkan layanan

Baik Anda membuat project Google Cloud untuk codelab ini atau menggunakan kembali project yang sudah ada, berhati-hatilah agar tidak dikenai biaya yang tidak perlu dari resource yang kami gunakan.

Anda dapat menghapus atau menonaktifkan layanan Cloud Run yang kami buat, menghapus image yang kami hosting, atau menonaktifkan seluruh project.

8 Referensi lainnya

Meskipun project Spring Native saat ini merupakan project baru dan eksperimental, sudah ada banyak referensi bagus untuk membantu pengguna awal memecahkan masalah dan terlibat:

Referensi lainnya

Berikut referensi online yang mungkin relevan untuk tutorial ini:

Lisensi

Karya ini dilisensikan berdasarkan Lisensi Umum Creative Commons Attribution 2.0.