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 uygulamayı Google Cloud'da dağıtacağız.

Bu araçla ilgili bileşenleri, projenin son geçmişini, bazı kullanım alanlarını ve elbette projelerinizde kullanmak için gereken adımları ele alacağız.

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

Tam zamanında derleme, uzun süren işlemler gibi durumlar için çok iyi optimize edilmiş olsa da önceden derlenmiş uygulamaların daha iyi performans gösterdiği belirli kullanım alanları vardır. Bu kullanım alanlarını codelab sırasında ele alacağız.

Bu demoda aşağıdaki işlemleri yapmayı öğreneceksiniz:

  • Cloud Shell'i kullanma
  • Cloud Run API'yi etkinleştirin
  • Spring Yerel 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 Okuyun ve alıştırmaları tamamlayın

Java ile ilgili deneyiminizi nasıl değerlendirirsiniz?

Yeni başlayan Orta düzey Uzman

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

Başlangıç Orta İleri

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 neler sağladıklarını ve burada nasıl birlikte çalıştıklarını anlamak faydalı olacaktır.

AOT derleme

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

Bu süreç, Java'ya kendine özgü taşınabilirlik özelliğini kazandırır. Bu sayede "bir kez yazıp her yerde çalıştırabiliriz". Ancak bu süreç, yerel kod çalıştırmaya kıyasla maliyetlidir.

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ılma sayısını sayarak gerçekleştirilir. İşlev, bir eşiği geçecek kadar sık çağrılırsa ( varsayılan olarak 10.000) daha fazla maliyetli yorumlamayı önlemek için çalışma zamanında yerel koda derlenir.

Önceden derleme ise tüm ulaşılabilir kodu derleme süresinde yerel bir yürütülebilir dosyaya derleyerek tam tersi bir yaklaşım benimser. Bu, çalışma zamanında bellek verimliliği ve diğer performans kazanımları için taşınabilirlikten ödün verir.

5042e8e62a05a27.png

Bu elbette bir ödünleşmedir ve her zaman almaya değmez. 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 kısıtlamalarının yüksek olduğu ortamlar

İlginç bir bilgi olarak, AOT derlemesi JDK 9'da deneysel bir özellik olarak kullanıma sunulmuştu. Ancak bu uygulamanın bakımı maliyetliydi ve hiç popüler olmamıştı. Bu nedenle, geliştiricilerin GraalVM'yi kullanması tercih edilerek Java 17'de sessizce kaldırıldı.

GraalVM

GraalVM, son derece hızlı başlatma süreleri, AOT yerel görüntü derlemesi ve geliştiricilerin birden fazla dili tek bir uygulamada karıştırmasına olanak tanıyan çok dilli özellikler sunan, yüksek düzeyde 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 ara hedeflerden bazıları şunlardır:

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

Spring Native

Basitçe ifade etmek gerekirse Spring Native, Spring uygulamalarını yerel olarak çalıştırılabilir dosyalara dönüştürmek için GraalVM'nin native-image derleyicisinin kullanılmasını sağlar.

Bu süreçte, uygulamanızdaki giriş noktasından erişilebilen tüm yöntemleri bulmak için derleme sırasında uygulamanızın statik analizi yapılır.

Bu, uygulamanızın "kapalı dünya" konseptini oluşturur. Bu konseptte tüm kodun derleme süresinde bilindiği varsayılır ve çalışma zamanında yeni kod yüklenmesine izin verilmez.

Yerel görüntü üretmenin, normal bir uygulamayı derlemekten daha uzun süren ve Java'nın belirli yönlerinde sınırlamalar getiren, belleği yoğun olarak kullanan bir işlem olduğunu unutmamak önemlidir.

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

3. Kurulum/Ön Hazırlık

Spring Native'i uygulamaya başlamadan önce, uygulamamızı oluşturup dağıtarak daha sonra yerel sürümle karşılaştırabileceğimiz bir performans temel çizgisi oluşturmamız gerekir.

1. Proje oluşturma

İlk olarak uygulamamızı start.spring.io adresinden 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ı, yazıldığı sırada spring-native projesinin desteklediği en son sürüm olan Spring Boot 2.6.4'ü kullanır.

GraalVM 21.0.3 sürümünden itibaren bu örnek için Java 17'yi de kullanabileceğinizi unutmayın. Bu eğitimde, yapılandırmayı en aza indirmek için Java 11'i kullanmaya devam edeceğiz.

Komut satırında ZIP dosyamız olduğunda projemiz için bir alt dizin oluşturabilir ve klasörü burada açabiliriz:

mkdir spring-native

