การพัฒนาด้วยเวิร์กสเตชันระบบคลาวด์และโค้ดของระบบคลาวด์

1. ภาพรวม

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

สิ่งที่คุณจะได้เรียนรู้

ในแล็บนี้ คุณจะได้เรียนรู้วิธีการพัฒนาด้วยคอนเทนเนอร์ใน GCP ซึ่งรวมถึง

  • การพัฒนา InnerLoop ด้วย Cloud Workstations
  • การสร้างแอปพลิเคชันเริ่มต้น Java ใหม่
  • การเดินผ่านกระบวนการพัฒนา
  • การพัฒนาบริการ REST แบบ CRUD อย่างง่าย
  • การแก้ไขข้อบกพร่องของแอปพลิเคชันในคลัสเตอร์ GKE
  • การเชื่อมต่อแอปพลิเคชันกับฐานข้อมูล Cloud SQL

58a4cdd3ed7a123a.png

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง

  1. ลงชื่อเข้าใช้ Google Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • ชื่อโปรเจ็กต์คือชื่อที่แสดงสำหรับผู้เข้าร่วมโปรเจ็กต์นี้ ซึ่งเป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตได้ทุกเมื่อ
  • รหัสโปรเจ็กต์จะไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมดและเปลี่ยนแปลงไม่ได้ (เปลี่ยนไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ซึ่งโดยปกติแล้วคุณไม่จำเป็นต้องสนใจว่าสตริงนั้นคืออะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น PROJECT_ID) หากไม่ชอบรหัสที่สร้างขึ้น คุณก็สร้างรหัสแบบสุ่มอีกรหัสหนึ่งได้ หรือคุณจะลองใช้ชื่อของคุณเองเพื่อดูว่าพร้อมใช้งานหรือไม่ก็ได้ คุณจะเปลี่ยนแปลงรหัสนี้หลังจากขั้นตอนนี้ไม่ได้ และรหัสจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์
  • โปรดทราบว่ายังมีค่าที่ 3 ซึ่งคือหมายเลขโปรเจ็กต์ที่ API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 นี้ได้ในเอกสารประกอบ
  1. จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของ Cloud การทำตาม Codelab นี้ไม่ควรมีค่าใช้จ่ายมากนัก หรืออาจไม่มีเลย หากต้องการปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่สร้างขึ้นหรือลบทั้งโปรเจ็กต์ได้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD

เริ่มโปรแกรมแก้ไข Cloud Shell

Lab นี้ออกแบบและทดสอบเพื่อใช้กับ Google Cloud Shell Editor วิธีเข้าถึงเครื่องมือแก้ไข

  1. เข้าถึงโปรเจ็กต์ Google ที่ https://console.cloud.google.com
  2. คลิกไอคอนโปรแกรมแก้ไข Cloud Shell ที่มุมขวาบน

8560cc8d45e8c112.png

  1. แผงใหม่จะเปิดขึ้นที่ด้านล่างของหน้าต่าง
  2. คลิกปุ่ม "เปิดเครื่องมือแก้ไข"

