1. Ringkasan
Dalam codelab ini, kita akan mempelajari project Spring Native, mem-build aplikasi yang menggunakannya, dan men-deploy-nya di Google Cloud.
Kita akan membahas komponennya, histori terbaru project, beberapa kasus penggunaan, dan tentu saja langkah-langkah yang diperlukan untuk menggunakannya dalam project Anda.
Project Spring Native saat ini sedang dalam fase eksperimental, sehingga memerlukan beberapa konfigurasi khusus untuk memulai. 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 project ini 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 saat 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 tersebut ke Cloud Run
Yang Anda butuhkan
- Project Google Cloud Platform dengan akun penagihan GCP yang aktif
- gcloud cli diinstal, atau akses ke Cloud Shell
- Keterampilan Java + XML dasar
- Pengetahuan perintah Linux umum yang berfungsi
Survei
Bagaimana Anda akan menggunakan tutorial ini?
Bagaimana Anda menilai pengalaman Anda dengan Java?
Bagaimana penilaian Anda terhadap pengalaman menggunakan layanan Google Cloud?
2. Latar belakang
Project Spring Native menggunakan beberapa teknologi untuk memberikan performa aplikasi native kepada developer.
Untuk memahami Spring Native sepenuhnya, sebaiknya pahami beberapa teknologi komponen ini, apa yang dapat diaktifkan untuk kita, dan cara kerjanya di sini.
Kompilasi AOT
Saat developer menjalankan javac secara normal pada waktu kompilasi, kode sumber .java kita dikompilasi menjadi file .class yang ditulis dalam bytecode. Bytecode ini hanya dimaksudkan untuk dipahami oleh Java Virtual Machine, sehingga JVM harus menafsirkan kode ini di mesin lain agar kita dapat menjalankan kode.
Proses ini yang memberi kita portabilitas tanda tangan Java - memungkinkan kita untuk "menulis sekali dan berjalan di mana saja", tetapi proses ini mahal jika dibandingkan dengan menjalankan kode native.
Untungnya, sebagian besar implementasi JVM menggunakan kompilasi tepat waktu untuk mengurangi biaya interpretasi ini. Hal ini dicapai dengan menghitung pemanggilan untuk suatu fungsi, dan jika fungsi tersebut cukup sering dipanggil untuk melewati 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 menggunakan pendekatan yang berlawanan, dengan mengompilasi semua kode yang dapat dijangkau menjadi file yang dapat dieksekusi native pada waktu kompilasi. Hal ini mengorbankan portabilitas untuk efisiensi memori dan peningkatan performa lainnya saat runtime.
Tentu saja ini adalah kompromi, dan tidak selalu sepadan. Namun, kompilasi AOT dapat bersinar dalam kasus penggunaan tertentu seperti:
- Aplikasi berumur pendek yang waktu startup-nya penting
- Lingkungan yang sangat dibatasi memori sehingga JIT mungkin terlalu mahal
Sebagai fakta menarik, kompilasi AOT diperkenalkan sebagai fitur eksperimental di JDK 9, meskipun implementasi ini mahal untuk dikelola, dan tidak pernah benar-benar populer, sehingga dihapus secara diam-diam di Java 17 untuk mendukung developer yang hanya menggunakan GraalVM.
GraalVM
GraalVM adalah distribusi JDK open source yang dioptimalkan dengan sangat baik yang memiliki waktu startup yang sangat cepat, kompilasi image native AOT, dan kemampuan polyglot yang memungkinkan developer menggabungkan beberapa bahasa ke dalam satu aplikasi.
GraalVM sedang dalam pengembangan aktif, mendapatkan kemampuan baru dan meningkatkan kemampuan yang sudah ada setiap saat, jadi saya mendorong developer untuk terus mengikuti perkembangannya.
Beberapa pencapaian terbaru adalah:
- Output build gambar native baru yang mudah digunakan ( 18-01-2021)
- Dukungan Java 17 ( 18-01-2022)
- Mengaktifkan kompilasi multi-tingkat secara default untuk meningkatkan waktu kompilasi polyglot ( 2021-04-20)
Spring Native
Sederhananya, Spring Native memungkinkan penggunaan compiler image native GraalVM untuk mengubah aplikasi Spring menjadi file yang dapat dieksekusi secara native.
Proses ini melibatkan analisis statis aplikasi Anda pada waktu kompilasi untuk menemukan semua metode dalam aplikasi yang dapat dijangkau dari titik entri.
Hal ini pada dasarnya menciptakan konsep "dunia tertutup" aplikasi Anda, dengan asumsi bahwa semua kode diketahui pada waktu kompilasi, dan tidak ada kode baru yang diizinkan untuk dimuat saat runtime.
Perlu diperhatikan bahwa pembuatan image native adalah proses yang intensif memori yang memerlukan waktu lebih lama daripada mengompilasi aplikasi reguler, dan memberlakukan batasan pada aspek tertentu dari Java.
Dalam beberapa kasus, perubahan kode tidak diperlukan agar aplikasi berfungsi dengan Spring Native. Namun, beberapa situasi memerlukan konfigurasi native tertentu agar dapat berfungsi dengan benar. Dalam situasi tersebut, Spring Native sering kali memberikan Petunjuk Native untuk menyederhanakan proses ini.
3. Penyiapan/Prakerja
Sebelum mulai menerapkan Spring Native, kita harus membuat dan men-deploy aplikasi untuk menetapkan dasar pengukuran performa yang dapat kita bandingkan dengan versi native nanti.
1. Membuat project
Kita akan mulai 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 project spring-native pada saat penulisan.
Perhatikan bahwa sejak rilis GraalVM 21.0.3, Anda juga dapat menggunakan Java 17 untuk contoh ini. Kita masih akan menggunakan Java 11 untuk tutorial ini guna meminimalkan konfigurasi yang terlibat.
Setelah memiliki file zip di command line, kita dapat membuat subdirektori untuk project dan mengekstrak folder di dalamnya:
mkdir spring-native cd spring-native unzip ../io-native-starter.zip
2. Perubahan kode
Setelah membuka project, kita akan segera menambahkan tanda aktivitas dan menampilkan performa Spring Native 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 sudah siap. Jadi, jangan ragu untuk mem-build image dan menjalankannya secara lokal untuk mendapatkan gambaran tentang waktu startup sebelum kita mengonversinya menjadi aplikasi native.
Untuk mem-build image:
mvn spring-boot:build-image
Anda juga dapat menggunakan docker images demo
untuk mendapatkan gambaran tentang ukuran gambar dasar pengukuran:
Untuk menjalankan aplikasi:
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
3. Men-deploy aplikasi dasar pengukuran
Setelah memiliki aplikasi, kita akan men-deploy-nya dan mencatat waktu, yang akan kita bandingkan dengan waktu startup aplikasi native nanti.
Bergantung pada jenis aplikasi yang Anda buat, ada beberapa hosting item Anda yang berbeda.
Namun, karena contoh kita adalah aplikasi web yang sangat sederhana dan mudah, kita dapat menyederhanakan semuanya dan mengandalkan Cloud Run.
Jika Anda mengikuti langkah-langkah ini di komputer Anda sendiri, pastikan alat gcloud CLI telah diinstal dan diupdate.
Jika Anda menggunakan Cloud Shell, semua hal tersebut akan ditangani dan Anda cukup menjalankan perintah berikut di direktori sumber:
gcloud run deploy
4. Konfigurasi Aplikasi
1. Mengonfigurasi repositori Maven
Karena project ini masih dalam fase eksperimental, kita harus mengonfigurasi aplikasi agar dapat menemukan artefak eksperimental, yang tidak tersedia di repositori pusat maven.
Hal ini akan melibatkan penambahan elemen berikut ke pom.xml, yang dapat Anda lakukan di editor pilihan Anda.
Tambahkan bagian repositori dan pluginRepositories berikut ke pom kita:
<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 spring-native, 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
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 plugin spring-boot-maven 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 tiny builder hanyalah salah satu dari beberapa opsi. Ini adalah pilihan yang baik untuk kasus penggunaan kita karena memiliki sangat sedikit library dan utilitas tambahan, yang membantu meminimalkan permukaan serangan kita.
Misalnya, jika Anda mem-build aplikasi yang memerlukan akses ke beberapa library C umum, atau Anda 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 dikompilasi.
Sebelum menjalankan build, berikut beberapa hal yang perlu diingat:
- Proses ini akan memerlukan waktu lebih lama daripada build reguler (beberapa menit)
- Proses build ini dapat menggunakan banyak memori (beberapa gigabyte)
- Proses build ini mengharuskan daemon Docker dapat dijangkau
- Meskipun dalam contoh ini kita akan melalui prosesnya secara manual, Anda juga dapat mengonfigurasi fase build untuk memicu profil build native secara otomatis.
Untuk mem-build image:
mvn spring-boot:build-image
Setelah aplikasi tersebut di-build, kita siap melihat cara kerja aplikasi native.
Untuk menjalankan aplikasi:
docker run --rm -p 8080:8080 demo:0.0.1-SNAPSHOT
Pada tahap ini, kita berada dalam posisi yang tepat untuk melihat kedua sisi persamaan aplikasi native.
Kita telah mengorbankan sedikit waktu dan penggunaan memori tambahan pada waktu kompilasi, tetapi sebagai gantinya kita mendapatkan aplikasi yang dapat dimulai jauh lebih cepat, dan menggunakan memori yang jauh lebih sedikit (bergantung pada beban kerja).
Jika menjalankan docker images demo
untuk membandingkan ukuran gambar native dengan gambar asli, kita dapat melihat pengurangan yang drastis:
Kita juga harus memperhatikan bahwa dalam kasus penggunaan yang lebih kompleks, ada modifikasi tambahan yang diperlukan untuk memberi tahu compiler AOT tentang tindakan yang akan dilakukan aplikasi Anda saat runtime. Oleh karena itu, workload tertentu yang dapat diprediksi (seperti tugas batch) mungkin sangat cocok untuk hal ini, sementara workload lainnya mungkin lebih besar.
6. Men-deploy aplikasi native
Untuk men-deploy aplikasi ke Cloud Run, kita harus memasukkan image native ke pengelola paket seperti Artifact Registry.
1. Menyiapkan repositori Docker
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 harus memastikan bahwa kita diautentikasi untuk melakukan push ke registry baru.
gcloud CLI dapat menyederhanakan proses tersebut:
gcloud auth configure-docker us-central1-docker.pkg.dev
2. Mengirim image 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
Kemudian, kita dapat menggunakan docker push
untuk mengirimnya 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 men-deploy image yang telah disimpan 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 telah mem-build dan men-deploy aplikasi sebagai image native, kita dapat yakin bahwa aplikasi kita menggunakan biaya infrastruktur dengan sangat baik saat berjalan.
Anda dapat membandingkan waktu startup aplikasi dasar pengukuran kami dengan aplikasi native baru ini.
7. Ringkasan/Pembersihan
Selamat telah mem-build dan men-deploy aplikasi Spring Native di Google Cloud.
Semoga tutorial ini mendorong Anda untuk lebih memahami project Spring Native dan mengingatnya jika 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 untuk menghindari tagihan yang tidak perlu dari resource yang kita 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 adalah referensi online yang mungkin relevan untuk tutorial ini:
- Pelajari Petunjuk Native lebih lanjut
- Pelajari GraalVM lebih lanjut
- Cara ikut serta
- Error kehabisan memori saat mem-build image native
- Error aplikasi gagal dimulai
Lisensi
Karya ini dilisensikan berdasarkan Lisensi Umum Creative Commons Attribution 2.0.