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

1. ภาพรวม

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

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

ในห้องทดลองนี้ คุณจะได้เรียนรู้วิธีการพัฒนาด้วยคอนเทนเนอร์ใน GCP ดังต่อไปนี้

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

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 ของระบบคลาวด์ การใช้งาน Codelab นี้น่าจะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบทั้งโปรเจ็กต์ได้ ผู้ใช้ใหม่ของ Google Cloud จะมีสิทธิ์เข้าร่วมโปรแกรมทดลองใช้ฟรี$300 USD

เริ่มผู้แก้ไข Cloudshell

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

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

8560cc8d45e8c112.png

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

9e504cb98a6a8005.png

  1. ตัวแก้ไขจะเปิดขึ้นโดยมีนักสำรวจอยู่ด้านขวา และเอดิเตอร์จะเปิดขึ้นตรงกลาง
  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-shop ใน GoogleCloudPlatform บน GitHub โคลนไฟล์ด้วยคำสั่งด้านล่างแล้วเปลี่ยนเป็นไดเรกทอรี

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

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

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

./setup_with_cw.sh &

คลัสเตอร์เวิร์กสเตชันของระบบคลาวด์

เปิด 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 นาทีในการเปลี่ยนสถานะเป็น READY

7a6af5aa2807a5f2.png

เปิดเวิร์กสเตชันระบบคลาวด์ในคอนโซลและสร้างอินสแตนซ์ใหม่

a53adeeac81a78c8.png

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

f21c216997746097.png

ยืนยันผลลัพธ์ในส่วนเวิร์กสเตชัน

66a9fc8b20543e32.png

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

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

c91bb69b61ec8635.png

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

fcf9405b6957b7d7.png

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

36a84c0e2e3b85b.png

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

2259694328628fba.png

เมื่อเปิดตัวเวิร์กสเตชันแล้ว คุณจะเห็น Code OSS IDE ปรากฏขึ้น คลิก "ทำเครื่องหมายว่าเสร็จสิ้น" ในหน้าเริ่มต้นใช้งาน 1 เวิร์กสเตชัน 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. เปิด "แอปตัวอย่าง" โฟลเดอร์
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

เพิ่ม Spring-boot-devtools และ จิบ

หากต้องการเปิดใช้ Spring Boot DevTools ให้ค้นหาและเปิด pom.xml จากเครื่องมือสำรวจในตัวแก้ไข จากนั้นวางโค้ดต่อไปนี้หลังบรรทัดรายละเอียดที่เขียนว่า <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-plugin ใน 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 ซึ่งจะสร้างไฟล์ Kubernetes YAML พื้นฐานโดยอัตโนมัติ กระบวนการนี้จะพยายามระบุไดเรกทอรีที่มีการกำหนดอิมเมจคอนเทนเนอร์ เช่น 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

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

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

โปรดทราบว่า "การซิงค์" โปรไฟล์ที่คุณกำหนดค่าในการกำหนดค่า Skaffold จะใช้ประโยชน์จาก "การซิงค์" ของ 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 ยังปรับปรุงกระบวนการพัฒนาโดยมอบความสามารถในการแก้ไขข้อบกพร่องและ Hotsync แบบดั้งเดิมให้แก่การพัฒนาตามคอนเทนเนอร์

ลงชื่อเข้าใช้ 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. เลือก "คลัสเตอร์ใบเสนอราคา" ที่สร้างขึ้นในการตั้งค่าเริ่มต้น

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

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

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

fcff2d10007ec5bc.png

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

gcloud config set project qwiklabs-gcp-project-id

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

f1c03d01b7ac112c.png

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

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

60b8e4e95868b561.png

  1. ในแผงที่ปรากฏใต้เซสชันการพัฒนา ให้เลือก "แก้ไขข้อบกพร่องบน Kubernetes"

เลื่อนลงหากตัวเลือกไม่ปรากฏ

