Native Spring di Google Cloud

1. Ringkasan

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

Kita akan membahas komponennya, riwayat terbaru proyek, beberapa kasus penggunaan, dan tentu saja langkah-langkah yang diperlukan untuk menggunakannya dalam proyek Anda.

Project Spring Native saat ini dalam fase eksperimental, sehingga akan memerlukan beberapa konfigurasi khusus untuk memulai. Namun, seperti yang diumumkan di SpringOne 2021, Spring Native akan diintegrasikan ke Spring Framework 6.0 dan Spring Boot 3.0 dengan dukungan kelas satu, sehingga ini adalah waktu yang tepat untuk melihat project tersebut lebih dekat 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 di 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 Native Spring
  • Men-deploy aplikasi tersebut ke Cloud Run

Yang Anda butuhkan

Survei

Bagaimana Anda akan menggunakan tutorial ini?

Hanya membacanya Membacanya dan menyelesaikan latihan

Bagaimana Anda menilai pengalaman Anda dengan 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 kita pahami juga beberapa teknologi komponen ini, apa yang dapat dilakukannya untuk kita, dan bagaimana teknologi tersebut bekerja sama di sini.

Kompilasi AOT

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

Proses inilah yang memberi kita portabilitas tanda tangan Java - yang memungkinkan kita untuk "menulis sekali dan berjalan 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 dicapai dengan menghitung pemanggilan untuk sebuah fungsi, dan jika cukup sering dipanggil untuk melewati nilai minimum ( 10.000 secara default), fungsi akan dikompilasi ke kode native pada saat runtime untuk mencegah penafsiran yang lebih mahal.

Kompilasi ahead-of-time mengambil pendekatan sebaliknya, yaitu dengan mengompilasi semua kode yang dapat dijangkau menjadi file native yang dapat dieksekusi pada waktu kompilasi. Hal ini mengorbankan portabilitas dengan efisiensi memori dan peningkatan performa lainnya pada waktu proses.

5042e8e62a05a27.pngS

Ini tentu saja merupakan kompromi, dan tidak selalu layak dilakukan. Namun, kompilasi AOT dapat berfungsi dalam kasus penggunaan tertentu seperti:

  • Aplikasi berumur pendek yang mengutamakan waktu startup
  • Lingkungan dengan memori yang sangat terbatas dengan JIT yang mungkin terlalu mahal

Faktanya, kompilasi AOT diperkenalkan sebagai fitur eksperimental di JDK 9, meskipun implementasi ini mahal untuk dipelihara, dan tidak pernah benar-benar populer, sehingga dihapus dengan diam-diam di Java 17 dan digantikan oleh developer yang hanya menggunakan GraalVM.

GraalVM

GraalVM adalah distribusi JDK open source yang sangat dioptimalkan yang memiliki waktu startup yang sangat cepat, kompilasi gambar native AOT, dan kemampuan polyglot yang memungkinkan developer menggabungkan beberapa bahasa ke dalam satu aplikasi.

GraalVM sedang dalam pengembangan aktif, memperoleh kemampuan baru dan meningkatkan yang sudah ada sepanjang waktu, jadi saya mendorong developer untuk terus mengikuti perkembangan.

Beberapa pencapaian terbaru adalah:

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

Native Spring

Secara sederhana, Spring Native memungkinkan penggunaan compiler image native GraalVM untuk mengubah aplikasi Spring menjadi file native yang dapat dieksekusi.

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

Pada dasarnya, hal ini menciptakan "{i>closed-world<i}" 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 memakan waktu lebih lama daripada mengompilasi aplikasi biasa, dan menerapkan batasan pada aspek tertentu dari Java.

Dalam beberapa kasus, tidak diperlukan perubahan kode agar aplikasi dapat berfungsi dengan Spring Native. Namun, beberapa situasi memerlukan konfigurasi native khusus agar dapat berfungsi dengan baik. Dalam situasi tersebut, Native Spring sering 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 performa yang dapat dibandingkan dengan versi native nanti.

1. Membuat project

Kita akan mulai dengan menginstal 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 berbasis musim semi pada saat penulisan.

Perlu diperhatikan bahwa sejak rilis GraalVM 21.0.3, Anda juga dapat menggunakan Java 17 untuk sampel ini. Kita masih akan menggunakan Java 11 untuk tutorial ini guna meminimalkan konfigurasi yang diperlukan.

