Google Cloud'da Spring Yerel

1. Genel Bakış

Bu codelab'de Spring Native projesi hakkında bilgi edinecek, bu projeyi kullanan bir uygulama oluşturacak ve bu uygulamayı Google Cloud'a dağıtacağız.

Bu makalede, bileşenlerini, projenin yakın geçmişini, bazı kullanım alanlarını ve elbette projenizde kullanmanız için gereken adımları ele alacağız.

Spring Native projesi şu anda deneysel aşamada olduğundan, çalışmaya başlamak için belirli bir yapılandırma gerekir. Ancak SpringOne 2021'de duyurulan üzere Spring Native, birinci sınıf destek ile Spring Framework 6.0 ve Spring Boot 3.0'a entegre edilecek. Bu nedenle, projenin yayınlanmasından birkaç ay önce projeye daha yakından bakmak için mükemmel bir zaman.

Tam zamanında derleme, uzun süre çalışan işlemler gibi işlemler için çok iyi optimize edilmiş olsa da önceden derlenmiş uygulamaların daha da iyi performans gösterdiği belirli kullanım alanları vardır. Bu kullanım alanlarını kod laboratuvarının ilerleyen bölümlerinde ele alacağız.

Aşağıdaki işlemleri yapmayı öğreneceksiniz:

  • Cloud Shell'i kullanma
  • Cloud Run API'yi etkinleştirme
  • Spring Native uygulaması oluşturma ve dağıtma
  • Bu tür bir uygulamayı Cloud Run'a dağıtma

Gerekenler

Anket

Bu eğitimi nasıl kullanacaksınız?

Yalnızca okuyun Okuyup alıştırmaları tamamlayın

Java ile ilgili deneyiminizi nasıl değerlendirirsiniz?

Acemi Orta Uzman

Google Cloud hizmetlerini kullanma deneyiminizi nasıl değerlendirirsiniz?

Acemi Orta Seviye Uzman

2. Arka plan

Spring Native projesi, geliştiricilere yerel uygulama performansı sunmak için çeşitli teknolojilerden yararlanır.

Spring Native'i tam olarak anlamak için bu bileşen teknolojilerinden birkaçını, bize ne gibi olanaklar sağladıklarını ve burada nasıl birlikte çalıştıklarını anlamak faydalı olacaktır.

AOT derlemesi

Geliştiriciler derleme zamanında normal olarak javac'ı çalıştırdığında .java kaynak kodumuz, bayt koduyla yazılmış .class dosyalarına derlenir. Bu bayt kodu yalnızca Java sanal makinesi tarafından anlaşılmak üzere tasarlanmıştır. Bu nedenle, kodumuzu çalıştırabilmemiz için JVM'nin bu kodu diğer makinelerde yorumlaması gerekir.

Bu işlem, Java'nın imzasını taşınabilir hale getirir. Böylece "bir kez yazıp her yerde çalıştırabiliriz". Ancak bu işlem, yerel kod çalıştırmaya kıyasla pahalıdır.

Neyse ki JVM'nin çoğu uygulaması, bu yorumlama maliyetini azaltmak için tam zamanında derlemeyi kullanır. Bu, bir işlevin çağrılarının sayılmasıyla elde edilir ve işlev bir eşiği ( varsayılan olarak 10.000) geçecek kadar sık çağrılırsa daha fazla pahalı yorumlamayı önlemek için çalışma zamanında yerel koda derlenir.

Önceden derleme, erişilebilir tüm kodu derleme zamanında yerel bir yürütülebilir dosyaya derleyerek tam tersini yapar. Bu, taşınabilirlik karşılığında bellek verimliliği ve çalışma zamanında diğer performans kazançlarını sağlar.

5042e8e62a05a27.png

Bu, elbette bir değişimdir ve her zaman buna değer değildir. Ancak AOT derlemesi, aşağıdakiler gibi belirli kullanım alanlarında öne çıkabilir:

  • Başlangıç süresinin önemli olduğu kısa ömürlü uygulamalar
  • JIT'nin çok maliyetli olabileceği, bellek açısından son derece kısıtlanmış ortamlar