cd spring-native

unzip ../io-native-starter.zip

2. Kod değişiklikleri

Proje açıldıktan sonra hızlıca bir yaşam işareti ekleyip Spring Native'i uyguladığımızda performansını göstereceğiz.

DemoApplication.java dosyasını aşağıdaki gibi 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ır. Bu nedenle, yerel uygulamaya dönüştürmeden önce başlangıç süresi hakkında fikir edinmek için bir görüntü oluşturup yerel olarak çalıştırabilirsiniz.

İmajımızı oluşturmak için:

mvn spring-boot:build-image

Temel 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. Temel uygulamayı dağıtma

Uygulamamızı oluşturduktan sonra dağıtıp süreleri not edeceğiz. Bu süreleri daha sonra yerel uygulamamızın başlangıç süreleriyle karşılaştıracağız.

Geliştirdiğiniz uygulamanın türüne bağlı olarak içeriklerinizi barındırmak için çeşitli seçenekler vardır.

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

Kendi makinenizde bu adımları uyguluyorsanız gcloud CLI aracının yüklü ve güncel olduğundan emin olun.

Cloud Shell'deyseniz tüm bunlar halledilir 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 hâlâ deneysel aşamada olduğundan, uygulamamızı Maven'in merkezi deposunda bulunmayan deneysel yapıları bulabilecek şekilde yapılandırmamız gerekiyor.

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

Aşağıdaki depoları ve pluginRepositories bölümlerini pom'umuza 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

Şimdi yerel resim uyumluluğunu ve kapladığı alanı 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>

Şimdi, spring-boot-maven-plugin'i yerel görüntü desteğini etkinleştirecek ve yerel görüntümüzü oluşturmak için paketo oluşturucuyu kullanacak şekilde güncelleyeceğiz:

<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 derleyici görüntüsü, çeşitli seçeneklerden yalnızca biridir. Çok az sayıda ek kitaplık ve yardımcı program içerdiğinden saldırı yüzeyimizi en aza indirmemize yardımcı olur. Bu nedenle, 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 full-builder daha uygun olabilir.

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

Tüm bunlar tamamlandığında görüntümüzü oluşturup 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 bir derlemeden (birkaç dakika) daha uzun sürer 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 ele alıyor olsak da derleme aşamalarınızı yerel bir derleme profilini otomatik olarak tetikleyecek şekilde de yapılandırabilirsiniz.

İmajımızı oluşturmak için:

mvn spring-boot:build-image

Bu işlem tamamlandığında doğal uygulamayı kullanmaya başlayabiliriz.

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örebiliriz.

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

Yerel resmin boyutunu orijinaliyle karşılaştırmak için docker images demo komutunu çalıştırdığımızda boyutun önemli ölçüde küçüldüğünü görebiliriz:

e667f65a011c1328.png

Daha karmaşık kullanım alanlarında, AOT derleyicisine uygulamanızın çalışma zamanında ne yapacağını bildirmek 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ş için çok uygun olabilirken diğerleri daha fazla çaba gerektirebilir.

6. Yerel uygulamamızı dağıtma

Uygulamamızı Cloud Run'a dağıtmak için yerel görüntümüzü Artifact Registry gibi bir paket yöneticisine aktarmamız gerekir.

1. Docker depomuzu hazırlama

Bu süreci 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 defterimize aktarmak için kimliğimizin doğrulandığından emin olmamız gerekir.

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

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

2. Görüntümüzü 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 görüntü olarak oluşturup dağıttığımız için, uygulamamızın çalışırken altyapı maliyetlerimizden mükemmel şekilde yararlandığından emin olabiliriz.

Temel uygulamamızın başlangıç sürelerini bu yeni yerel uygulamayla kendiniz karşılaştırabilirsiniz.

6dde63d35959b1bb.png

7. Özet/Temizleme

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

Bu eğitimin, Spring Native projesi hakkında daha fazla bilgi edinmenizi ve gelecekte ihtiyaçlarınızı karşılayabileceğini göz önünde bulundurmanızı sağlayacağını umuyoruz.

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

Bu codelab için bir Google Cloud projesi oluşturduysanız veya mevcut bir projeyi yeniden kullanıyorsanız kullandığımız kaynaklardan gereksiz ücret alınmaması için dikkatli olun.

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 ilk kullanıcıların sorunları gidermesine ve projeye dahil olmasına yardımcı olacak çok sayıda iyi kaynak bulunmaktadır:

Ek kaynaklar

Bu eğitimle ilgili olabilecek online kaynakları aşağıda bulabilirsiniz:

Lisans

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