Setelah menempatkan 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 project terbuka, kita akan dengan cepat menambahkan tanda kehidupan dan menampilkan performa Spring Native setelah kita menjalankannya.

Edit DemoApplication.java agar cocok:

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 kita siap digunakan, jadi jangan ragu untuk membangun image dan menjalankannya secara lokal untuk mendapatkan gambaran tentang waktu startup sebelum kita mengonversinya menjadi aplikasi native.

Untuk membuat image:

mvn spring-boot:build-image

Anda juga dapat menggunakan docker images demo untuk mendapatkan gambaran tentang ukuran gambar dasar pengukuran: 6ecb403e9af1475e.pngS

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 waktunya, yang akan kita bandingkan dengan waktu startup aplikasi native nanti.

Tergantung pada jenis aplikasi yang Anda bangun, ada beberapa hosting yang berbeda untuk barang Anda.

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

Jika Anda mengikuti petunjuk di mesin sendiri, pastikan alat gcloud CLI sudah diinstal dan diupdate.

Jika Anda berada di Cloud Shell yang semuanya 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 tahap eksperimental, kita harus mengonfigurasi aplikasi agar dapat menemukan artefak eksperimental, yang tidak tersedia di repositori pusat Maven.

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

Tambahkan bagian repositori dan plugin Repositories 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 pegas, yang diperlukan untuk menjalankan aplikasi Spring sebagai gambar 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 kami

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 semi-boot-maven-plugin untuk mengaktifkan dukungan image native dan menggunakan builder paketo untuk membangun 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>

Perlu diketahui bahwa gambar builder kecil 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.

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

5. Membangun dan Menjalankan aplikasi native

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

Sebelum menjalankan build, berikut beberapa hal yang perlu diingat:

  • Proses ini memerlukan waktu yang lebih lama daripada build biasa (beberapa menit) d420322893640701.png
  • Proses build ini dapat membutuhkan banyak memori (beberapa gigabyte) cda24e1eb11fdbea.png
  • Proses build ini mengharuskan daemon Docker dapat dijangkau
  • Meskipun dalam contoh ini kita melalui proses secara manual, Anda juga dapat mengonfigurasi fase build untuk secara otomatis memicu profil build native.

Untuk membuat image:

mvn spring-boot:build-image

Setelah selesai dibuat, 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.

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

Jika menjalankan docker images demo untuk membandingkan ukuran gambar native dengan aslinya, kita dapat melihat pengurangan yang signifikan:

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. Oleh karena itu, workload tertentu yang dapat diprediksi (seperti tugas batch) mungkin sangat cocok untuk hal ini, sementara workload lain mungkin membutuhkan peningkatan yang lebih besar.

6. Men-deploy aplikasi native

Untuk men-deploy aplikasi ke Cloud Run, kita harus memasukkan image native ke dalam 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 perlu memastikan bahwa kita diautentikasi untuk melakukan push ke registry baru.

gcloud CLI dapat sedikit 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

Selanjutnya, 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 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 kita telah membangun dan men-deploy aplikasi sebagai image native, aplikasi kita bisa menggunakan biaya infrastruktur kita dengan baik saat berjalan.

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

6dde63d35959b1bb.pngS

7. Ringkasan/Pembersihan

Selamat, Anda telah berhasil membangun dan men-deploy aplikasi Native Spring di Google Cloud.

Semoga tutorial ini mendorong Anda untuk lebih mengenal project Spring Native dan mengingatnya jika dapat 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 Anda tidak dikenai biaya yang tidak perlu dari resource yang kami gunakan.

Anda dapat menghapus atau menonaktifkan layanan Cloud Run yang telah kita buat, menghapus gambar yang kita hosting, atau menonaktifkan seluruh project.

8. Referensi lainnya

Meskipun project Spring Native saat ini merupakan project baru dan eksperimental, sudah ada banyak sumber daya yang baik untuk membantu pengguna awal memecahkan masalah dan terlibat:

Referensi lainnya

Di bawah ini adalah referensi online yang mungkin relevan untuk tutorial ini:

Lisensi

Karya ini dilisensikan berdasarkan Lisensi Umum Creative Commons Attribution 2.0.