9e504cb98a6a8005.png

  1. เครื่องมือแก้ไขจะเปิดขึ้นพร้อมกับ Explorer ทางด้านขวาและเครื่องมือแก้ไขในพื้นที่ส่วนกลาง
  2. นอกจากนี้ ควรมีแผงเทอร์มินัลที่ด้านล่างของหน้าจอด้วย
  3. หากเทอร์มินัลไม่ได้เปิดอยู่ ให้ใช้ชุดค่าผสมของปุ่ม `ctrl+`` เพื่อเปิดหน้าต่างเทอร์มินัลใหม่

ตั้งค่า gcloud

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

export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

โคลนซอร์สโค้ด

ซอร์สโค้ดสำหรับแล็บนี้อยู่ใน container-developer-workshop ใน GoogleCloudPlatform บน GitHub โคลนด้วยคำสั่งด้านล่าง แล้วเปลี่ยนเป็นไดเรกทอรี

git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot

จัดสรรโครงสร้างพื้นฐานที่ใช้ใน Lab นี้

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

./setup_with_cw.sh &

คลัสเตอร์ Cloud Workstations

เปิด Cloud Workstations ใน Cloud Console รอให้คลัสเตอร์อยู่ในสถานะ READY

305e1a3d63ac7ff6.png

สร้างการกำหนดค่าเวิร์กสเตชัน

หากเซสชัน Cloud Shell ถูกตัดการเชื่อมต่อ ให้คลิก "เชื่อมต่ออีกครั้ง" แล้วเรียกใช้คำสั่ง gcloud CLI เพื่อตั้งค่ารหัสโปรเจ็กต์ แทนที่รหัสโปรเจ็กต์ตัวอย่างด้านล่างด้วยรหัสโปรเจ็กต์ Qwiklabs ของคุณก่อนเรียกใช้คำสั่ง

gcloud config set project qwiklabs-gcp-project-id

เรียกใช้สคริปต์ด้านล่างในเทอร์มินัลเพื่อสร้างการกำหนดค่า Cloud Workstations

cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh

ตรวจสอบผลลัพธ์ในส่วนการกำหนดค่า โดยจะใช้เวลา 2 นาทีในการเปลี่ยนเป็นสถานะพร้อม

7a6af5aa2807a5f2.png

เปิด Cloud Workstations ในคอนโซล แล้วสร้างอินสแตนซ์ใหม่

a53adeeac81a78c8.png

เปลี่ยนชื่อเป็น my-workstation แล้วเลือกการกำหนดค่าที่มีอยู่: codeoss-java

f21c216997746097.png

ตรวจสอบผลลัพธ์ในส่วนเวิร์กสเตชัน

66a9fc8b20543e32.png

เปิดเวิร์กสเตชัน

เริ่มและเปิดตัวเวิร์กสเตชัน

c91bb69b61ec8635.png

อนุญาตคุกกี้ของบุคคลที่สามโดยคลิกไอคอนในแถบที่อยู่ 1b8923e2943f9bc4.png

fcf9405b6957b7d7.png

คลิก "เว็บไซต์ไม่ทำงานใช่ไหม"

36a84c0e2e3b85b.png

คลิก "อนุญาตคุกกี้"

2259694328628fba.png

เมื่อเปิดตัวเวิร์กสเตชันแล้ว คุณจะเห็น IDE ของ Code OSS ปรากฏขึ้น คลิก "ทำเครื่องหมายว่าเสร็จสิ้น" ในหน้าเริ่มต้นใช้งาน IDE ของเวิร์กสเตชัน

94874fba9b74cc22.png

3. การสร้างแอปพลิเคชันเริ่มต้น Java ใหม่

ในส่วนนี้ คุณจะได้สร้างแอปพลิเคชัน Java Spring Boot ใหม่ตั้งแต่ต้นโดยใช้แอปพลิเคชันตัวอย่างที่ spring.io จัดเตรียมไว้ เปิดเทอร์มินัลใหม่

c31d48f2e4938c38.png

โคลนแอปพลิเคชันตัวอย่าง

  1. สร้างแอปพลิเคชันเริ่มต้น
curl  https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=17 -d packageName=com.example.springboot -o sample-app.zip

คลิกปุ่มอนุญาตหากเห็นข้อความนี้ เพื่อให้คุณคัดลอกและวางลงในเวิร์กสเตชันได้

58149777e5cc350a.png

  1. คลายซิปแอปพลิเคชัน
unzip sample-app.zip -d sample-app
  1. เปิดโฟลเดอร์ "sample-app"
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

เพิ่ม spring-boot-devtools และ Jib

หากต้องการเปิดใช้ Spring Boot DevTools ให้ค้นหาและเปิด pom.xml จาก Explorer ในโปรแกรมแก้ไข จากนั้นวางโค้ดต่อไปนี้หลังบรรทัดรายละเอียดที่อ่านว่า <description>Demo project for Spring Boot</description>

  1. เพิ่ม spring-boot-devtools ใน pom.xml

เปิด pom.xml ในรูทของโปรเจ็กต์ เพิ่มการกำหนดค่าต่อไปนี้หลังจากรายการ Description

pom.xml

  <!--  Spring profiles-->
  <profiles>
    <profile>
      <id>sync</id>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
        </dependency>
      </dependencies>
    </profile>
  </profiles>
  1. เปิดใช้ปลั๊กอิน Jib Maven ใน pom.xml

Jib เป็นเครื่องมือสร้างคอนเทนเนอร์ Java แบบโอเพนซอร์สจาก Google ที่ช่วยให้นักพัฒนาซอฟต์แวร์ Java สร้างคอนเทนเนอร์ได้โดยใช้เครื่องมือ Java ที่คุ้นเคย Jib เป็นเครื่องมือสร้างอิมเมจคอนเทนเนอร์ที่รวดเร็วและเรียบง่าย ซึ่งจะจัดการขั้นตอนทั้งหมดในการแพ็กเกจแอปพลิเคชันของคุณลงในอิมเมจคอนเทนเนอร์ โดยไม่จำเป็นต้องเขียน Dockerfile หรือติดตั้ง Docker และผสานรวมเข้ากับ Maven และ Gradle โดยตรง

เลื่อนลงในไฟล์ pom.xml แล้วอัปเดตส่วน Build เพื่อรวมปลั๊กอิน Jib ส่วนบิลด์ควรตรงกับส่วนต่อไปนี้เมื่อเสร็จสมบูรณ์

pom.xml

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <!--  Jib Plugin-->
      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
       <!--  Maven Resources Plugin-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
    </plugins>
  </build>

สร้างไฟล์ Manifest

Skaffold มีเครื่องมือแบบผสานรวมที่จะช่วยลดความซับซ้อนในการพัฒนาคอนเทนเนอร์ ในขั้นตอนนี้ คุณจะเริ่มต้น Skaffold ซึ่งจะสร้างไฟล์ YAML ของ Kubernetes ฐานโดยอัตโนมัติ กระบวนการนี้จะพยายามระบุไดเรกทอรีที่มีคำจำกัดความของอิมเมจคอนเทนเนอร์ เช่น Dockerfile จากนั้นจะสร้างไฟล์ Manifest ของการทำให้ใช้งานได้และบริการสำหรับแต่ละรายการ

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

d869e0cd38e983d7.png

  1. เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัล
skaffold init --generate-manifests
  1. เมื่อได้รับข้อความแจ้ง ให้ทำดังนี้
  • ใช้ลูกศรเพื่อเลื่อนเคอร์เซอร์ไปที่ Jib Maven Plugin
  • กดแป้นเว้นวรรคเพื่อเลือกตัวเลือก
  • กด Enter เพื่อดำเนินการต่อ
  1. ป้อน 8080 สำหรับพอร์ต
  2. ป้อน y เพื่อบันทึกการกำหนดค่า

เพิ่มไฟล์ 2 ไฟล์ไปยังพื้นที่ทำงาน skaffold.yaml และ deployment.yaml

เอาต์พุต Skaffold:

b33cc1e0c2077ab8.png

อัปเดตชื่อแอป

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

  1. เปลี่ยนรายการในการกำหนดค่า Skaffold
  • เปิด skaffold.yaml
  • เลือกชื่อรูปภาพที่ตั้งเป็น pom-xml-image ในปัจจุบัน
  • คลิกขวาแล้วเลือก "เปลี่ยนทุกรายการ"
  • พิมพ์ชื่อใหม่เป็นภาษาdemo-app
  1. เปลี่ยนรายการในการกำหนดค่า Kubernetes
  • เปิดไฟล์ deployment.yaml
  • เลือกชื่อรูปภาพที่ตั้งเป็น pom-xml-image ในปัจจุบัน
  • คลิกขวาแล้วเลือก "เปลี่ยนทุกรายการ"
  • พิมพ์ชื่อใหม่เป็นภาษาdemo-app

เปิดใช้โหมดซิงค์อัตโนมัติ

คุณจะใช้ฟีเจอร์ซิงค์ที่ Jib มีให้เพื่อมอบประสบการณ์การรีโหลดด่วนที่ได้รับการเพิ่มประสิทธิภาพ ในขั้นตอนนี้ คุณจะกำหนดค่า Skaffold เพื่อใช้ฟีเจอร์ดังกล่าวในกระบวนการบิลด์

โปรดทราบว่าโปรไฟล์ "sync" ที่คุณกําหนดค่าในการกําหนดค่า Skaffold จะใช้ประโยชน์จากโปรไฟล์ "sync" ของ Spring ที่คุณกําหนดค่าไว้ในขั้นตอนก่อนหน้า ซึ่งคุณได้เปิดใช้การรองรับ spring-dev-tools

  1. อัปเดตการกำหนดค่า Skaffold

ในไฟล์ skaffold.yaml ให้แทนที่ทั้งส่วนบิลด์ของไฟล์ด้วยข้อกำหนดต่อไปนี้ อย่าแก้ไขส่วนอื่นๆ ของไฟล์

skaffold.yaml

build:
  artifacts:
  - image: demo-app
    jib:
      project: com.example:demo
      type: maven
      args: 
      - --no-transfer-progress
      - -Psync
      fromImage: gcr.io/distroless/java17-debian11:debug
    sync:
      auto: true

เพิ่มเส้นทางเริ่มต้น

สร้างไฟล์ชื่อ HelloController.java ในโฟลเดอร์ /src/main/java/com/example/springboot/

a624f5dd0c477c09.png

วางเนื้อหาต่อไปนี้ในไฟล์เพื่อสร้างเส้นทาง http เริ่มต้น

HelloController.java

package com.example.springboot;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;

@RestController
public class HelloController {

    @Value("${target:local}")
    String target;

    @GetMapping("/") 
    public String hello()
    {
        return String.format("Hello from your %s environment!", target);
    }
}

4. การเดินผ่านกระบวนการพัฒนา

ในส่วนนี้ คุณจะได้ทำตามขั้นตอน 2-3 ขั้นตอนโดยใช้ปลั๊กอิน Cloud Code เพื่อเรียนรู้กระบวนการพื้นฐาน รวมถึงตรวจสอบการกำหนดค่าและการตั้งค่าของแอปพลิเคชันเริ่มต้น

Cloud Code ผสานรวมกับ Skaffold เพื่อเพิ่มประสิทธิภาพกระบวนการพัฒนา เมื่อคุณทำให้ใช้งานได้ใน GKE ในขั้นตอนต่อไปนี้ Cloud Code และ Skaffold จะบิลด์อิมเมจคอนเทนเนอร์ พุชไปยัง Container Registry แล้วทำให้ใช้งานได้แอปพลิเคชันใน GKE โดยอัตโนมัติ ซึ่งจะเกิดขึ้นเบื้องหลังโดยซ่อนรายละเอียดจากขั้นตอนการทำงานของนักพัฒนาแอป นอกจากนี้ Cloud Code ยังช่วยเพิ่มประสิทธิภาพกระบวนการพัฒนาด้วยการมอบความสามารถในการแก้ไขข้อบกพร่องและการซิงค์ด่วนแบบเดิมให้กับการพัฒนาที่อิงตามคอนเทนเนอร์

ลงชื่อเข้าใช้ Google Cloud

คลิกไอคอน Cloud Code แล้วเลือก "ลงชื่อเข้าใช้ Google Cloud"

1769afd39be372ff.png

คลิก "ไปลงชื่อเข้าใช้"

923bb1c8f63160f9.png

ตรวจสอบเอาต์พุตในเทอร์มินัลและเปิดลิงก์

517fdd579c34aa21.png

เข้าสู่ระบบด้วยข้อมูลเข้าสู่ระบบของนักเรียน Qwiklabs

db99b345f7a8e72c.png

เลือก "อนุญาต"

a5376553c430ac84.png

คัดลอกรหัสยืนยันแล้วกลับไปที่แท็บเวิร์กสเตชัน

6719421277b92eac.png

วางรหัสยืนยันแล้วกด Enter

e9847cfe3fa8a2ce.png

เพิ่มคลัสเตอร์ Kubernetes

  1. เพิ่มคลัสเตอร์

62a3b97bdbb427e5.png

  1. เลือก Google Kubernetes Engine

9577de423568bbaa.png

  1. เลือกโปรเจ็กต์

c5202fcbeebcd41c.png

  1. เลือก "quote-cluster" ที่สร้างขึ้นในการตั้งค่าเริ่มต้น

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

ตั้งค่ารหัสโปรเจ็กต์ปัจจุบันโดยใช้ gcloud CLI

คัดลอกรหัสโปรเจ็กต์สำหรับ Lab นี้จากหน้า Qwiklabs

fcff2d10007ec5bc.png

เรียกใช้คำสั่ง gcloud CLI เพื่อตั้งค่ารหัสโปรเจ็กต์ แทนที่รหัสโปรเจ็กต์ตัวอย่างก่อนเรียกใช้คำสั่ง

gcloud config set project qwiklabs-gcp-project-id

ตัวอย่างเอาต์พุต

f1c03d01b7ac112c.png

แก้ไขข้อบกพร่องใน Kubernetes

  1. ที่แผงด้านซ้าย ให้เลือก Cloud Code ที่ด้านล่าง

60b8e4e95868b561.png

  1. ในแผงที่ปรากฏในส่วน DEVELOPMENT SESSIONS ให้เลือก Debug on Kubernetes

เลื่อนลงหากไม่เห็นตัวเลือกนี้

7d30833d96632ca0.png

  1. เลือก "ใช่" เพื่อใช้บริบทปัจจุบัน

a024a69b64de7e9e.png

  1. เลือก "quote-cluster" ที่สร้างขึ้นในระหว่างการตั้งค่าครั้งแรก

faebabf372e3caf0.png

  1. เลือกที่เก็บคอนเทนเนอร์

fabc6dce48bae1b4.png

  1. เลือกแท็บเอาต์พุตในบานหน้าต่างด้านล่างเพื่อดูความคืบหน้าและการแจ้งเตือน
  2. เลือก "Kubernetes: เรียกใช้/แก้ไขข้อบกพร่อง - โดยละเอียด" ในเมนูแบบเลื่อนลงของช่องทางทางด้านขวาเพื่อดูรายละเอียดเพิ่มเติมและบันทึกที่สตรีมแบบสดจากคอนเทนเนอร์

86b44c59db58f8f3.png

รอให้ระบบติดตั้งใช้งานแอปพลิเคชัน

9f37706a752829fe.png

  1. ตรวจสอบแอปพลิเคชันที่ทําให้ใช้งานได้ใน GKE ใน Cloud Console

6ad220e5d1980756.png

  1. กลับไปที่มุมมองแบบง่ายโดยเลือก "Kubernetes: เรียกใช้/แก้ไขข้อบกพร่อง" จากเมนูแบบเลื่อนลงในแท็บเอาต์พุต
  2. เมื่อการสร้างและการทดสอบเสร็จสมบูรณ์ แท็บเอาต์พุตจะแสดง Resource deployment/demo-app status completed successfully และ URL ที่ระบุคือ "Forwarded URL from service demo-app: http://localhost:8080"
  3. ในเทอร์มินัล Cloud Code ให้วางเมาส์เหนือ URL ในเอาต์พุต (http://localhost:8080) จากนั้นเลือก "ติดตามลิงก์" ในเคล็ดลับเครื่องมือที่ปรากฏขึ้น

28c5539880194a8e.png

แท็บใหม่จะเปิดขึ้นและคุณจะเห็นเอาต์พุตด้านล่าง

d67253ca16238f49.png

ใช้เบรกพอยท์

  1. เปิดแอปพลิเคชัน HelloController.java ที่ /src/main/java/com/example/springboot/HelloController.java
  2. ค้นหาคำสั่ง "return" สำหรับเส้นทางรูทซึ่งอ่านว่า return String.format("Hello from your %s environment!", target);
  3. เพิ่มเบรกพอยต์ในบรรทัดนั้นโดยคลิกพื้นที่ว่างทางด้านซ้ายของหมายเลขบรรทัด ตัวบ่งชี้สีแดงจะแสดงขึ้นเพื่อระบุว่าได้ตั้งค่าเบรกพอยต์แล้ว

5027dc6da2618a39.png

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

71acfb426623cec2.png

  1. คลิกส่วนตัวแปรลงไปจนกว่าจะพบตัวแปร "เป้าหมาย"
  2. สังเกตค่าปัจจุบันเป็น "local"

a1160d2ed2bb5c82.png

  1. ดับเบิลคลิกที่ชื่อตัวแปร "target" และในป๊อปอัป

เปลี่ยนค่าเป็น "Cloud Workstations"

e597a556a5c53f32.png

  1. คลิกปุ่ม "ดำเนินการต่อ" ในแผงควบคุมการแก้ไขข้อบกพร่อง

ec17086191770d0d.png

  1. ตรวจสอบการตอบกลับในเบราว์เซอร์ ซึ่งตอนนี้จะแสดงค่าที่อัปเดตที่คุณเพิ่งป้อน

6698a9db9e729925.png

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

Hot Reload

  1. เปลี่ยนคำสั่งให้แสดงค่าอื่น เช่น "สวัสดีจาก %s Code"
  2. ระบบจะบันทึกและซิงค์ไฟล์ไปยังคอนเทนเนอร์ระยะไกลใน GKE โดยอัตโนมัติ
  3. โปรดรีเฟรชเบราว์เซอร์เพื่อดูผลลัพธ์ที่อัปเดต
  4. หยุดเซสชันการแก้ไขข้อบกพร่องโดยคลิกสี่เหลี่ยมสีแดงในแถบเครื่องมือแก้ไขข้อบกพร่อง

a541f928ec8f430e.png c2752bb28d82af86.png

เลือก "ใช่ ล้างข้อมูลหลังการเรียกใช้แต่ละครั้ง"

984eb2fa34867d70.png

5. การพัฒนาบริการ REST แบบ CRUD อย่างง่าย

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

กำหนดค่าการอ้างอิง

โค้ดของแอปพลิเคชันใช้ฐานข้อมูลเพื่อจัดเก็บข้อมูลบริการ REST ตรวจสอบว่าทรัพยากร Dependency พร้อมใช้งานโดยเพิ่มข้อมูลต่อไปนี้ใน pom.xl

  1. เปิดไฟล์ pom.xml แล้วเพิ่มข้อมูลต่อไปนี้ลงในส่วนการขึ้นต่อกันของการกำหนดค่า

pom.xml

    <!--  Database dependencies-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-core</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>javax.persistence-api</artifactId>
      <version>2.2</version>
    </dependency>

บริการ REST ของโค้ด

Quote.java

สร้างไฟล์ชื่อ Quote.java ใน /src/main/java/com/example/springboot/ แล้วคัดลอกโค้ดด้านล่าง ซึ่งกำหนดโมเดลเอนทิตีสำหรับออบเจ็กต์ใบเสนอราคาที่ใช้ในแอปพลิเคชัน

package com.example.springboot;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import java.util.Objects;

@Entity
@Table(name = "quotes")
public class Quote
{
    @Id
    @Column(name = "id")
    private Integer id;

    @Column(name="quote")
    private String quote;

    @Column(name="author")
    private String author;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
        Quote quote1 = (Quote) o;
        return Objects.equals(id, quote1.id) &&
                Objects.equals(quote, quote1.quote) &&
                Objects.equals(author, quote1.author);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, quote, author);
    }
}

QuoteRepository.java

สร้างไฟล์ชื่อ QuoteRepository.java ที่ src/main/java/com/example/springboot แล้วคัดลอกโค้ดต่อไปนี้

package com.example.springboot;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface QuoteRepository extends JpaRepository<Quote,Integer> {

    @Query( nativeQuery = true, value =
            "SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
    Quote findRandomQuote();
}

โค้ดนี้ใช้ JPA เพื่อบันทึกข้อมูล คลาสนี้ขยายอินเทอร์เฟซของ Spring JPARepository และอนุญาตให้สร้างโค้ดที่กำหนดเอง ในโค้ดที่คุณเพิ่มfindRandomQuoteเมธอดที่กำหนดเอง

QuoteController.java

หากต้องการแสดงอุปกรณ์ปลายทางสำหรับบริการ QuoteController คลาสจะให้ฟังก์ชันนี้

สร้างไฟล์ชื่อ QuoteController.java ที่ src/main/java/com/example/springboot แล้วคัดลอกเนื้อหาต่อไปนี้

package com.example.springboot;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class QuoteController {

    private final QuoteRepository quoteRepository;

    public QuoteController(QuoteRepository quoteRepository) {
        this.quoteRepository = quoteRepository;
    }

    @GetMapping("/random-quote") 
    public Quote randomQuote()
    {
        return quoteRepository.findRandomQuote();  
    }

    @GetMapping("/quotes") 
    public ResponseEntity<List<Quote>> allQuotes()
    {
        try {
            List<Quote> quotes = new ArrayList<Quote>();
            
            quoteRepository.findAll().forEach(quotes::add);

            if (quotes.size()==0 || quotes.isEmpty()) 
                return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
                
            return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
        }        
    }

    @PostMapping("/quotes")
    public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
        try {
            Quote saved = quoteRepository.save(quote);
            return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }     

    @PutMapping("/quotes/{id}")
    public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
        try {
            Optional<Quote> existingQuote = quoteRepository.findById(id);
            
            if(existingQuote.isPresent()){
                Quote updatedQuote = existingQuote.get();
                updatedQuote.setAuthor(quote.getAuthor());
                updatedQuote.setQuote(quote.getQuote());

                return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
            } else {
                return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }     

    @DeleteMapping("/quotes/{id}")
    public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        Optional<Quote> quote = quoteRepository.findById(id);
        if (quote.isPresent()) {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } else {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

}

เพิ่มการกำหนดค่าฐานข้อมูล

application.yaml

เพิ่มการกำหนดค่าสำหรับฐานข้อมูลแบ็กเอนด์ที่บริการเข้าถึง แก้ไข (หรือสร้างหากไม่มี) ไฟล์ที่ชื่อ application.yaml ใน src/main/resources แล้วเพิ่มการกำหนดค่า Spring ที่กำหนดพารามิเตอร์สำหรับแบ็กเอนด์

target: local

spring:
  config:
    activate:
      on-profile: cloud-dev
  datasource:
    url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
    username: '${DB_USER:user}'
    password: '${DB_PASS:password}'
  jpa:
    properties:
      hibernate:
        jdbc:
          lob:
            non_contextual_creation: true
        dialect: org.hibernate.dialect.PostgreSQLDialect
    hibernate:
      ddl-auto: update

เพิ่มการย้ายฐานข้อมูล

สร้างโฟลเดอร์ db/migration ใน src/main/resources

สร้างไฟล์ SQL: V1__create_quotes_table.sql

วางเนื้อหาต่อไปนี้ลงในไฟล์

V1__create_quotes_table.sql

CREATE TABLE quotes(
   id INTEGER PRIMARY KEY,
   quote VARCHAR(1024),
   author VARCHAR(256)
);

INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');

การกำหนดค่า Kubernetes

การเพิ่มต่อไปนี้ในไฟล์ deployment.yaml จะช่วยให้แอปพลิเคชันเชื่อมต่อกับอินสแตนซ์ CloudSQL ได้

  • TARGET - กำหนดค่าตัวแปรเพื่อระบุสภาพแวดล้อมที่แอปทำงาน
  • SPRING_PROFILES_ACTIVE - แสดงโปรไฟล์ Spring ที่ใช้งานอยู่ ซึ่งจะได้รับการกำหนดค่าเป็น cloud-dev
  • DB_HOST - IP ส่วนตัวสำหรับฐานข้อมูล ซึ่งได้บันทึกไว้เมื่อสร้างอินสแตนซ์ฐานข้อมูล หรือโดยคลิก SQL ในเมนูการนำทางของ คอนโซล Google Cloud - โปรดเปลี่ยนค่า
  • DB_USER และ DB_PASS - ตามที่ตั้งค่าไว้ในการกำหนดค่าอินสแตนซ์ CloudSQL ซึ่งจัดเก็บเป็น Secret ใน GCP

อัปเดต deployment.yaml ด้วยเนื้อหาด้านล่าง

deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  ports:
  - port: 8080
    protocol: TCP
  clusterIP: None
  selector:
    app: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  labels:
    app: demo-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: demo-app
        image: demo-app
        env:
          - name: PORT
            value: "8080"
          - name: TARGET
            value: "Local Dev - CloudSQL Database - K8s Cluster"
          - name: SPRING_PROFILES_ACTIVE
            value: cloud-dev
          - name: DB_HOST
            value: ${DB_INSTANCE_IP}   
          - name: DB_PORT
            value: "5432"  
          - name: DB_USER
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: username
          - name: DB_PASS
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: password
          - name: DB_NAME
            valueFrom:
              secretKeyRef:
                name: gke-cloud-sql-secrets
                key: database

แทนที่ค่า DB_HOST ด้วยที่อยู่ของฐานข้อมูลโดยเรียกใช้คำสั่งด้านล่างในเทอร์มินัล

export DB_INSTANCE_IP=$(gcloud sql instances describe quote-db-instance \
    --format=json | jq \
    --raw-output ".ipAddresses[].ipAddress")

envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml

เปิด deployment.yaml และตรวจสอบว่าค่า DB_HOST ได้รับการอัปเดตด้วย IP ของอินสแตนซ์แล้ว

fd63c0aede14beba.png

ทำให้แอปพลิเคชันใช้งานได้และตรวจสอบ

  1. ในแผงที่ด้านล่างของ Cloud Shell Editor ให้เลือก Cloud Code แล้วเลือกแก้ไขข้อบกพร่องใน Kubernetes ที่ด้านบนของหน้าจอ

33a5cf41aae91adb.png

  1. เมื่อการสร้างและการทดสอบเสร็จสมบูรณ์ แท็บเอาต์พุตจะแสดง Resource deployment/demo-app status completed successfully และ URL ที่ระบุคือ "Forwarded URL from service demo-app: http://localhost:8080" โปรดทราบว่าบางครั้งพอร์ตอาจแตกต่างกัน เช่น 8081 หากเป็นเช่นนั้น ให้ตั้งค่าที่เหมาะสม ตั้งค่า URL ในเทอร์มินัล
export URL=localhost:8080
  1. ดูคำคมแบบสุ่ม

จากเทอร์มินัล ให้เรียกใช้คำสั่งด้านล่างหลายครั้งกับปลายทาง random-quote สังเกตการเรียกซ้ำที่แสดงราคาที่แตกต่างกัน

curl $URL/random-quote | jq
  1. เพิ่มคำพูด

สร้างใบเสนอราคาใหม่โดยมีรหัส=6 โดยใช้คำสั่งที่ระบุไว้ด้านล่างและสังเกตคำขอที่ส่งกลับมา

curl -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST $URL/quotes
  1. ลบใบเสนอราคา

ตอนนี้ให้ลบคำพูดที่คุณเพิ่งเพิ่มด้วยเมธอดลบและสังเกตโค้ดตอบกลับ HTTP/1.1 204

curl -v -X DELETE $URL/quotes/6
  1. เกิดข้อผิดพลาดที่เซิร์ฟเวอร์

พบสถานะข้อผิดพลาดโดยการเรียกใช้คำขอล่าสุดอีกครั้งหลังจากลบรายการไปแล้ว

curl -v -X DELETE $URL/quotes/6

โปรดสังเกตว่าการตอบกลับจะแสดง HTTP:500 Internal Server Error

แก้ไขข้อบกพร่องของแอปพลิเคชัน

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

  1. เปิด src/main/java/com/example/springboot/QuoteController.java
  2. ค้นหาวิธี deleteQuote()
  3. ค้นหาบรรทัดต่อไปนี้ Optional<Quote> quote = quoteRepository.findById(id);
  4. ตั้งค่าเบรกพอยต์ในบรรทัดนั้นโดยคลิกพื้นที่ว่างทางด้านซ้ายของหมายเลขบรรทัด
  5. ตัวบ่งชี้สีแดงจะปรากฏขึ้นเพื่อระบุว่าได้ตั้งค่าเบรกพอยต์แล้ว
  6. เรียกใช้คำสั่ง delete อีกครั้ง
curl -v -X DELETE $URL/quotes/6
  1. เปลี่ยนกลับไปใช้มุมมองการแก้ไขข้อบกพร่องโดยคลิกไอคอนในคอลัมน์ด้านซ้าย
  2. สังเกตบรรทัดการแก้ไขข้อบกพร่องที่หยุดในคลาส QuoteController
  3. ในดีบักเกอร์ ให้คลิกไอคอน step over b814d39b2e5f3d9e.png
  4. สังเกตว่าโค้ดแสดงข้อผิดพลาด HTTP 500 Internal Server Error ต่อไคลเอ็นต์ ซึ่งไม่เหมาะสม
   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /quotes/6 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 500
< Content-Length: 0
< Date: 
<
* Connection #0 to host 127.0.0.1 left intact

อัปเดตรหัส

รหัสไม่ถูกต้องและควรปรับโครงสร้างบล็อก else เพื่อส่งรหัสสถานะ HTTP 404 Not Found กลับ

แก้ไขข้อผิดพลาด

  1. ขณะที่เซสชันการแก้ไขข้อบกพร่องยังทำงานอยู่ ให้ดำเนินการตามคำขอให้เสร็จสมบูรณ์โดยกดปุ่ม "ดำเนินการต่อ" ในแผงควบคุมการแก้ไขข้อบกพร่อง
  2. จากนั้นเปลี่ยนบล็อก else เป็นโค้ดต่อไปนี้
       else {
                return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
            }

วิธีการควรมีลักษณะดังนี้

@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        Optional<Quote> quote = quoteRepository.findById(id);
        if (quote.isPresent()) {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } else {
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }
    }
  1. เรียกใช้คำสั่งลบอีกครั้ง
curl -v -X DELETE $URL/quotes/6
  1. ทำตามขั้นตอนในดีบักเกอร์และสังเกตข้อความ "ไม่พบ HTTP 404" ที่ส่งกลับไปยังผู้โทร
   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /quotes/6 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404
< Content-Length: 0
< Date: 
<
* Connection #0 to host 127.0.0.1 left intact
  1. หยุดเซสชันการแก้ไขข้อบกพร่องโดยคลิกสี่เหลี่ยมสีแดงในแถบเครื่องมือแก้ไขข้อบกพร่อง

12bc3c82f63dcd8a.png

6f19c0f855832407.png

6. ขอแสดงความยินดี

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

สิ่งที่คุณได้เรียนรู้

  • การพัฒนา InnerLoop ด้วย Cloud Workstations
  • การสร้างแอปพลิเคชันเริ่มต้น Java ใหม่
  • การเดินผ่านกระบวนการพัฒนา
  • การพัฒนาบริการ REST แบบ CRUD อย่างง่าย
  • การแก้ไขข้อบกพร่องของแอปพลิเคชันในคลัสเตอร์ GKE
  • การเชื่อมต่อแอปพลิเคชันกับฐานข้อมูล Cloud SQL