1. ภาพรวม
ห้องทดลองนี้แสดงให้เห็นฟีเจอร์และความสามารถที่ออกแบบมาเพื่อปรับปรุงเวิร์กโฟลว์การพัฒนาสำหรับวิศวกรซอฟต์แวร์ที่ได้รับมอบหมายให้พัฒนาแอปพลิเคชัน Java ในสภาพแวดล้อมที่สร้างโดยใช้คอนเทนเนอร์ การพัฒนาคอนเทนเนอร์ทั่วไปกำหนดให้ผู้ใช้ทำความเข้าใจรายละเอียดของคอนเทนเนอร์และกระบวนการสร้างคอนเทนเนอร์ นอกจากนี้ นักพัฒนาซอฟต์แวร์มักต้องแยกขั้นตอนของตนเองโดยย้ายออกจาก IDE เพื่อทดสอบและแก้ไขข้อบกพร่องแอปพลิเคชันในสภาพแวดล้อมระยะไกล ด้วยเครื่องมือและเทคโนโลยีที่กล่าวถึงในบทแนะนำนี้ นักพัฒนาซอฟต์แวร์สามารถทำงานได้อย่างมีประสิทธิภาพกับแอปพลิเคชันที่สร้างโดยใช้คอนเทนเนอร์โดยไม่ต้องออกจาก IDE
สิ่งที่คุณจะได้เรียนรู้
ในห้องทดลองนี้ คุณจะได้เรียนรู้วิธีการพัฒนาด้วยคอนเทนเนอร์ใน GCP ดังต่อไปนี้
- การพัฒนา InnerLoop ด้วย Cloud Workstations
- การสร้างแอปพลิเคชันเริ่มต้นสำหรับ Java ใหม่
- แนะนำกระบวนการพัฒนา
- การพัฒนาบริการ CRUD REST แบบง่าย
- การแก้ไขข้อบกพร่องของแอปพลิเคชันบนคลัสเตอร์ GKE
- กำลังเชื่อมต่อแอปพลิเคชันกับฐานข้อมูล CloudSQL
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก
- ลงชื่อเข้าใช้ Google Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี
- ชื่อโครงการคือชื่อที่แสดงของผู้เข้าร่วมโปรเจ็กต์นี้ เป็นสตริงอักขระที่ Google APIs ไม่ได้ใช้ โดยคุณจะอัปเดตได้ทุกเมื่อ
- รหัสโปรเจ็กต์จะไม่ซ้ำกันในทุกโปรเจ็กต์ของ Google Cloud และจะเปลี่ยนแปลงไม่ได้ (เปลี่ยนแปลงไม่ได้หลังจากตั้งค่าแล้ว) Cloud Console จะสร้างสตริงที่ไม่ซ้ำกันโดยอัตโนมัติ ปกติแล้วคุณไม่สนว่าอะไรเป็นอะไร ใน Codelab ส่วนใหญ่ คุณจะต้องอ้างอิงรหัสโปรเจ็กต์ (โดยปกติจะระบุเป็น
PROJECT_ID
) หากคุณไม่ชอบรหัสที่สร้างขึ้น คุณสามารถสร้างรหัสแบบสุ่มอื่นได้ หรือคุณจะลองดำเนินการเองแล้วดูว่าพร้อมให้บริการหรือไม่ และไม่สามารถเปลี่ยนแปลงได้หลังจากขั้นตอนนี้และจะยังคงอยู่ตลอดระยะเวลาของโปรเจ็กต์ - สำหรับข้อมูลของคุณ ค่าที่ 3 คือหมายเลขโปรเจ็กต์ที่ API บางตัวใช้ ดูข้อมูลเพิ่มเติมเกี่ยวกับค่าทั้ง 3 ค่าได้ในเอกสารประกอบ
- ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร/API ของระบบคลาวด์ การใช้งาน Codelab นี้น่าจะไม่มีค่าใช้จ่ายใดๆ หากมี หากต้องการปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ คุณสามารถลบทรัพยากรที่คุณสร้างหรือลบทั้งโปรเจ็กต์ได้ ผู้ใช้ใหม่ของ Google Cloud จะมีสิทธิ์เข้าร่วมโปรแกรมทดลองใช้ฟรี$300 USD
เริ่มผู้แก้ไข Cloudshell
ห้องทดลองนี้ออกแบบและทดสอบเพื่อใช้กับ Google Cloud Shell Editor วิธีเข้าถึงเครื่องมือแก้ไข
- เข้าถึงโปรเจ็กต์ Google ที่ https://console.cloud.google.com
- คลิกไอคอนตัวแก้ไข Cloud Shell ที่มุมขวาบน
- บานหน้าต่างใหม่จะเปิดขึ้นที่ด้านล่างของหน้าต่าง
- คลิกปุ่ม "เปิดเครื่องมือแก้ไข"
- ตัวแก้ไขจะเปิดขึ้นโดยมีนักสำรวจอยู่ด้านขวา และเอดิเตอร์จะเปิดขึ้นตรงกลาง
- ควรจะมีแผงเทอร์มินัลที่ด้านล่างของหน้าจอด้วย
- หากเทอร์มินัลไม่ได้เปิด ให้ใช้คีย์ผสม "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
สร้างการกำหนดค่าเวิร์กสเตชัน
หากเซสชัน 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
เปิดเวิร์กสเตชันระบบคลาวด์ในคอนโซลและสร้างอินสแตนซ์ใหม่
เปลี่ยนชื่อเป็น my-workstation
และเลือกการกำหนดค่าที่มีอยู่: codeoss-java
ยืนยันผลลัพธ์ในส่วนเวิร์กสเตชัน
เปิดเวิร์กสเตชัน
เริ่มและเปิดตัวเวิร์กสเตชัน
อนุญาตคุกกี้ของบุคคลที่สามได้โดยคลิกไอคอนในแถบที่อยู่
คลิก "เว็บไซต์ไม่ทำงาน?"
คลิก "อนุญาตคุกกี้"
เมื่อเปิดตัวเวิร์กสเตชันแล้ว คุณจะเห็น Code OSS IDE ปรากฏขึ้น คลิก "ทำเครื่องหมายว่าเสร็จสิ้น" ในหน้าเริ่มต้นใช้งาน 1 เวิร์กสเตชัน IDE
3. การสร้างแอปพลิเคชันเริ่มต้นสำหรับ Java ใหม่
ในส่วนนี้ คุณจะได้สร้างแอปพลิเคชัน Java Spring Boot ใหม่ตั้งแต่ต้นโดยใช้แอปพลิเคชันตัวอย่างจาก spring.io เปิดเทอร์มินัลใหม่
โคลนแอปพลิเคชันตัวอย่าง
- สร้างแอปพลิเคชันเริ่มต้น
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
คลิกปุ่ม "อนุญาต" หากคุณเห็นข้อความนี้ เพื่อให้คุณสามารถคัดลอกและวางลงในเวิร์กสเตชันได้
- แตกไฟล์ซิปแอปพลิเคชัน
unzip sample-app.zip -d sample-app
- เปิด "แอปตัวอย่าง" โฟลเดอร์
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>
- เพิ่ม 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>
- เปิดใช้ 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 ของการทำให้ใช้งานได้และบริการสำหรับแต่ละรายการ
เรียกใช้คำสั่งด้านล่างในเทอร์มินัลเพื่อเริ่มกระบวนการ
- เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัล
skaffold init --generate-manifests
- เมื่อระบบแจ้ง ให้ทำดังนี้
- ใช้ลูกศรเพื่อเลื่อนเคอร์เซอร์ไปที่
Jib Maven Plugin
- กดแป้นเว้นวรรคเพื่อเลือกตัวเลือก
- กด Enter เพื่อดำเนินการต่อ
- ป้อน 8080 ของพอร์ต
- ป้อน y เพื่อบันทึกการกำหนดค่า
เพิ่มไฟล์ 2 รายการไปยังพื้นที่ทำงาน skaffold.yaml
และ deployment.yaml
แล้ว
เอาต์พุต Skaffold:
อัปเดตชื่อแอป
ค่าเริ่มต้นที่รวมอยู่ในการกำหนดค่าไม่ตรงกับชื่อแอปพลิเคชันของคุณในขณะนี้ อัปเดตไฟล์ให้อ้างอิงถึงชื่อแอปพลิเคชันของคุณแทนค่าเริ่มต้น
- เปลี่ยนรายการในการกำหนดค่า Skaffold
- เปิด
skaffold.yaml
- เลือกชื่อรูปภาพที่ตั้งเป็น
pom-xml-image
ในปัจจุบัน - คลิกขวาและเลือกเปลี่ยนรายการทั้งหมด
- พิมพ์ชื่อใหม่เป็น
demo-app
- เปลี่ยนรายการในการกำหนดค่า Kubernetes
- เปิดไฟล์
deployment.yaml
- เลือกชื่อรูปภาพที่ตั้งเป็น
pom-xml-image
ในปัจจุบัน - คลิกขวาและเลือกเปลี่ยนรายการทั้งหมด
- พิมพ์ชื่อใหม่เป็น
demo-app
เปิดใช้งานโหมดซิงค์อัตโนมัติ
เพื่ออำนวยความสะดวกในการโหลดซ้ำแบบ Hot Load คุณจะต้องใช้ฟีเจอร์การซิงค์ที่ Jib มีให้ ในขั้นตอนนี้ คุณจะต้องกำหนดค่า Skaffold เพื่อใช้ฟีเจอร์ดังกล่าวในกระบวนการบิลด์
โปรดทราบว่า "การซิงค์" โปรไฟล์ที่คุณกำหนดค่าในการกำหนดค่า Skaffold จะใช้ประโยชน์จาก "การซิงค์" ของ Spring โปรไฟล์ที่คุณกำหนดค่าในขั้นตอนก่อนหน้าที่เปิดใช้การรองรับ spring-dev-tools
- อัปเดตการกำหนดค่า 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/
วางเนื้อหาต่อไปนี้ในไฟล์เพื่อสร้างเส้นทาง 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"
คลิก "ดำเนินการต่อเพื่อลงชื่อเข้าใช้"
ตรวจสอบเอาต์พุตในเทอร์มินัลและเปิดลิงก์ดังนี้
เข้าสู่ระบบด้วยข้อมูลรับรองสำหรับนักเรียน Qwiklabs
เลือก "อนุญาต":
คัดลอกรหัสยืนยันและกลับไปที่แท็บเวิร์กสเตชัน
วางรหัสยืนยันแล้วกด Enter
เพิ่มคลัสเตอร์ Kubernetes
- เพิ่มคลัสเตอร์
- เลือก Google Kubernetes Engine:
- เลือกโปรเจ็กต์
- เลือก "คลัสเตอร์ใบเสนอราคา" ที่สร้างขึ้นในการตั้งค่าเริ่มต้น
ตั้งค่ารหัสโปรเจ็กต์ปัจจุบันโดยใช้ gcloud cli
คัดลอกรหัสโปรเจ็กต์สำหรับห้องทดลองนี้จากหน้า qwiklabs
เรียกใช้คำสั่ง gcloud cli เพื่อตั้งค่ารหัสโปรเจ็กต์ โปรดแทนที่รหัสโปรเจ็กต์ตัวอย่างก่อนเรียกใช้คำสั่ง
gcloud config set project qwiklabs-gcp-project-id
ตัวอย่างเอาต์พุต:
แก้ไขข้อบกพร่องใน Kubernetes
- เลือก Cloud Code ในแผงด้านซ้ายที่ด้านล่าง
- ในแผงที่ปรากฏใต้เซสชันการพัฒนา ให้เลือก "แก้ไขข้อบกพร่องบน Kubernetes"
เลื่อนลงหากตัวเลือกไม่ปรากฏ
- เลือก "ใช่" ที่จะใช้บริบทในปัจจุบัน
- เลือก "คลัสเตอร์ใบเสนอราคา" ที่สร้างขึ้นระหว่างการตั้งค่าเริ่มต้น
- เลือกที่เก็บคอนเทนเนอร์
- เลือกแท็บเอาต์พุตในแผงด้านล่างเพื่อดูความคืบหน้าและการแจ้งเตือน
- เลือก "Kubernetes: เรียกใช้/แก้ไขข้อบกพร่อง - แบบละเอียด" ในเมนูแบบเลื่อนลงของช่องทางด้านขวาเพื่อดูรายละเอียดเพิ่มเติม และบันทึกที่สตรีมแบบสดจากคอนเทนเนอร์
รอให้แอปพลิเคชันใช้งานได้
- ตรวจสอบแอปพลิเคชันที่ทำให้ใช้งานได้แล้วบน GKE ใน Cloud Console
- กลับไปที่มุมมองแบบง่ายโดยเลือก "Kubernetes: เรียกใช้/แก้ไขข้อบกพร่อง" จากเมนูแบบเลื่อนลงในแท็บ OUTPUT
- เมื่อบิลด์และทดสอบเสร็จสิ้น แท็บเอาต์พุตจะระบุว่า
Resource deployment/demo-app status completed successfully
และ URL ที่ส่งต่อมาจากแอปเดโมบริการ: http://localhost:8080" - ในเทอร์มินัล Cloud Code ให้วางเมาส์เหนือ URL ในเอาต์พุต (http://localhost:8080) แล้วเลือก "ติดตามลิงก์" ในเคล็ดลับเครื่องมือที่ปรากฏขึ้น
แท็บใหม่จะเปิดขึ้นและคุณจะเห็นเอาต์พุตด้านล่าง:
ใช้เบรกพอยท์
- เปิดแอปพลิเคชัน
HelloController.java
ที่/src/main/java/com/example/springboot/HelloController.java
- ค้นหาคำสั่งส่งกลับสำหรับเส้นทางรูทซึ่งเขียนว่า
return String.format("Hello from your %s environment!", target);
- เพิ่มเบรกพอยท์ในบรรทัดดังกล่าวโดยคลิกที่ช่องว่างทางด้านซ้ายของหมายเลขบรรทัด สัญญาณบอกสถานะสีแดงจะแสดงขึ้นเพื่อระบุว่าตั้งค่าเบรกพอยท์แล้ว
- โหลดเบราว์เซอร์ซ้ำและโปรดทราบว่าโปรแกรมแก้ไขข้อบกพร่องจะหยุดกระบวนการที่เบรกพอยท์และช่วยให้คุณตรวจสอบตัวแปรและสถานะของแอปพลิเคชันที่ทำงานจากระยะไกลใน GKE ได้
- คลิกลงไปยังส่วนตัวแปรจนกว่าจะพบส่วน "เป้าหมาย" ตัวแปร
- สังเกตค่าปัจจุบันเป็น "local"
- ดับเบิลคลิกชื่อตัวแปร "target" และในป๊อปอัป
เปลี่ยนค่าเป็น "Cloud Workstations"
- คลิกปุ่ม ดำเนินการต่อ ในแผงควบคุมการแก้ไขข้อบกพร่อง
- ตรวจสอบคำตอบในเบราว์เซอร์ ซึ่งตอนนี้จะแสดงค่าที่อัปเดตที่คุณเพิ่งป้อน
- นำเบรกพอยท์ออกโดยคลิกสัญญาณบอกสถานะสีแดงทางด้านซ้ายของหมายเลขบรรทัด ซึ่งจะป้องกันไม่ให้โค้ดของคุณหยุดการเรียกใช้ที่บรรทัดนี้เมื่อคุณดำเนินการต่อในห้องทดลองนี้
โหลดซ้ำแบบ Hot
- เปลี่ยนคำสั่งให้คืนค่าอื่น เช่น "สวัสดี จาก %s โค้ด"
- ระบบจะบันทึกและซิงค์ไฟล์กับคอนเทนเนอร์ระยะไกลใน GKE โดยอัตโนมัติ
- โปรดรีเฟรชเบราว์เซอร์เพื่อดูผลการค้นหาที่อัปเดต
- หยุดเซสชันการแก้ไขข้อบกพร่องโดยคลิกสี่เหลี่ยมจัตุรัสสีแดงในแถบเครื่องมือแก้ไขข้อบกพร่อง
เลือก "ใช่ ล้างข้อมูลหลังการเรียกใช้แต่ละครั้ง"
5. การพัฒนาบริการ CRUD REST แบบง่าย
ณ จุดนี้ แอปพลิเคชันของคุณได้รับการกำหนดค่าสำหรับการพัฒนาที่สร้างโดยใช้คอนเทนเนอร์เสร็จสมบูรณ์แล้ว และคุณได้แนะนำเวิร์กโฟลว์การพัฒนาขั้นพื้นฐานด้วย Cloud Code แล้ว ในส่วนต่อไปนี้ คุณจะได้ฝึกฝนสิ่งที่ได้เรียนรู้ด้วยการเพิ่มปลายทางของบริการ REST ที่เชื่อมต่อกับฐานข้อมูลที่มีการจัดการใน Google Cloud
กำหนดค่าการขึ้นต่อกัน
โค้ดของแอปพลิเคชันจะใช้ฐานข้อมูลเพื่อเก็บข้อมูลบริการที่เหลือไว้ ตรวจสอบว่าทรัพยากร Dependency พร้อมใช้งานโดยการเพิ่มรายการต่อไปนี้ใน pom.xl
- เปิดไฟล์
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 ของอินสแตนซ์แล้ว
ทำให้แอปพลิเคชันใช้งานได้และตรวจสอบ
- ในบานหน้าต่างที่ด้านล่างของ Cloud Shell Editor ให้เลือก Cloud Code จากนั้นเลือก "แก้ไขข้อบกพร่องบน Kubernetes" ที่ด้านบนของหน้าจอ
- เมื่อบิลด์และทดสอบเสร็จสิ้น แท็บเอาต์พุตจะแสดงข้อความ
Resource deployment/demo-app status completed successfully
และจะมี URL อยู่ในรายการ "URL ที่ส่งต่อจากแอปเดโมบริการ: http://localhost:8080" โปรดทราบว่าบางครั้งพอร์ตอาจแตกต่างกัน เช่น 8081 หากใช่ ให้ตั้งค่าที่เหมาะสม ตั้งค่าของ URL ในเทอร์มินัล
export URL=localhost:8080
- ดูใบเสนอราคาแบบสุ่ม
จากเทอร์มินัล ให้เรียกใช้คำสั่งด้านล่างหลายๆ ครั้งกับปลายทางข้อความที่ยกมาแบบสุ่ม สังเกตการโทรซ้ำๆ ที่ส่งคืนใบเสนอราคาที่แตกต่างกัน
curl $URL/random-quote | jq
- ใส่ใบเสนอราคา
สร้างใบเสนอราคาใหม่ด้วย 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
- ลบใบเสนอราคา
ตอนนี้ให้ลบใบเสนอราคาที่เพิ่งเพิ่มโดยใช้วิธีการลบและสังเกตโค้ดตอบกลับ HTTP/1.1 204
curl -v -X DELETE $URL/quotes/6
- ข้อผิดพลาดเกี่ยวกับเซิร์ฟเวอร์
พบกับสถานะข้อผิดพลาดโดยการส่งคำขอล่าสุดอีกครั้งหลังจากรายการถูกลบไปแล้ว
curl -v -X DELETE $URL/quotes/6
โปรดทราบว่าการตอบกลับแสดงผล HTTP:500 Internal Server Error
แก้ไขข้อบกพร่องของแอปพลิเคชัน
ในส่วนก่อนหน้านี้ คุณพบสถานะข้อผิดพลาดในแอปพลิเคชันเมื่อคุณพยายามลบรายการที่ไม่อยู่ในฐานข้อมูล ในส่วนนี้ คุณจะได้ตั้งค่าเบรกพอยท์เพื่อค้นหาปัญหา เกิดข้อผิดพลาดในการดำเนินการ DELETE เพื่อให้คุณทำงานกับคลาส BidController ได้
- เปิด
src/main/java/com/example/springboot/QuoteController.java
- ค้นหาเมธอด
deleteQuote()
- ค้นหาเส้น:
Optional<Quote> quote = quoteRepository.findById(id);
- กำหนดเบรกพอยท์ในบรรทัดนั้นด้วยการคลิกพื้นที่ว่างทางด้านซ้ายของหมายเลขบรรทัด
- ตัวบ่งชี้สีแดงจะปรากฏขึ้นเพื่อระบุว่าตั้งค่าเบรกพอยท์แล้ว
- เรียกใช้คำสั่ง
delete
อีกครั้ง
curl -v -X DELETE $URL/quotes/6
- เปลี่ยนกลับมาที่มุมมองแก้ไขข้อบกพร่องโดยคลิกไอคอนในคอลัมน์ด้านซ้าย
- สังเกตบรรทัดแก้ไขข้อบกพร่องที่หยุดไว้ในคลาส BidController
- ในโปรแกรมแก้ไขข้อบกพร่อง ให้คลิกไอคอน
step over
- สังเกตว่าโค้ดส่งคืน 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 ไม่พบ
แก้ไขข้อผิดพลาด
- โดยที่เซสชันการแก้ไขข้อบกพร่องยังทำงานอยู่ ให้ดำเนินการตามคำขอให้เสร็จสมบูรณ์โดยกด "ต่อไป" ในแผงควบคุมการแก้ไขข้อบกพร่อง
- จากนั้นเปลี่ยนบล็อก
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); } }
- เรียกใช้คำสั่งลบอีกครั้ง
curl -v -X DELETE $URL/quotes/6
- ให้ทำตามขั้นตอนในโปรแกรมแก้ไขข้อบกพร่องและตรวจสอบว่า 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
- หยุดเซสชันการแก้ไขข้อบกพร่องโดยคลิกสี่เหลี่ยมจัตุรัสสีแดงในแถบเครื่องมือแก้ไขข้อบกพร่อง
6. ขอแสดงความยินดี
ยินดีด้วย ในห้องทดลองนี้ คุณได้สร้างแอปพลิเคชัน Java ใหม่ตั้งแต่ต้นและกำหนดค่าให้ทำงานกับคอนเทนเนอร์ได้อย่างมีประสิทธิภาพ จากนั้นคุณได้ทำให้แอปพลิเคชันใช้งานได้และแก้ไขข้อบกพร่องของแอปพลิเคชันไปยังคลัสเตอร์ GKE ระยะไกลตามกระบวนการของนักพัฒนาซอฟต์แวร์เดียวกันที่พบในสแต็กแอปพลิเคชันแบบดั้งเดิมแล้ว
สิ่งที่คุณได้เรียนรู้
- การพัฒนา InnerLoop ด้วย Cloud Workstations
- การสร้างแอปพลิเคชันเริ่มต้นสำหรับ Java ใหม่
- แนะนำกระบวนการพัฒนา
- การพัฒนาบริการ CRUD REST แบบง่าย
- การแก้ไขข้อบกพร่องของแอปพลิเคชันบนคลัสเตอร์ GKE
- กำลังเชื่อมต่อแอปพลิเคชันกับฐานข้อมูล CloudSQL