7d30833d96632ca0.png

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

a024a69b64de7e9e.png

  1. เลือก "คลัสเตอร์ใบเสนอราคา" ที่สร้างขึ้นระหว่างการตั้งค่าเริ่มต้น

faebabf372e3caf0.png

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

fabc6dce48bae1b4.png

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

86b44c59db58f8f3.png

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

9f37706a752829fe.png

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

6ad220e5d1980756.png

  1. กลับไปที่มุมมองแบบง่ายโดยเลือก "Kubernetes: เรียกใช้/แก้ไขข้อบกพร่อง" จากเมนูแบบเลื่อนลงในแท็บ OUTPUT
  2. เมื่อบิลด์และทดสอบเสร็จสิ้น แท็บเอาต์พุตจะระบุว่า Resource deployment/demo-app status completed successfully และ URL ที่ส่งต่อมาจากแอปเดโมบริการ: 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 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

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

a541f928ec8f430e.png c2752bb28d82af86.png

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

984eb2fa34867d70.png

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

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

กำหนดค่าการขึ้นต่อกัน

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

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

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 ได้

  • เป้าหมาย - กำหนดค่าตัวแปรเพื่อระบุสภาพแวดล้อมที่มีการเรียกใช้แอป
  • SPRING_PROFILES_ACTIVE - แสดงโปรไฟล์ Spring ที่ใช้งานอยู่ ซึ่งจะกำหนดค่าให้เป็น cloud-dev
  • DB_HOST - IP ส่วนตัวสำหรับฐานข้อมูล ซึ่งได้บันทึกไว้เมื่อสร้างอินสแตนซ์ฐานข้อมูลแล้ว หรือคลิก SQL ในเมนูนำทางของ Google Cloud Console โปรดเปลี่ยนค่า !
  • DB_USER และ DB_PASS - ตามที่ตั้งค่าไว้ในการกำหนดค่าอินสแตนซ์ CloudSQL โดยจัดเก็บเป็นข้อมูลลับใน 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 แสดงขึ้นว่า "URL ที่ส่งต่อจากแอปเดโมบริการ: http://localhost:8080" โปรดทราบว่าบางครั้งพอร์ตอาจแตกต่างกัน เช่น 8081 หากใช่ ให้ตั้งค่าที่เหมาะสม ตั้งค่าของ URL ในเทอร์มินัล
export URL=localhost:8080
  1. ดูใบเสนอราคาแบบสุ่ม

จากเทอร์มินัล ให้เรียกใช้คำสั่งด้านล่างหลายๆ ครั้งกับปลายทางข้อความที่ยกมาแบบสุ่ม สังเกตการโทรซ้ำๆ ที่ส่งคืนใบเสนอราคาที่แตกต่างกัน

curl $URL/random-quote | jq
  1. ใส่ใบเสนอราคา

สร้างใบเสนอราคาใหม่ด้วย id=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 เพื่อให้คุณทำงานกับคลาส BidController ได้

  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. สังเกตบรรทัดแก้ไขข้อบกพร่องที่หยุดไว้ในคลาส BidController
  3. ในโปรแกรมแก้ไขข้อบกพร่อง ให้คลิกไอคอน step over b814d39b2e5f3d9e.png
  4. สังเกตว่าโค้ดส่งคืน HTTP 500 ข้อผิดพลาดเซิร์ฟเวอร์ภายในไปยังไคลเอ็นต์ซึ่งไม่เหมาะ
   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 ไม่พบ

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

  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 Not Found ส่งกลับมาให้ผู้เรียกใช้
   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 ใหม่
  • แนะนำกระบวนการพัฒนา
  • การพัฒนาบริการ CRUD REST แบบง่าย
  • การแก้ไขข้อบกพร่องของแอปพลิเคชันบนคลัสเตอร์ GKE
  • กำลังเชื่อมต่อแอปพลิเคชันกับฐานข้อมูล CloudSQL