เนทีฟในช่วงฤดูใบไม้ผลิบน Google Cloud

1. ภาพรวม

ในโค้ดแล็บนี้ เราจะได้เรียนรู้เกี่ยวกับโปรเจ็กต์ Spring Native, การสร้างแอปที่ใช้โปรเจ็กต์ดังกล่าว และทำให้ใช้งานได้ใน Google Cloud

เราจะพูดถึงคอมโพเนนต์ ประวัติล่าสุดของโปรเจ็กต์ กรณีการใช้งานบางส่วน และแน่นอนขั้นตอนที่จำเป็นสำหรับคุณในการใช้โปรเจ็กต์ในโปรเจ็กต์ของคุณ

ขณะนี้โปรเจ็กต์ Spring Native อยู่ในขั้นทดลอง จึงต้องมีการกำหนดค่าบางอย่างเพื่อเริ่มต้นใช้งาน อย่างไรก็ตาม ตามที่ได้ประกาศไว้ในงาน SpringOne 2021 ทางเราจะผสานรวม Spring Native เข้ากับ Spring Framework 6.0 และ Spring Boot 3.0 พร้อมการสนับสนุนชั้นยอด ดังนั้นตอนนี้จึงเป็นโอกาสที่ดีที่จะได้ศึกษาโปรเจ็กต์นี้อย่างละเอียดอีกสัก 2-3 เดือนก่อนเปิดตัว

แม้ว่าการคอมไพล์แบบทันเวลาจะได้รับการเพิ่มประสิทธิภาพอย่างดีสำหรับสิ่งต่างๆ เช่น กระบวนการที่ทำงานเป็นเวลานาน แต่ก็มีบางกรณีการใช้งานที่แอปพลิเคชันที่คอมไพล์ล่วงหน้าจะมีประสิทธิภาพดีกว่า ซึ่งเราจะพูดถึงในโค้ดแล็บ

คุณจะได้เรียนรู้วิธีต่อไปนี้

  • ใช้ Cloud Shell
  • เปิดใช้ Cloud Run API
  • สร้างและทำให้แอป Spring Native ใช้งานได้
  • ทำให้แอปดังกล่าวใช้งานได้ใน Cloud Run

สิ่งที่ต้องมี

แบบสำรวจ

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านอย่างเดียว อ่านและทำแบบฝึกหัดให้เสร็จ

คุณจะให้คะแนนประสบการณ์การใช้งาน Java เท่าไร

ผู้ฝึกหัด ระดับกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์การใช้งานบริการ Google Cloud เท่าใด

ผู้ฝึกหัด ระดับกลาง ผู้ชำนาญ

2. ข้อมูลเบื้องต้น

โปรเจ็กต์ Spring Native ใช้เทคโนโลยีหลายอย่างเพื่อมอบประสิทธิภาพของแอปพลิเคชันเนทีฟให้แก่นักพัฒนาแอป

หากต้องการทําความเข้าใจ Spring Native อย่างเต็มรูปแบบ คุณควรทําความเข้าใจเทคโนโลยีคอมโพเนนต์เหล่านี้ 2-3 อย่าง ประโยชน์ที่เทคโนโลยีเหล่านี้มอบให้เรา และวิธีทํางานร่วมกัน

การคอมไพล์ AOT

เมื่อนักพัฒนาซอฟต์แวร์เรียกใช้ javac ตามปกติในเวลาคอมไพล์ ซอร์สโค้ด .java ของเราจะคอมไพล์เป็นไฟล์ .class ซึ่งเขียนด้วยไบต์โค้ด บิตโค้ดนี้มีไว้เพื่อให้เครื่องเสมือน Java เข้าใจเท่านั้น ดังนั้น JVM จะต้องตีความโค้ดนี้บนเครื่องอื่นๆ เพื่อให้เราเรียกใช้โค้ดได้

กระบวนการนี้ช่วยให้เรานําโค้ด Java ไปใช้ได้ในทุกแพลตฟอร์ม ซึ่งช่วยให้เรา "เขียนครั้งเดียวและใช้งานได้ทุกที่" แต่ก็มีค่าใช้จ่ายสูงเมื่อเทียบกับการเรียกใช้โค้ดเนทีฟ