AOT derlemesi, JDK 9'da deneysel bir özellik olarak kullanıma sunulmuştur. Ancak bu uygulamanın bakımı pahalı olduğundan ve hiçbir zaman tam olarak benimsenmediğinden, Java 17'de yalnızca GraalVM kullanan geliştiriciler için sessizce kaldırılmıştır.

GraalVM

GraalVM, son derece hızlı başlatma süreleri, AOT yerel görüntü derlemesi ve geliştiricilerin tek bir uygulamada birden fazla dili karıştırmasına olanak tanıyan çok dilli özellikler sunan, yüksek oranda optimize edilmiş bir açık kaynak JDK dağıtımıdır.

GraalVM aktif olarak geliştirilmekte, sürekli olarak yeni özellikler kazanmakta ve mevcut özellikleri iyileştirmektedir. Bu nedenle, geliştiricilerin gelişmeleri takip etmesini öneririm.

Son zamanlardaki bazı önemli gelişmeleri aşağıda bulabilirsiniz:

  • Yeni, kullanıcı dostu yerel resim derleme çıkışı ( 18.01.2021)
  • Java 17 desteği ( 18.01.2022)
  • Çok dilli derleme sürelerini iyileştirmek için varsayılan olarak çok katmanlı derlemeyi etkinleştirme ( 2021-04-20)

Spring Native

Basitçe söylemek gerekirse Spring Native, Spring uygulamalarını yerel yürütülebilir dosyalara dönüştürmek için GraalVM'in doğal görüntü derleyicisinin kullanımını sağlar.

Bu işlem, uygulamanızdaki giriş noktasından erişilebilen tüm yöntemleri bulmak için derleme zamanında uygulamanızın statik analizini gerçekleştirmeyi içerir.

Bu, temel olarak uygulamanız için "kapalı dünya" bir kavram oluşturur. Bu kavramda, tüm kodun derleme zamanında bilindiği varsayılır ve çalışma zamanında yeni kod yüklenmesine izin verilmez.

Yerel resim oluşturmanın, normal bir uygulamayı derlemekten daha uzun süren ve Java'nın belirli yönlerine sınırlamalar getiren, belleği yoğun olarak kullanan bir işlem olduğunu unutmayın.

Bazı durumlarda, bir uygulamanın Spring Native ile çalışması için kod değişikliği gerekmez. Ancak bazı durumlarda, düzgün çalışması için belirli yerel yapılandırmalar gerekir. Bu gibi durumlarda Spring Native, bu süreci basitleştirmek için genellikle Native Hints sağlar.

3. Kurulum/Ön çalışma

Spring Native'i uygulamaya başlamadan önce, daha sonra yerel sürümle karşılaştırabileceğimiz bir performans referans değeri oluşturmak için uygulamamızı oluşturup dağıtmamız gerekir.

1. Projeyi oluşturma

Uygulamamızı start.spring.io'dan alarak başlayacağız:

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

Bu başlangıç uygulaması, spring-native projesinin bu makalenin yazıldığı sırada desteklediği en son sürüm olan Spring Boot 2.6.4'ü kullanır.

GraalVM 21.0.3'ün yayınlanmasından bu yana bu örnek için Java 17'i de kullanabileceğinizi unutmayın. Bu eğitici için, ilgili yapılandırmayı en aza indirmek amacıyla Java 11'i kullanmaya devam edeceğiz.

ZIP'imizi komut satırına ekledikten sonra projemiz için bir alt dizin oluşturabilir ve klasörün sıkıştırmasını burada açabiliriz:

mkdir spring-native

cd spring-native

unzip ../io-native-starter.zip

2. Kod değişiklikleri

Projeyi açtıktan sonra, hızlıca bir hareketlilik belirtisi ekleyecek ve Spring Native'i çalıştırdıktan sonra performansını göstereceğiz.

