1. Tổng quan
Phòng thí nghiệm này trình bày các tính năng và khả năng được thiết kế để hợp lý hoá quy trình phát triển dành cho các kỹ sư phần mềm được giao nhiệm vụ phát triển các ứng dụng Java trong môi trường vùng chứa. Thông thường, quá trình phát triển vùng chứa yêu cầu người dùng hiểu thông tin chi tiết về vùng chứa và quy trình xây dựng vùng chứa. Ngoài ra, các nhà phát triển thường phải ngắt luồng của mình, rời khỏi IDE để kiểm thử và gỡ lỗi ứng dụng trong môi trường từ xa. Với các công cụ và công nghệ được đề cập trong hướng dẫn này, nhà phát triển có thể làm việc hiệu quả với các ứng dụng trong vùng chứa mà không cần rời khỏi IDE của họ.
Kiến thức bạn sẽ học được
Trong phòng thí nghiệm này, bạn sẽ tìm hiểu các phương pháp phát triển bằng vùng chứa trong GCP, bao gồm:
- Phát triển InsideLoop với Cloud Workstation
- Tạo ứng dụng khởi đầu Java mới
- Tìm hiểu về quá trình phát triển
- Phát triển một dịch vụ nghỉ ngơi CRUD đơn giản
- Gỡ lỗi ứng dụng trên cụm GKE
- Kết nối ứng dụng với cơ sở dữ liệu CloudSQL
2. Thiết lập và yêu cầu
Thiết lập môi trường theo tiến độ riêng
- Đăng nhập vào Google Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.
- Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự không được API của Google sử dụng. Bạn có thể cập nhật thông tin này bất cứ lúc nào.
- Mã dự án là duy nhất trong tất cả các dự án Google Cloud và không thể thay đổi (không thể thay đổi sau khi đã đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường bạn không quan tâm đến sản phẩm đó là gì. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (mã này thường được xác định là
PROJECT_ID
). Nếu không thích mã đã tạo, bạn có thể tạo một mã nhận dạng ngẫu nhiên khác. Ngoài ra, bạn có thể thử phương pháp của riêng mình và xem có được cung cấp hay không. Bạn không thể thay đổi thông tin này sau bước này và thông báo đó sẽ vẫn tồn tại trong thời gian của dự án. - Đối với thông tin của bạn, có giá trị thứ ba, Project Number (Số dự án) mà một số API sử dụng. Tìm hiểu thêm về cả ba giá trị này trong tài liệu này.
- Tiếp theo, bạn sẽ phải bật tính năng thanh toán trong Cloud Console để sử dụng API/tài nguyên trên đám mây. Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Để tắt các tài nguyên nhằm tránh bị tính phí ngoài hướng dẫn này, bạn có thể xoá các tài nguyên bạn đã tạo hoặc xoá toàn bộ dự án. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.
Khởi động Cloudshell Editor
Phòng thí nghiệm này được thiết kế và thử nghiệm để sử dụng với Google Cloud Shell Editor. Để truy cập vào trình chỉnh sửa,
- truy cập vào dự án google của bạn tại https://console.cloud.google.com.
- Ở góc trên cùng bên phải, hãy nhấp vào biểu tượng trình chỉnh sửa vỏ đám mây
- Một ngăn mới sẽ mở ra ở cuối cửa sổ
- Nhấp vào nút Open Editor (Mở trình chỉnh sửa)
- Trình chỉnh sửa sẽ mở ra cùng với một trình khám phá ở bên phải và trình chỉnh sửa ở khu vực trung tâm
- Ngăn thiết bị đầu cuối cũng sẽ xuất hiện ở cuối màn hình
- Nếu cửa sổ dòng lệnh KHÔNG mở, hãy sử dụng tổ hợp phím "ctrl+" để mở cửa sổ dòng lệnh mới
Thiết lập gcloud
Trong Cloud Shell, hãy đặt mã dự án và khu vực mà bạn muốn triển khai ứng dụng. Hãy lưu các biến này dưới dạng biến PROJECT_ID
và REGION
.
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Sao chép mã nguồn
Mã nguồn cho phòng thí nghiệm này nằm tại Container-developer-workshop trong GoogleCloudPlatform trên GitHub. Hãy sao chép tệp đó bằng lệnh bên dưới rồi thay đổi vào thư mục.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Cung cấp cơ sở hạ tầng dùng trong phòng thí nghiệm này
Trong phòng thí nghiệm này, bạn sẽ triển khai mã cho GKE và truy cập vào dữ liệu được lưu trữ trong cơ sở dữ liệu CloudSQL. Tập lệnh thiết lập dưới đây chuẩn bị cơ sở hạ tầng này cho bạn. Quá trình cấp phép sẽ mất hơn 25 phút. Hãy chờ tập lệnh hoàn tất trước khi chuyển sang phần tiếp theo.
./setup_with_cw.sh &
Cụm Cloud Workstation
Mở Cloud Workstations trong Cloud Console. Chờ cụm ở trạng thái READY
.
Tạo cấu hình máy trạm
Nếu phiên Cloud Shell của bạn bị ngắt kết nối, hãy nhấp vào "Kết nối lại" rồi chạy lệnh gcloud cli để đặt mã dự án. Thay thế mã dự án mẫu bên dưới bằng mã dự án qwiklabs của bạn trước khi chạy lệnh.
gcloud config set project qwiklabs-gcp-project-id
Chạy tập lệnh bên dưới trong cửa sổ dòng lệnh để tạo cấu hình Cloud Workstations.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Xác minh kết quả trong phần Cấu hình. Sẽ mất 2 phút để chuyển sang trạng thái SẴN SÀNG.
Mở Cloud Workstations trong Console rồi tạo phiên bản mới.
Đổi tên thành my-workstation
và chọn cấu hình hiện có: codeoss-java
.
Xác minh kết quả trong phần Workstations (Máy trạm).
Khởi chạy máy trạm
Khởi động và khởi chạy máy trạm.
Cho phép cookie của bên thứ ba bằng cách nhấp vào biểu tượng trên thanh địa chỉ.
Nhấp vào "Trang web không hoạt động?".
Nhấp vào "Allow cookies".
Sau khi máy trạm chạy, bạn sẽ thấy IDE Mã OSS xuất hiện. Nhấp vào "Đánh dấu là đã xong" trên trang Bắt đầu một, IDE máy trạm
3. Tạo ứng dụng khởi động Java mới
Trong phần này, bạn sẽ tạo một ứng dụng Java Spring Boot mới từ đầu bằng cách dùng một ứng dụng mẫu do spring.io cung cấp. Mở một cửa sổ dòng lệnh mới.
Sao chép ứng dụng mẫu
- Tạo ứng dụng khởi đầu
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
Nhấp vào nút Cho phép nếu bạn thấy thông báo này để bạn có thể sao chép và dán vào máy trạm.
- Giải nén ứng dụng
unzip sample-app.zip -d sample-app
- Mở "ứng dụng mẫu" thư mục
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Thêm spring-boot-devtools & Kim chỉ nam
Để bật Công cụ cho nhà phát triển khởi động Spring, hãy tìm và mở tệp pom.xml qua trình khám phá trong trình chỉnh sửa của bạn. Tiếp theo, hãy dán mã sau vào sau dòng mô tả có nội dung <description>Demo project for Spring Boot</description>
- Thêm spring-boot-devtools trong pom.xml
Mở pom.xml
trong thư mục gốc của dự án. Thêm cấu hình sau đây sau mục nhập 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>
- Bật jib-maven-plugin trong pom.xml
Jib là một công cụ chứa Java nguồn mở của Google, cho phép các nhà phát triển Java xây dựng vùng chứa bằng các công cụ Java mà họ biết. Jib là trình tạo hình ảnh vùng chứa nhanh và đơn giản, xử lý tất cả các bước đóng gói ứng dụng của bạn vào hình ảnh vùng chứa. Cấu hình này không yêu cầu bạn phải viết Dockerfile hoặc cài đặt docker, đồng thời được tích hợp trực tiếp vào Maven và Gradle.
Di chuyển xuống tệp pom.xml
rồi cập nhật phần Build
để thêm trình bổ trợ Jib. Phần bản dựng phải khớp với phần sau khi hoàn tất.
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>
Tạo tệp kê khai
Skaffold cung cấp các công cụ tích hợp để đơn giản hoá quá trình phát triển vùng chứa. Ở bước này, bạn sẽ khởi chạy Skaffold sẽ tự động tạo các tệp YAML của Kubernetes cơ bản. Quá trình này sẽ cố gắng xác định các thư mục có định nghĩa hình ảnh vùng chứa, chẳng hạn như Dockerfile, sau đó tạo một tệp kê khai dịch vụ và triển khai cho mỗi thư mục.
Thực thi lệnh dưới đây trong Cửa sổ dòng lệnh để bắt đầu quá trình.
- Thực thi lệnh sau trong dòng lệnh
skaffold init --generate-manifests
- Khi được nhắc:
- Sử dụng các mũi tên để di chuyển con trỏ đến
Jib Maven Plugin
- Nhấn phím cách để chọn tuỳ chọn.
- Nhấn phím Enter để tiếp tục
- Nhập 8080 cho cổng
- Nhập y để lưu cấu hình
Hai tệp được thêm vào không gian làm việc skaffold.yaml
và deployment.yaml
Đầu ra Skaffold:
Cập nhật tên ứng dụng
Các giá trị mặc định có trong cấu hình hiện không khớp với tên ứng dụng của bạn. Cập nhật các tệp để tham chiếu tên ứng dụng của bạn thay vì các giá trị mặc định.
- Thay đổi các mục trong cấu hình Skaffold
- Mở
skaffold.yaml
- Chọn tên hình ảnh hiện được đặt là
pom-xml-image
- Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
- Nhập tên mới là
demo-app
- Thay đổi các mục trong cấu hình Kubernetes
- Mở tệp
deployment.yaml
- Chọn tên hình ảnh hiện được đặt là
pom-xml-image
- Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
- Nhập tên mới là
demo-app
Bật chế độ tự động đồng bộ hoá
Để tối ưu hoá trải nghiệm tải lại nóng, bạn cần dùng tính năng Đồng bộ hoá do Jib cung cấp. Ở bước này, bạn sẽ định cấu hình Skaffold để sử dụng tính năng đó trong quy trình xây dựng.
Lưu ý rằng thao tác "đồng bộ hoá" hồ sơ mà bạn đang định cấu hình trong cấu hình Skaffold tận dụng tính năng "đồng bộ hoá" Spring Hồ sơ bạn đã định cấu hình ở bước trước, trong đó bạn đã bật tính năng hỗ trợ cho spring-dev-tools.
- Cập nhật cấu hình Skaffold
Trong tệp skaffold.yaml
, hãy thay thế toàn bộ phần bản dựng của tệp bằng thông số kỹ thuật sau. Đừng thay đổi các phần khác của tệp.
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
Thêm tuyến đường mặc định
Tạo một tệp có tên là HelloController.java
trong thư mục /src/main/java/com/example/springboot/
.
Dán những nội dung sau vào tệp để tạo tuyến http mặc định.
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. Tìm hiểu về quá trình phát triển
Trong phần này, bạn sẽ tìm hiểu một số bước sử dụng trình bổ trợ Mã đám mây để tìm hiểu các quy trình cơ bản, cũng như để xác thực cấu hình và chế độ thiết lập ứng dụng khởi đầu.
Cloud Code tích hợp với Skaffold để đơn giản hoá quy trình phát triển của bạn. Khi bạn triển khai GKE ở các bước sau, Cloud Code và Skaffold sẽ tự động tạo hình ảnh vùng chứa của bạn, đẩy hình ảnh vùng chứa đó vào Container Registry, sau đó triển khai ứng dụng của bạn lên GKE. Quá trình này xảy ra trong nền, trừu tượng chi tiết khỏi quy trình của nhà phát triển. Cloud Code cũng giúp cải thiện quá trình phát triển bằng cách cung cấp các chức năng gỡ lỗi và đồng bộ hoá nóng truyền thống cho hoạt động phát triển dựa trên vùng chứa.
Đăng nhập vào Google Cloud
Nhấp vào biểu tượng Mã đám mây rồi chọn "Đăng nhập vào Google Cloud":
Nhấp vào "Tiếp tục để đăng nhập".
Kiểm tra kết quả trong dòng lệnh và mở đường liên kết:
Đăng nhập bằng thông tin đăng nhập của sinh viên trên Qwiklabs.
Chọn "Cho phép":
Sao chép mã xác minh rồi quay lại thẻ Máy trạm.
Dán mã xác minh rồi nhấn Enter.
Thêm cụm Kubernetes
- Thêm một cụm
- Chọn Google Kubernetes Engine:
- Chọn dự án.
- Chọn " trích dẫn-cụm" được tạo ở bước thiết lập ban đầu.
Đặt mã dự án hiện tại bằng gcloud cli
Sao chép mã dự án của phòng thí nghiệm này từ trang qwiklabs.
Chạy lệnh gcloud cli để đặt mã dự án. Hãy thay thế mã dự án mẫu trước khi chạy lệnh.
gcloud config set project qwiklabs-gcp-project-id
Kết quả mẫu:
Gỡ lỗi trên Kubernetes
- Trong ngăn bên trái ở dưới cùng, chọn Cloud Code.
- Trong bảng điều khiển xuất hiện ở phần PHIÊN PHÁT TRIỂN, hãy chọn Gỡ lỗi trên Kubernetes.
Di chuyển xuống nếu lựa chọn này không xuất hiện.
- Chọn "Có" để sử dụng ngữ cảnh hiện tại.
- Chọn " trích dẫn-cụm" được tạo trong quá trình thiết lập ban đầu.
- Chọn Kho lưu trữ vùng chứa.
- Chọn thẻ Output (Kết quả) trong ngăn phía dưới để xem tiến trình và thông báo
- Chọn "Kubernetes: Chạy/Gỡ lỗi – Chi tiết" trong trình đơn thả xuống ở bên phải của kênh để xem thêm thông tin chi tiết và nhật ký phát trực tiếp từ các vùng chứa
Chờ ứng dụng được triển khai.
- Xem xét ứng dụng đã triển khai trên GKE trong Cloud Console.
- Quay lại chế độ xem đơn giản bằng cách chọn "Kubernetes: Chạy/Gỡ lỗi" từ trình đơn thả xuống trên tab OUTPUT.
- Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết:
Resource deployment/demo-app status completed successfully
, và một URL có trong danh sách: "Được chuyển tiếp URL từ ứng dụng minh hoạ dịch vụ: http://localhost:8080" - Trong cửa sổ dòng lệnh Mã đám mây, hãy di chuột qua URL trong dữ liệu đầu ra (http://localhost:8080), sau đó chọn Theo dõi đường liên kết của công cụ hiện ra.
Thẻ mới sẽ mở ra và bạn sẽ thấy kết quả bên dưới:
Khai thác các điểm ngắt
- Mở ứng dụng
HelloController.java
tại/src/main/java/com/example/springboot/HelloController.java
- Tìm câu lệnh trả về cho đường dẫn gốc có nội dung
return String.format("Hello from your %s environment!", target);
- Thêm điểm ngắt vào dòng đó bằng cách nhấp vào khoảng trống ở bên trái số dòng. Một chỉ báo màu đỏ sẽ xuất hiện để ghi chú điểm ngắt đã được đặt
- Tải lại trình duyệt và lưu ý rằng trình gỡ lỗi sẽ dừng quá trình này tại điểm ngắt và cho phép bạn điều tra các biến cũng như trạng thái của ứng dụng đang chạy từ xa trong GKE
- Nhấp vào mục biến cho đến khi bạn thấy mục "Mục tiêu" biến.
- Quan sát giá trị hiện tại dưới dạng "cục bộ"
- Nhấp đúp vào tên biến "mục tiêu" và trong cửa sổ bật lên,
thay đổi giá trị thành "Cloud Workstations"
- Nhấp vào nút Tiếp tục trong bảng điều khiển gỡ lỗi
- Xem lại phản hồi trong trình duyệt để hiện giá trị được cập nhật mà bạn vừa nhập.
- Xoá điểm ngắt bằng cách nhấp vào chỉ báo màu đỏ ở bên trái số dòng. Thao tác này sẽ ngăn mã của bạn ngừng thực thi tại dòng này trong quá trình bạn tiếp tục học trong phòng thí nghiệm này.
Tải lại nhanh
- Thay đổi câu lệnh để trả về một giá trị khác, chẳng hạn như "Hello from %s Code" (Xin chào từ %s Code)
- Tệp được tự động lưu và đồng bộ hoá vào các vùng chứa từ xa trong GKE
- Làm mới trình duyệt của bạn để xem kết quả được cập nhật.
- Dừng phiên gỡ lỗi bằng cách nhấp vào hình vuông màu đỏ trên thanh công cụ gỡ lỗi
Chọn "Có, dọn dẹp sau mỗi lần chạy".
5. Phát triển một dịch vụ nghỉ ngơi CRUD đơn giản
Tại thời điểm này, ứng dụng của bạn đã được định cấu hình đầy đủ để phát triển theo vùng chứa và bạn đã tìm hiểu quy trình phát triển cơ bản bằng Cloud Code. Trong các phần sau, bạn sẽ thực hành nội dung đã học bằng cách thêm điểm cuối của dịch vụ Kiến trúc chuyển trạng thái đại diện (REST) để kết nối với một cơ sở dữ liệu được quản lý trong Google Cloud.
Định cấu hình phần phụ thuộc
Mã xử lý ứng dụng dùng cơ sở dữ liệu để lưu trữ dữ liệu dịch vụ còn lại. Đảm bảo các phần phụ thuộc có sẵn bằng cách thêm đoạn mã sau vào tệp pom.xl
- Mở tệp
pom.xml
rồi thêm nội dung sau vào mục phần phụ thuộc của cấu hình
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>
Dịch vụ REST của mã
Quote.java
Tạo một tệp có tên là Quote.java
trong /src/main/java/com/example/springboot/
rồi sao chép vào đoạn mã bên dưới. Thao tác này xác định mô hình Thực thể cho đối tượng Trích dẫn được dùng trong ứng dụng.
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
Tạo một tệp có tên là QuoteRepository.java
tại src/main/java/com/example/springboot
rồi sao chép vào đoạn mã sau
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();
}
Mã này sử dụng JPA để duy trì dữ liệu. Lớp này mở rộng giao diện Spring JPARepository
và cho phép tạo mã tuỳ chỉnh. Trong mã, bạn đã thêm một phương thức tuỳ chỉnh findRandomQuote
.
QuoteController.java
Để hiển thị điểm cuối của dịch vụ, lớp QuoteController
sẽ cung cấp chức năng này.
Tạo một tệp có tên là QuoteController.java
tại src/main/java/com/example/springboot
rồi sao chép vào các nội dung sau
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);
}
}
}
Thêm cấu hình cơ sở dữ liệu
application.yaml
Thêm cấu hình cho cơ sở dữ liệu phụ trợ mà dịch vụ truy cập. Chỉnh sửa (hoặc tạo nếu không có) tệp có tên là tệp application.yaml
trong src/main/resources
và thêm một cấu hình Spring có tham số cho phần phụ trợ.
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
Thêm quy trình di chuyển cơ sở dữ liệu
Tạo thư mục db/migration
trong src/main/resources
Tạo tệp SQL: V1__create_quotes_table.sql
Dán các nội dung sau vào tệp
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');
Cấu hình Kerberos
Những nội dung bổ sung sau đây cho tệp deployment.yaml
cho phép ứng dụng kết nối với các thực thể CloudSQL.
- TARGET – định cấu hình biến để cho biết môi trường nơi ứng dụng được thực thi
- SPbạn_Hồ sơ_HOẠT ĐỘNG – hiển thị hồ sơ Spring đang hoạt động, hồ sơ này sẽ được định cấu hình thành
cloud-dev
- DB_HOST – IP riêng tư của cơ sở dữ liệu, đã được ghi chú khi thực thể cơ sở dữ liệu được tạo hoặc bằng cách nhấp vào
SQL
trong Trình đơn điều hướng của Google Cloud Console – vui lòng thay đổi giá trị! - DB_USER và DB_PASS – như được đặt trong cấu hình phiên bản CloudSQL, được lưu trữ dưới dạng Khoá bí mật trong GCP
Hãy cập nhật triển khai.yaml của bạn theo nội dung bên dưới.
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
Thay thế giá trị DB_HOST bằng địa chỉ Cơ sở dữ liệu của bạn bằng cách chạy các lệnh bên dưới trong cửa sổ dòng lệnh:
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
Mở triển khai.yaml và xác minh rằng giá trị DB_HOST đã được cập nhật bằng IP của thực thể.
Triển khai và xác thực ứng dụng
- Trong ngăn ở cuối Cloud Shell Editor, chọn Cloud Code rồi chọn Debug on Kubernetes ở đầu màn hình.
- Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết:
Resource deployment/demo-app status completed successfully
và một URL có trong danh sách: "Được chuyển tiếp URL từ ứng dụng minh hoạ dịch vụ: http://localhost:8080". Xin lưu ý rằng đôi khi cổng có thể khác như 8081. Nếu có, hãy đặt giá trị phù hợp. Đặt giá trị của URL trên thiết bị đầu cuối
export URL=localhost:8080
- Xem trích dẫn ngẫu nhiên
Trong dòng lệnh, hãy chạy lệnh dưới đây nhiều lần đối với điểm cuối trích dẫn ngẫu nhiên. Quan sát cuộc gọi lặp lại và trả về các dấu ngoặc kép khác nhau
curl $URL/random-quote | jq
- Thêm báo giá
Tạo một bản báo giá mới, với id=6 bằng cách sử dụng lệnh được liệt kê bên dưới và quan sát yêu cầu được lặp lại
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
- Xoá bản báo giá
Bây giờ, hãy xoá bản báo giá bạn vừa thêm bằng phương thức xoá và quan sát mã phản hồi HTTP/1.1 204
.
curl -v -X DELETE $URL/quotes/6
- Lỗi máy chủ
Trải nghiệm trạng thái lỗi bằng cách chạy lại yêu cầu gần đây nhất sau khi mục nhập đã bị xoá
curl -v -X DELETE $URL/quotes/6
Lưu ý rằng phản hồi trả về HTTP:500 Internal Server Error
.
Gỡ lỗi ứng dụng
Ở phần trước, bạn đã phát hiện trạng thái lỗi trong ứng dụng khi cố gắng xoá một mục không có trong cơ sở dữ liệu. Trong phần này, bạn sẽ đặt một điểm ngắt để xác định vấn đề. Đã xảy ra lỗi trong thao tác DELETE, vì vậy, bạn sẽ làm việc với lớp QuoteController.
- Mở
src/main/java/com/example/springboot/QuoteController.java
- Tìm phương thức
deleteQuote()
- Tìm đường thẳng này:
Optional<Quote> quote = quoteRepository.findById(id);
- Đặt điểm ngắt trên dòng đó bằng cách nhấp vào khoảng trống ở bên trái số dòng.
- Một chỉ báo màu đỏ sẽ xuất hiện cho biết điểm ngắt đã được đặt
- Chạy lại lệnh
delete
curl -v -X DELETE $URL/quotes/6
- Chuyển về chế độ xem gỡ lỗi bằng cách nhấp vào biểu tượng ở cột bên trái
- Quan sát dòng gỡ lỗi bị dừng trong lớp QuoteController.
- Trong trình gỡ lỗi, hãy nhấp vào biểu tượng
step over
- Lưu ý rằng mã sẽ trả về lỗi HTTP 500 lỗi máy chủ nội bộ cho máy khách. Đây là lỗi không lý tưởng.
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
Cập nhật đoạn mã
Mã này không chính xác và bạn cần tái cấu trúc khối else
để gửi lại mã trạng thái HTTP 404 không tìm thấy.
Sửa lỗi.
- Khi phiên Gỡ lỗi vẫn đang chạy, hãy hoàn tất yêu cầu bằng cách nhấn "tiếp tục" trong bảng điều khiển gỡ lỗi.
- Tiếp theo, hãy thay đổi khối
else
thành mã:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Phương thức sẽ có dạng như sau
@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); } }
- Chạy lại lệnh xoá
curl -v -X DELETE $URL/quotes/6
- Thực hiện các bước trong trình gỡ lỗi và quan sát lỗi HTTP 404 Not Tìm thấy được trả về cho phương thức gọi.
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
- Dừng phiên gỡ lỗi bằng cách nhấp vào hình vuông màu đỏ trên thanh công cụ gỡ lỗi
6. Xin chúc mừng
Xin chúc mừng! Trong phòng thí nghiệm này, bạn đã tạo một ứng dụng Java mới từ đầu và định cấu hình để ứng dụng này hoạt động hiệu quả với vùng chứa. Sau đó, bạn đã triển khai và gỡ lỗi ứng dụng cho một cụm GKE từ xa theo cùng một quy trình dành cho nhà phát triển trong các ngăn xếp ứng dụng truyền thống.
Kiến thức bạn học được
- Phát triển InsideLoop với Cloud Workstation
- Tạo ứng dụng khởi đầu Java mới
- Tìm hiểu về quá trình phát triển
- Phát triển một Dịch vụ CRUD REST đơn giản
- Gỡ lỗi ứng dụng trên cụm GKE
- Kết nối ứng dụng với cơ sở dữ liệu CloudSQL