แต่โชคดีที่การใช้งาน JVM ส่วนใหญ่ใช้การคอมไพล์แบบทันท่วงทีเพื่อลดต้นทุนการตีความนี้ ซึ่งทำได้โดยการนับการเรียกใช้ฟังก์ชัน และหากมีการเรียกใช้บ่อยพอที่จะผ่านเกณฑ์ ( 10,000 ครั้งโดยค่าเริ่มต้น) ระบบจะคอมไพล์เป็นโค้ดเนทีฟที่รันไทม์เพื่อป้องกันการตีความที่สิ้นเปลืองค่าใช้จ่ายเพิ่มเติม

การคอมไพล์ล่วงหน้าใช้แนวทางที่ตรงกันข้าม โดยคอมไพล์โค้ดที่เข้าถึงได้ทั้งหมดเป็นไฟล์ปฏิบัติการแบบเนทีฟ ณ เวลาที่คอมไพล์ ซึ่งจะแลกความสามารถในการพกพาเพื่อประสิทธิภาพหน่วยความจําและประสิทธิภาพอื่นๆ ที่เพิ่มขึ้นเมื่อรันไทม์

5042e8e62a05a27.png

แน่นอนว่าข้อเสียนี้อาจไม่คุ้มค่าเสมอไป อย่างไรก็ตาม การคอมไพล์ AOT จะมีประโยชน์ในบางกรณี เช่น

  • แอปพลิเคชันที่มีการใช้งานสั้นๆ ซึ่งเวลาเริ่มต้นมีความสำคัญ
  • สภาพแวดล้อมที่มีหน่วยความจําจํากัดสูงซึ่ง JIT อาจใช้ทรัพยากรมากเกินไป

เกร็ดความรู้ที่น่าสนใจคือ การคอมไพล์ AOT เปิดตัวเป็นฟีเจอร์ทดลองใน JDK 9 แม้ว่าการใช้งานนี้จะบำรุงรักษาได้ยากและไม่ค่อยได้รับความนิยมนัก แต่เราก็นําออกอย่างเงียบๆ ใน Java 17 เพื่อสนับสนุนนักพัฒนาซอฟต์แวร์ที่ใช้ GraalVM เท่านั้น

GraalVM

GraalVM เป็น JDK แบบโอเพนซอร์สที่ได้รับการเพิ่มประสิทธิภาพอย่างสูง ซึ่งใช้เวลาเริ่มต้นอย่างรวดเร็วมาก มีการคอมไพล์อิมเมจเนทีฟ AOT และความสามารถในการใช้หลายภาษา ซึ่งช่วยให้นักพัฒนาแอปสามารถผสมผสานหลายภาษาไว้ในแอปพลิเคชันเดียวได้

GraalVM อยู่ระหว่างการพัฒนาอย่างต่อเนื่อง โดยได้รับความสามารถใหม่ๆ และปรับปรุงความสามารถที่มีอยู่อยู่เสมอ เราจึงขอแนะนำให้นักพัฒนาซอฟต์แวร์ติดตามข่าวสารอยู่เสมอ

เหตุการณ์สำคัญล่าสุดบางส่วนมีดังนี้

  • เอาต์พุตการสร้างรูปภาพเนทีฟใหม่ที่ใช้งานง่าย ( 2021-01-18)
  • การรองรับ Java 17 ( 2022-01-18)
  • การเปิดใช้การคอมไพล์แบบหลายระดับโดยค่าเริ่มต้นเพื่อปรับปรุงเวลาในการคอมไพล์หลายภาษา ( 2021-04-20)

Spring Native

กล่าวอย่างง่ายคือ Spring Native ช่วยให้ใช้คอมไพเลอร์รูปภาพเนทีฟของ GraalVM เพื่อเปลี่ยนแอปพลิเคชัน Spring ให้เป็นไฟล์ปฏิบัติการแบบเนทีฟได้

กระบวนการนี้เกี่ยวข้องกับการวิเคราะห์แบบคงที่ของแอปพลิเคชัน ณ เวลาคอมไพล์เพื่อค้นหาเมธอดทั้งหมดในแอปพลิเคชันซึ่งเข้าถึงได้จากจุดแรกเข้า

ซึ่งโดยพื้นฐานแล้วจะเป็นการสร้างแนวคิด "โลกแบบปิด" ของแอปพลิเคชัน โดยระบบจะถือว่าโค้ดทั้งหมดเป็นที่รู้จัก ณ เวลาคอมไพล์ และจะไม่อนุญาตให้โหลดโค้ดใหม่เมื่อรันไทม์