DemoApplication.java dosyasını aşağıdakiyle eşleşecek şekilde düzenleyin:

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

Bu noktada temel uygulamamız kullanıma hazırdır. Bu nedenle, yerel bir uygulamaya dönüştürmeden önce başlatma süresi hakkında fikir edinmek için bir resim oluşturup yerel olarak çalıştırabilirsiniz.

Resmimizi oluşturmak için:

mvn spring-boot:build-image

Referans resmin boyutu hakkında fikir edinmek için docker images demo simgesini de kullanabilirsiniz: 6ecb403e9af1475e.png

Uygulamamızı çalıştırmak için:

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

3. Referans uygulamayı dağıtma

Uygulamamız hazır olduğuna göre uygulamayı dağıtıp süreleri not alacağız. Daha sonra bu süreleri yerel uygulama başlatma sürelerimizle karşılaştıracağız.

Oluşturduğunuz uygulamanın türüne bağlı olarak öğelerinizi barındırmak için kullanabileceğiniz birkaç farklı seçenek vardır.

Ancak örneğimiz çok basit ve anlaşılır bir web uygulaması olduğu için işleri basit tutabilir ve Cloud Run'a güvenebiliriz.

Bu adımları kendi makinenizde izliyorsanız gcloud CLI aracının yüklü ve güncel olduğundan emin olun.

Cloud Shell'deyseniz tüm bunlar sizin adınıza yapılır ve kaynak dizinde aşağıdaki komutu çalıştırmanız yeterlidir:

gcloud run deploy

4. Uygulama Yapılandırması

1. Maven depolarımızı yapılandırma

Bu proje henüz deneysel aşamada olduğundan, uygulamamızı maven'in merkezi deposunda bulunmayan deneysel yapıları bulabilecek şekilde yapılandırmamız gerekir.

Bunun için pom.xml dosyamıza aşağıdaki öğeleri eklememiz gerekir. Bu işlemi, seçtiğiniz düzenleyicide yapabilirsiniz.

pom dosyamıza aşağıdaki repositories ve pluginRepositories bölümlerini ekleyin:

<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. Bağımlılıklarımızı ekleme

Ardından, Spring uygulamasını yerel görüntü olarak çalıştırmak için gereken spring-native bağımlılığını ekleyin. Not: Gradle kullanıyorsanız bu adım gerekli değildir.

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

3. Eklentilerimizi ekleme/etkinleştirme

Ardından, yerel resim uyumluluğunu ve ayak izini iyileştirmek için AOT eklentisini ekleyin ( Daha fazla bilgi):

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

Ardından, spring-boot-maven-plugin'i yerel görüntü desteğini etkinleştirecek şekilde güncelleyeceğiz ve yerel görüntümüzü oluşturmak için paketo builder'ı kullanacağız:

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

Küçük inşaatçı resminin, çeşitli seçeneklerden yalnızca biri olduğunu unutmayın. Çok az sayıda ek kitaplığı ve yardımcı programı olduğu için saldırı yüzeyimizi en aza indirmeye yardımcı olan bu işletim sistemi, kullanım alanımız için iyi bir seçimdir.

Örneğin, bazı yaygın C kitaplıklarına erişmesi gereken bir uygulama geliştiriyorsanız veya uygulamanızın gereksinimlerinden henüz emin değilseniz tam derleyici daha uygun olabilir.

5. Yerel uygulama oluşturma ve çalıştırma

Tüm bunlar tamamlandığında, görüntümüzü oluşturabilir ve yerel, derlenmiş uygulamamızı çalıştırabiliriz.

Derlemeyi çalıştırmadan önce göz önünde bulundurmanız gereken birkaç nokta:

  • Bu işlem normal derlemeden daha uzun sürer (birkaç dakika) d420322893640701.png
  • Bu derleme işlemi çok fazla bellek (birkaç gigabayt) kullanabilir cda24e1eb11fdbea.png
  • Bu derleme işlemi için Docker daemon'a erişilebilmesi gerekir.
  • Bu örnekte süreci manuel olarak yürütüyoruz ancak derleme aşamalarınızı yerel derleme profilini otomatik olarak tetikleyecek şekilde de yapılandırabilirsiniz.

