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:
- Thiết lập và yêu cầu
- Tạo ứng dụng khởi động 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
- Dọn dẹp
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 và bạn có thể cập nhật chuỗi này bất cứ lúc nào.
- Mã dự án phải là duy nhất trong tất cả các dự án Google Cloud và không thể thay đổi (bạn không thể thay đổi mã này 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 (và mã này thường được xác định là
PROJECT_ID
). Vì vậy, nếu không thích, bạn có thể tạo một mã ngẫu nhiên khác hoặc bạn có thể thử mã của riêng mình để xem có mã này chưa. Sau đó, video sẽ được "đóng băng" sau khi tạo dự án. - Có giá trị thứ ba là 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ẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên/API trên Cloud. 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 để bạn không phải chịu thanh toán ngoài hướng dẫn này, hãy làm theo mọi thao tác "dọn dẹp" hướng dẫn ở cuối lớp học lập trình. 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 PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Lấy 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 10 phút. Bạn có thể tiếp tục thực hiện một vài bước tiếp theo trong khi quá trình thiết lập đang diễn ra.
./setup.sh
3. Tạo ứng dụng khởi đầu 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
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=11 -d packageName=com.example.springboot -o sample-app.zip
- Giải nén ứng dụng
unzip sample-app.zip -d sample-app
- Chuyển sang thư mục ứng dụng mẫu và mở thư mục đó trong không gian làm việc IDE Cloud Shell
cd sample-app && cloudshell workspace .
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> (Dự án minh hoạ cho Spring Boot</description>)
- Thêm spring-boot-devtools trong pom.xml
Mở tệp 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 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>
Chọn Always
nếu được nhắc về việc thay đổi tệp bản dựng.
Tạo tệp kê khai
Skaffold cung cấp các công cụ tích hợp để đơn giản hoá việc phát triển vùng chứa. Ở bước này, bạn sẽ khởi chạy skafprint, thao tác này sẽ tự động tạo các tệp YAML của Kubernetes cơ sở. 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 để bắt đầu quy 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 viz, skaffold.yaml
và deployment.yaml
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 tính năng đồng bộ hoá nóng
Để tối ưu hoá trải nghiệm tải lại nóng, bạn sẽ 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á" cấu hình 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/java:debug
sync:
auto: true
Thêm tuyến đường mặc định
Tạo một tệp có tên HelloController.java tại /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.
Triển khai cho Kubernetes
- Trong ngăn ở cuối Cloud Shell Editor, hãy chọn Cloud Code ‡
- Trong bảng điều khiển xuất hiện ở trên cùng, hãy chọn Debug on Kubernetes. Nếu được nhắc, hãy chọn Có để sử dụng ngữ cảnh Kubernetes hiện tại.
- Trong lần đầu tiên bạn chạy lệnh, một lời nhắc sẽ xuất hiện ở đầu màn hình và hỏi xem bạn có muốn sử dụng ngữ cảnh hiện tại của Kubernetes không, hãy chọn "Có" để chấp nhận và sử dụng ngữ cảnh hiện tại.
- Tiếp theo, một lời nhắc sẽ xuất hiện hỏi xem bạn nên dùng sổ đăng ký vùng chứa nào. Nhấn phím Enter để chấp nhận giá trị mặc định được cung cấp
- 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
- Quay lại chế độ xem đơn giản bằng cách chọn "Kubernetes: Chạy/Gỡ lỗi" trong trình đơn thả xuống
- 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 "Open Web Preview" (Xem trước trên web) hiện ra trong chú giải công cụ vừa xuất hiện.
Câu trả lời sẽ là:
Hello from your local environment!
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 trạng thái cát thay đổ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, hãy thay đổi giá trị thành một giá trị khác, chẳng hạn như "Cloud"
- 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.
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
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 những gì đã học được bằng cách thêm thiết bị đầu cuối cho dịch vụ nghỉ ngơi 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>
Viết mã cho dịch vụ còn lại
Quote.java
Tạo một tệp có tên Trích dẫn.java trong /src/main/java/com/example/springboot/ rồi sao chép vào 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 javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.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 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 QuoteController.java tại src/main/java/com/example/springboot và sao chép vào 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) {
try {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
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 một thư mục tại src/main/resources/db/migration/
Tạo tệp SQL: V1__create_cites_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ủa Cơ sở dữ liệu của bạn
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
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" - Xem trích dẫn ngẫu nhiên
Trên ứng dụng Cloudshell Terminal, hãy chạy lệnh bên dưới 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 -v 127.0.0.1:8080/random-quote
- 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 -v -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 127.0.0.1:8080/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 127.0.0.1:8080/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 127.0.0.1:8080/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 dòng xoá một mặt hàng khỏi cơ sở dữ liệu:
quoteRepository.deleteById(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 127.0.0.1:8080/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
và quan sát thấy một ngoại lệ được gửi - Hãy lưu ý rằng một
RuntimeException was caught.
rất chung. Thao tác này sẽ trả về Lỗi máy chủ nội bộ HTTP 500 và lỗi này 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ã không chính xác và bạn nên tái cấu trúc khối ngoại lệ để phát hiện ngoại lệ EmptyResultDataAccessException
và gửi lại mã trạng thái không tìm thấy HTTP 404.
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 thêm khối sau vào mã:
} catch (EmptyResultDataAccessException e){
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Phương thức sẽ có dạng như sau
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) { try { quoteRepository.deleteById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch(EmptyResultDataAccessException e){ return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND); } catch (RuntimeException e) { System.out.println(e.getMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } }
- Chạy lại lệnh xoá
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Thao tác trên trình gỡ lỗi và quan sát thấy
EmptyResultDataAccessException
bị phát hiện và phản hồi HTTP 404 Not found (Không tìm thấy) trả về 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. Dọn dẹp
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.
Cách làm sạch sau khi hoàn thành phòng thí nghiệm:
- Xoá các tệp được sử dụng trong phòng thí nghiệm
cd ~ && rm -rf container-developer-workshop
- Xoá dự án để xoá tất cả cơ sở hạ tầng và tài nguyên có liên quan