โปรดทราบว่าการสร้างรูปภาพเนทีฟเป็นกระบวนการที่ใช้หน่วยความจำมาก ซึ่งใช้เวลานานกว่าการคอมไพล์แอปพลิเคชันปกติ และมีข้อจํากัดบางประการในบางแง่มุมของ Java

ในบางกรณี คุณไม่จําเป็นต้องเปลี่ยนแปลงโค้ดเพื่อให้แอปพลิเคชันทํางานร่วมกับ Spring Native ได้ อย่างไรก็ตาม ในบางสถานการณ์ต้องมีการกําหนดค่าเนทีฟที่เฉพาะเจาะจงจึงจะทํางานได้อย่างถูกต้อง ในกรณีเหล่านี้ Spring Native มักจะให้คำแนะนำแบบเนทีฟเพื่อลดความซับซ้อนของกระบวนการนี้

3. การตั้งค่า/การเตรียมงาน

ก่อนเริ่มใช้งาน Spring Native เราจะต้องสร้างและติดตั้งใช้งานแอปเพื่อสร้างพื้นฐานประสิทธิภาพที่เรานำไปเปรียบเทียบกับเวอร์ชันเนทีฟในภายหลังได้

1. การสร้างโปรเจ็กต์

เราจะเริ่มต้นด้วยการสร้างแอปจาก 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

แอปเริ่มต้นนี้ใช้ Spring Boot 2.6.4 ซึ่งเป็นเวอร์ชันล่าสุดที่โปรเจ็กต์ spring-native รองรับ ณ เวลาที่เขียน

โปรดทราบว่าตั้งแต่การเปิดตัว GraalVM 21.0.3 คุณสามารถใช้ Java 17 กับตัวอย่างนี้ได้เช่นกัน เราจะยังคงใช้ Java 11 ในบทแนะนำนี้เพื่อลดการกำหนดค่าที่เกี่ยวข้อง

เมื่อเรามีไฟล์ ZIP ในบรรทัดคำสั่งแล้ว เราก็สามารถสร้างไดเรกทอรีย่อยสำหรับโปรเจ็กต์และแตกไฟล์โฟลเดอร์ในนั้นได้โดยทำดังนี้

mkdir spring-native

cd spring-native

unzip ../io-native-starter.zip

2. การเปลี่ยนแปลงโค้ด

เมื่อเปิดโปรเจ็กต์แล้ว เราจะเพิ่มสัญญาณของชีวิตอย่างรวดเร็วและแสดงประสิทธิภาพของ Spring Native เมื่อเรียกใช้

แก้ไข DemoApplication.java ให้ตรงกับข้อมูลต่อไปนี้

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

ตอนนี้แอปพื้นฐานของเราพร้อมใช้งานแล้ว คุณจึงสร้างภาพและเรียกใช้ในเครื่องเพื่อดูเวลาเริ่มต้นได้ก่อนที่จะแปลงเป็นแอปพลิเคชันเนทีฟ

วิธีสร้างรูปภาพ

mvn spring-boot:build-image

นอกจากนี้ คุณยังใช้ docker images demo เพื่อดูขนาดของรูปภาพพื้นฐานได้ 6ecb403e9af1475e.png

วิธีเรียกใช้แอป

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

3. ติดตั้งใช้งานแอปพื้นฐาน

ตอนนี้เรามีแอปแล้ว เราจะติดตั้งใช้งานและจดบันทึกเวลาไว้เพื่อเปรียบเทียบกับเวลาเริ่มต้นของแอปเนทีฟในภายหลัง

บริการโฮสติ้งข้อมูลของคุณมีหลายประเภท ขึ้นอยู่กับประเภทแอปพลิเคชันที่คุณสร้าง

อย่างไรก็ตาม เนื่องจากตัวอย่างของเราเป็นเว็บแอปพลิเคชันที่เรียบง่ายและตรงไปตรงมา เราจึงใช้ Cloud Run ได้อย่างง่ายดาย

หากคุณทำตามในเครื่องของคุณเอง โปรดตรวจสอบว่าได้ติดตั้งและอัปเดตเครื่องมือ gcloud CLI แล้ว

หากคุณใช้ Cloud Shell ระบบจะจัดการทุกอย่างให้คุณและคุณเพียงเรียกใช้คำสั่งต่อไปนี้ในไดเรกทอรีต้นทาง