Resmimizi oluşturmak için:

mvn spring-boot:build-image

Bu işlem tamamlandıktan sonra yerel uygulamayı çalışırken görmeye hazırız.

Uygulamamızı çalıştırmak için:

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

Bu noktada, yerel uygulama denkleminin her iki tarafını da görebiliyoruz.

Derleme sırasında biraz zaman ve fazladan bellek kullanımından vazgeçtik ancak bunun karşılığında çok daha hızlı başlatılabilen ve (iş yüküne bağlı olarak) önemli ölçüde daha az bellek kullanan bir uygulama elde ettik.

Yerel resmin boyutunu orijinal resimle karşılaştırmak için docker images demo'ü çalıştırırsak önemli bir düşüş görebiliriz:

e667f65a011c1328.png

Daha karmaşık kullanım alanlarında, AOT derleyiciyi uygulamanızın çalışma zamanında ne yapacağını bilgilendirmek için ek değişiklikler yapılması gerektiğini de belirtmek isteriz. Bu nedenle, belirli tahmin edilebilir iş yükleri (ör. toplu işler) bu işleme çok uygun olabilirken diğerleri daha büyük bir artış sağlayabilir.

6. Yerel uygulamamızı dağıtma

Uygulamamızı Cloud Run'a dağıtmak için yerel resmimizi Artifact Registry gibi bir paket yöneticisine eklememiz gerekir.

1. Docker depomuzu hazırlama

Bu işlemi bir depo oluşturarak başlatabiliriz:

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

Ardından, yeni kayıt otoritemize push yapmak için kimlik doğrulamasını yaptığımızdan emin olmak isteriz.

gcloud CLI bu süreci oldukça basitleştirebilir:

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

2. Görüntüyü Artifact Registry'ye gönderme

Ardından resmimizi etiketleyeceğiz:

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

Ardından docker push kullanarak Artifact Registry'ye gönderebiliriz:

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

3. Cloud Run'a dağıtma

Artık Artifact Registry'de depoladığımız görüntüyü Cloud Run'a dağıtmaya hazırız:

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

Uygulamamızı yerel resim olarak oluşturup dağıttığımız için, uygulamamızın çalışırken altyapı maliyetlerimizi mükemmel bir şekilde kullandığından emin olabiliriz.

Referans uygulamamızın başlatma sürelerini bu yeni yerel uygulamayla karşılaştırabilirsiniz.

6dde63d35959b1bb.png

7. Özet/Temizleme

Google Cloud'da Spring Native uygulaması derleyip dağıttığınız için tebrikler.

Bu eğitim, Spring Native projesiyle daha fazla bilgi edinmenizi ve gelecekte ihtiyaçlarınızı karşılayabilecek bir çözüm olarak bu projeyi göz önünde bulundurmanızı sağlayacaktır.

İsteğe bağlı: Hizmeti temizleme ve/veya devre dışı bırakma

Bu kod laboratuvarı için bir Google Cloud projesi oluşturduysanız veya mevcut bir projeyi yeniden kullanıyorsanız kullandığımız kaynaklardan gereksiz ücretler alınmamasına dikkat edin.

Oluşturduğumuz Cloud Run hizmetlerini silebilir veya devre dışı bırakabilir, barındırdığımız resmi silebilir ya da projenin tamamını kapatabilirsiniz.

8. Ek kaynaklar

Spring Native projesi şu anda yeni ve deneysel bir proje olsa da erken kullanıcıların sorunları gidermesine ve projeye katılmasına yardımcı olacak çok sayıda iyi kaynak mevcuttur:

Ek kaynaklar

Aşağıda, bu eğitimle alakalı olabilecek internet kaynakları verilmiştir:

Lisans

Bu çalışma, Creative Commons Attribution 2.0 Genel Amaçlı Lisans ile lisans altına alınmıştır.