gcloud run deploy

4. การกำหนดค่าแอปพลิเคชัน

1. การกำหนดค่าที่เก็บ Maven

เนื่องจากโปรเจ็กต์นี้ยังอยู่ในขั้นทดลอง เราจึงต้องกำหนดค่าแอปเพื่อให้ค้นหาอาร์ติแฟกต์เวอร์ชันทดลองได้ ซึ่งไม่มีอยู่ในที่เก็บข้อมูลส่วนกลางของ Maven

ซึ่งจะเกี่ยวข้องกับการเพิ่มองค์ประกอบต่อไปนี้ลงใน pom.xml ซึ่งคุณทําได้ในเครื่องมือแก้ไขที่เลือก

เพิ่มส่วน repositories และ pluginRepositories ต่อไปนี้ลงใน pom

<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. การเพิ่มทรัพยากร Dependency

ถัดไป ให้เพิ่มข้อกําหนดของ spring-native ซึ่งจําเป็นสําหรับเรียกใช้แอปพลิเคชัน Spring เป็นอิมเมจเนทีฟ หมายเหตุ: ขั้นตอนนี้ไม่จําเป็นหากคุณใช้ Gradle

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

3. การเพิ่ม/เปิดใช้ปลั๊กอิน

จากนั้นเพิ่มปลั๊กอิน AOT เพื่อปรับปรุงความเข้ากันได้และขนาดของรูปภาพเนทีฟ ( อ่านเพิ่มเติม)

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

ตอนนี้เราจะอัปเดต spring-boot-maven-plugin เพื่อเปิดใช้การรองรับอิมเมจเนทีฟ และใช้ Paketo Builder เพื่อสร้างอิมเมจเนทีฟ

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

โปรดทราบว่ารูปภาพTiny Builder เป็นเพียงตัวเลือกหนึ่งในหลายตัวเลือก ซึ่งเป็นตัวเลือกที่ดีสำหรับ Use Case ของเราเนื่องจากมีไลบรารีและยูทิลิตีเพิ่มเติมเพียงไม่กี่รายการ ซึ่งช่วยจํากัดพื้นที่ในการโจมตีได้

เช่น หากคุณกำลังสร้างแอปที่ต้องเข้าถึงไลบรารี C ทั่วไปบางรายการ หรือยังไม่แน่ใจเกี่ยวกับข้อกำหนดของแอป full-builder อาจเหมาะกว่า

5. สร้างและเรียกใช้แอปเนทีฟ

เมื่อทุกอย่างพร้อมแล้ว เราควรจะสร้างรูปภาพและเรียกใช้แอปที่คอมไพล์แบบเนทีฟได้

สิ่งที่ควรทราบก่อนเรียกใช้บิลด์มีดังนี้

  • ซึ่งจะใช้เวลานานกว่าการสร้างปกติ (2-3 นาที) d420322893640701.png
  • กระบวนการบิลด์นี้อาจใช้หน่วยความจํามาก (2-3 กิกะไบต์) cda24e1eb11fdbea.png
  • กระบวนการสร้างนี้กำหนดให้เข้าถึง Daemon ของ Docker ได้
  • แม้ว่าในตัวอย่างนี้เราจะทำตามขั้นตอนด้วยตนเอง แต่คุณยังกำหนดค่าระยะการสร้างเพื่อเรียกใช้โปรไฟล์การสร้างเนทีฟโดยอัตโนมัติได้ด้วย

วิธีสร้างรูปภาพ

mvn spring-boot:build-image

เมื่อสร้างแล้ว เราก็พร้อมดูแอปเนทีฟที่ทำงานจริง

วิธีเรียกใช้แอป

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

ตอนนี้เราอยู่ในจุดที่เหมาะสําหรับดูทั้ง 2 ด้านของสมการแอปพลิเคชันเนทีฟ

เราเสียเวลาและหน่วยความจําไปบ้างในช่วงคอมไพล์ แต่สิ่งที่ได้คือแอปพลิเคชันที่เริ่มต้นได้เร็วขึ้นมากและใช้หน่วยความจําน้อยลงอย่างมาก (ขึ้นอยู่กับปริมาณงาน)

หากเรียกใช้ docker images demo เพื่อเปรียบเทียบขนาดของรูปภาพเนทีฟกับต้นฉบับ เราจะเห็นว่าขนาดลดลงอย่างมาก

e667f65a011c1328.png

นอกจากนี้ โปรดทราบว่าใน Use Case ที่ซับซ้อนมากขึ้น ต้องมีการแก้ไขเพิ่มเติมเพื่อแจ้งให้คอมไพเลอร์ AOT ทราบว่าแอปของคุณจะทําอะไรบ้างเมื่อรันไทม์ ด้วยเหตุนี้ ปริมาณงานที่คาดการณ์ได้บางอย่าง (เช่น งานแบทช์) จึงอาจเหมาะกับวิธีนี้มาก ขณะที่งานอื่นๆ อาจให้ประสิทธิภาพที่ดีขึ้น

6. การนำแอปเวอร์ชันเนทีฟของเราไปใช้งาน

หากต้องการทำให้แอปใช้งานได้ใน Cloud Run เราจะต้องส่งอิมเมจเนทีฟไปยังเครื่องมือจัดการแพ็กเกจ เช่น Artifact Registry

1. เตรียมที่เก็บ Docker

เราเริ่มกระบวนการนี้ได้โดยการสร้างที่เก็บข้อมูล ดังนี้

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

ถัดไป เราต้องการตรวจสอบว่าเราตรวจสอบสิทธิ์เพื่อส่งข้อมูลไปยังรีจิสทรีใหม่แล้ว

gcloud CLI ช่วยให้คุณดำเนินการดังกล่าวได้ง่ายขึ้นมาก

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

2. การพุชรูปภาพไปยัง Artifact Registry

ต่อไปเราจะติดแท็กรูปภาพ โดยทำดังนี้

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

จากนั้นเราจะใช้ docker push เพื่อส่งไปยัง Artifact Registry ได้

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

3. การทำให้ใช้งานได้กับ Cloud Run

ตอนนี้เราพร้อมที่จะทำให้อิมเมจที่เราจัดเก็บไว้ใน Artifact Registry ใช้งานได้กับ Cloud Run แล้ว โดยทำดังนี้

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

เนื่องจากเราได้สร้างและติดตั้งใช้งานแอปเป็นอิมเมจเนทีฟ เราจึงมั่นใจได้ว่าแอปพลิเคชันของเราใช้โครงสร้างพื้นฐานอย่างคุ้มค่าขณะทำงาน

คุณสามารถเปรียบเทียบเวลาเริ่มต้นของแอปพื้นฐานกับแอปใหม่แบบเนทีฟนี้ด้วยตัวคุณเอง

6dde63d35959b1bb.png

7. สรุป/ล้างข้อมูล

ยินดีด้วยที่สร้างและทำให้แอปพลิเคชัน Spring Native ใช้งานได้ใน Google Cloud

เราหวังว่าบทแนะนำนี้จะทําให้คุณคุ้นเคยกับโปรเจ็กต์ Spring Native มากขึ้นและอย่าลืมใช้โปรเจ็กต์นี้หากตรงกับความต้องการของคุณในอนาคต

ไม่บังคับ: ล้างข้อมูลและ/หรือปิดใช้บริการ

ไม่ว่าคุณจะสร้างโปรเจ็กต์ Google Cloud สําหรับโค้ดแล็บนี้หรือใช้โปรเจ็กต์ที่มีอยู่ โปรดระมัดระวังการเรียกเก็บเงินที่ไม่จําเป็นจากทรัพยากรที่เราใช้

คุณสามารถลบหรือปิดใช้บริการ Cloud Run ที่เราสร้างขึ้น ลบรูปภาพที่เราโฮสต์ หรือปิดทั้งโปรเจ็กต์

8. แหล่งข้อมูลเพิ่มเติม

แม้ว่าขณะนี้โปรเจ็กต์ Spring Native จะเป็นโปรเจ็กต์ใหม่และโปรเจ็กต์ทดลอง แต่ก็มีแหล่งข้อมูลที่เป็นประโยชน์มากมายที่จะช่วยให้ผู้ใช้งานกลุ่มแรกแก้ปัญหาและมีส่วนร่วมได้

แหล่งข้อมูลเพิ่มเติม

ด้านล่างนี้คือแหล่งข้อมูลออนไลน์ที่อาจเกี่ยวข้องกับบทแนะนำนี้

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 ทั่วไป