1. Обзор
В этой лабораторной работе демонстрируются функции и возможности, разработанные для оптимизации рабочего процесса разработки для инженеров-программистов, занимающихся разработкой Java-приложений в контейнеризированной среде. Типичная разработка в контейнерах требует от пользователя понимания деталей контейнеров и процесса сборки контейнеров. Кроме того, разработчикам обычно приходится прерывать свой рабочий процесс, выходя из IDE для тестирования и отладки своих приложений в удаленных средах. С помощью инструментов и технологий, упомянутых в этом руководстве, разработчики могут эффективно работать с контейнеризированными приложениями, не покидая свою IDE.
Что вы узнаете
В этой лабораторной работе вы изучите методы разработки с использованием контейнеров в GCP, в том числе:
- Разработка с использованием технологии InnerLoop на облачных рабочих станциях.
- Создание нового стартового Java-приложения
- Обзор процесса разработки
- Разработка простого REST-сервиса с операциями CRUD.
- Отладка приложения в кластере GKE.
- Подключение приложения к базе данных CloudSQL

2. Настройка и требования
Настройка среды для самостоятельного обучения
- Войдите в консоль Google Cloud и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .



- Название проекта — это отображаемое имя участников данного проекта. Это строка символов, не используемая API Google. Вы можете изменить её в любое время.
- Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку; обычно вам неважно, какая она. В большинстве практических заданий вам потребуется указать идентификатор проекта (обычно он обозначается как
PROJECT_ID). Если сгенерированный идентификатор вас не устраивает, вы можете сгенерировать другой случайный идентификатор. В качестве альтернативы вы можете попробовать свой собственный и посмотреть, доступен ли он. После этого шага его нельзя изменить, и он останется неизменным на протяжении всего проекта. - К вашему сведению, существует третье значение — номер проекта , который используется некоторыми API. Подробнее обо всех трех значениях можно узнать в документации .
- Далее вам потребуется включить оплату в консоли Cloud для использования ресурсов/API Cloud. Выполнение этого практического задания не должно стоить дорого, если вообще что-либо. Чтобы отключить ресурсы и избежать дополнительных расходов после завершения этого урока, вы можете удалить созданные ресурсы или удалить весь проект. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .
Запустить редактор Cloudshell
Данная лабораторная работа разработана и протестирована для использования с редактором Google Cloud Shell. Для доступа к редактору,
- Получите доступ к своему проекту Google по адресу https://console.cloud.google.com .
- В правом верхнем углу нажмите на значок редактора облачной оболочки.

- В нижней части вашего окна откроется новое окно.
- Нажмите кнопку «Открыть редактор».

- В начале редактора справа будет изображен исследователь, а в центре — сам редактор.
- В нижней части экрана также должна быть доступна панель терминала.
- Если терминал НЕ открыт, используйте комбинацию клавиш `Ctrl+`, чтобы открыть новое окно терминала.
Настройте gcloud
В Cloud Shell укажите идентификатор проекта и регион, в который вы хотите развернуть приложение. Сохраните их как переменные PROJECT_ID и REGION .
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Клонируйте исходный код
Исходный код для этой лабораторной работы находится в репозитории container-developer-workshop на GitHub в GoogleCloudPlatform. Клонируйте его с помощью приведенной ниже команды, а затем перейдите в нужную директорию.
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 была разорвана, нажмите «Переподключиться», а затем выполните команду cli gcloud, чтобы установить идентификатор проекта. Перед выполнением команды замените указанный ниже пример идентификатора проекта на идентификатор вашего проекта qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
Запустите приведенный ниже скрипт в терминале, чтобы создать конфигурацию облачных рабочих станций.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Проверьте результаты в разделе «Конфигурации». Переход в состояние «ГОТОВО» займет 2 минуты.

Откройте раздел «Облачные рабочие станции» в консоли и создайте новый экземпляр.

Измените имя на my-workstation и выберите существующую конфигурацию: codeoss-java .

Проверьте результаты в разделе «Рабочие станции».

Запуск рабочей станции
Запустите рабочую станцию.

Разрешите использование сторонних файлов cookie, нажав на значок в адресной строке. 

Нажмите «Сайт не работает?».

Нажмите «Разрешить файлы cookie».

После запуска рабочей станции вы увидите запущенную среду разработки Code OSS 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
- Откройте папку "sample-app".
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Добавьте spring-boot-devtools и Jib.
Чтобы включить инструменты разработчика Spring Boot, найдите и откройте файл 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. После завершения раздел Build должен соответствовать следующему содержимому.
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>
Создать манифесты
Skaffold предоставляет интегрированные инструменты для упрощения разработки контейнеров. На этом этапе вы инициализируете Skaffold, который автоматически создаст базовые YAML-файлы Kubernetes. Процесс пытается идентифицировать каталоги с определениями образов контейнеров, подобно Dockerfile, а затем создает манифест развертывания и службы для каждого из них.
Для начала процесса выполните следующую команду в терминале.

- Выполните следующую команду в терминале.
skaffold init --generate-manifests
- При появлении запроса:
- Используйте стрелки, чтобы переместить курсор к
Jib Maven Plugin - Нажмите пробел, чтобы выбрать нужный вариант.
- Нажмите Enter, чтобы продолжить.
- Введите 8080 для порта.
- Введите y для сохранения конфигурации.
В рабочую область добавлены два файла skaffold.yaml и deployment.yaml
Вывод Skaffold:

Обновить название приложения
Значения по умолчанию, указанные в конфигурации, в настоящее время не соответствуют названию вашего приложения. Обновите файлы, чтобы они ссылались на название вашего приложения, а не на значения по умолчанию.
- Измените записи в конфигурации Skaffold.
- Откройте
skaffold.yaml - Выберите имя изображения, которое в данный момент задано как
pom-xml-image - Щелкните правой кнопкой мыши и выберите «Изменить все вхождения».
- Введите новое имя как
demo-app
- Измените записи в конфигурации Kubernetes.
- Откройте файл
deployment.yaml - Выберите имя изображения, которое в данный момент задано как
pom-xml-image - Щелкните правой кнопкой мыши и выберите «Изменить все вхождения».
- Введите новое имя как
demo-app
Включить режим автоматической синхронизации
Для оптимизации процесса горячей перезагрузки вы будете использовать функцию синхронизации, предоставляемую Jib. На этом шаге вы настроите Skaffold для использования этой функции в процессе сборки.
Обратите внимание, что профиль "sync", который вы настраиваете в конфигурации Skaffold, использует профиль "sync" Spring, который вы настроили на предыдущем шаге, где вы включили поддержку spring-dev-tools.
- Обновить конфигурацию Skaffold
В файле skaffold.yaml замените весь раздел build следующим описанием. Не изменяйте другие разделы файла.
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. Обзор процесса разработки.
В этом разделе вы шаг за шагом изучите основные процессы с помощью плагина Cloud Code, а также проверите конфигурацию и настройку вашего стартового приложения.
Cloud Code интегрируется со Skaffold для оптимизации процесса разработки. При развертывании в GKE на следующих этапах Cloud Code и Skaffold автоматически создадут образ контейнера, загрузят его в реестр контейнеров, а затем развернут ваше приложение в GKE. Это происходит в фоновом режиме, абстрагируя детали от процесса разработки. Cloud Code также улучшает процесс разработки, предоставляя традиционные возможности отладки и синхронизации для разработки на основе контейнеров.
Войдите в Google Cloud
Нажмите на значок Cloud Code и выберите «Войти в Google Cloud»:

Нажмите «Перейти к входу».

Проверьте вывод в терминале и откройте ссылку:

Войдите в систему, используя свои учетные данные студента Qwiklabs.

Выберите «Разрешить»:

Скопируйте проверочный код и вернитесь на вкладку «Рабочая станция».

Вставьте проверочный код и нажмите Enter.

Добавить кластер Kubernetes
- Добавить кластер

- Выберите Google Kubernetes Engine:

- Выберите проект.

- Выберите "quote-cluster", созданный при первоначальной настройке.


Установите идентификатор текущего проекта с помощью gcloud cli.
Скопируйте идентификатор проекта для этой лабораторной работы со страницы qwiklabs.

Выполните команду gcloud cli, чтобы установить идентификатор проекта. Перед выполнением команды замените sample project id.
gcloud config set project qwiklabs-gcp-project-id
Пример выходных данных:

Отладка в Kubernetes
- В левой панели внизу выберите Cloud Code.

- В панели, которая отображается в разделе «СЕАНСЫ РАЗРАБОТКИ», выберите «Отладка в Kubernetes».
Если эта опция не видна, прокрутите вниз.

- Выберите «Да», чтобы использовать текущий контекст.

- Выберите "quote-cluster", созданный во время первоначальной настройки.

- Выберите репозиторий контейнеров.

- Чтобы просмотреть ход выполнения и уведомления, выберите вкладку «Вывод» в нижней панели.
- Выберите «Kubernetes: Запуск/Отладка — Подробная информация» в раскрывающемся списке каналов справа, чтобы просмотреть дополнительные сведения и журналы, транслируемые в режиме реального времени из контейнеров.

Дождитесь развертывания приложения.

- Проверьте развернутое приложение в GKE в консоли Cloud Console .

- Чтобы вернуться к упрощенному представлению, выберите "Kubernetes: Run/Debug" из выпадающего списка на вкладке OUTPUT.
- После завершения сборки и тестирования на вкладке «Вывод» отображается сообщение:
Resource deployment/demo-app status completed successfully, а также указан URL-адрес: «Перенаправленный URL-адрес из службы демонстрационного приложения: http://localhost:8080». - В терминале Cloud Code наведите курсор на URL-адрес в выводе (http://localhost:8080), а затем во всплывающей подсказке выберите «Перейти по ссылке».

Откроется новая вкладка, и вы увидите результат, показанный ниже:

Используйте точки останова
- Откройте приложение
HelloController.java, расположенное по адресу/src/main/java/com/example/springboot/HelloController.java - Найдите оператор return для корневого пути, который выглядит следующим образом:
return String.format("Hello from your %s environment!", target); - Установите точку останова на этой строке, щелкнув по пустому месту слева от номера строки. Красный индикатор покажет, что точка останова установлена.

- Перезагрузите браузер и обратите внимание, что отладчик останавливает процесс в точке останова и позволяет вам исследовать переменные и состояние приложения, работающего удаленно в GKE.

- Прокрутите вниз в раздел переменных, пока не найдете переменную "Цель".
- Текущее значение следует рассматривать как "локальное".

- Дважды щелкните по имени переменной "target", и во всплывающем окне...
Измените значение на "Облачные рабочие станции".

- Нажмите кнопку «Продолжить» на панели управления отладкой.

- Проверьте ответ в браузере, где теперь отображается обновленное значение, которое вы только что ввели.

- Снимите точку останова, щелкнув красный индикатор слева от номера строки. Это предотвратит остановку выполнения вашего кода на этой строке по мере прохождения данной лабораторной работы.
Горячая перезарядка
- Измените оператор так, чтобы он возвращал другое значение, например, "Привет от %s Code".
- Файл автоматически сохраняется и синхронизируется с удалёнными контейнерами в GKE.
- Обновите страницу в браузере, чтобы увидеть обновленные результаты.
- Остановите сеанс отладки, нажав на красный квадрат на панели инструментов отладки.


Выберите «Да, очищать после каждого запуска».

5. Разработка простого REST-сервиса с операциями CRUD.
На этом этапе ваше приложение полностью настроено для контейнерной разработки, и вы прошли базовый рабочий процесс разработки с помощью Cloud Code. В следующих разделах вы попрактикуетесь в применении полученных знаний, добавив конечные точки REST-сервисов, подключающиеся к управляемой базе данных в Google Cloud.
Настройка зависимостей
Код приложения использует базу данных для сохранения данных REST-сервиса. Убедитесь в наличии зависимостей, добавив следующее в файл pom.xl.
- Откройте файл
pom.xmlи добавьте следующее в раздел dependencies файла config.xml.
pom.xml
<!-- Database dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
Код REST-сервиса
Quote.java
Создайте файл с именем Quote.java в /src/main/java/com/example/springboot/ и скопируйте в него приведенный ниже код. Он определяет модель Entity для объекта Quote, используемого в приложении.
package com.example.springboot;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
Создайте файл с именем QuoteRepository.java в src/main/java/com/example/springboot и скопируйте в него следующий код.
package com.example.springboot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface QuoteRepository extends JpaRepository<Quote,Integer> {
@Query( nativeQuery = true, value =
"SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
Quote findRandomQuote();
}
Этот код использует JPA для сохранения данных. Класс расширяет интерфейс Spring JPARepository и позволяет создавать собственный код. В коде вы добавили пользовательский метод findRandomQuote .
QuoteController.java
Для предоставления доступа к конечной точке сервиса этот функционал будет реализован в классе QuoteController .
Создайте файл с именем QuoteController.java в src/main/java/com/example/springboot и скопируйте в него следующее содержимое.
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
Optional<Quote> quote = quoteRepository.findById(id);
if (quote.isPresent()) {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Добавить настройки базы данных
application.yaml
Добавьте конфигурацию для базы данных бэкэнда, к которой обращается сервис. Отредактируйте (или создайте, если она отсутствует) файл application.yaml в папке src/main/resources и добавьте параметризованную конфигурацию Spring для бэкэнда.
target: local
spring:
config:
activate:
on-profile: cloud-dev
datasource:
url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
username: '${DB_USER:user}'
password: '${DB_PASS:password}'
jpa:
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
Добавить миграцию базы данных
Создайте папку db/migration в каталоге src/main/resources
Создайте SQL-файл: V1__create_quotes_table.sql
Вставьте следующее содержимое в файл.
V1__create_quotes_table.sql
CREATE TABLE quotes(
id INTEGER PRIMARY KEY,
quote VARCHAR(1024),
author VARCHAR(256)
);
INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');
Конфигурация Kubernetes
Следующие дополнения к файлу deployment.yaml позволяют приложению подключаться к экземплярам CloudSQL.
- TARGET — задает переменную, указывающую среду, в которой выполняется приложение.
- SPRING_PROFILES_ACTIVE — показывает активный профиль Spring, который будет настроен для
cloud-dev - DB_HOST — частный IP-адрес базы данных, который был указан при создании экземпляра базы данных или при нажатии на
SQLв навигационном меню консоли Google Cloud — пожалуйста, измените это значение! - DB_USER и DB_PASS — параметры, заданные в конфигурации экземпляра CloudSQL и хранящиеся в качестве секрета в 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 выберите Cloud Code, а затем в верхней части экрана выберите Debug on Kubernetes.

- После завершения сборки и тестирования на вкладке «Вывод» отображается сообщение:
Resource deployment/demo-app status completed successfully, а также указан URL-адрес: «Перенаправленный URL-адрес из сервиса demo-app: http://localhost:8080 ». Обратите внимание, что иногда порт может отличаться, например, 8081. В этом случае установите соответствующее значение. Установите значение URL-адреса в терминале.
export URL=localhost:8080
- Просмотреть случайные цитаты
В терминале несколько раз выполните указанную ниже команду для конечной точки random-quote. Обратите внимание, что при повторном вызове возвращаются разные котировки.
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
- Удалить цитату
Теперь удалите только что добавленную цитату с помощью метода delete и посмотрите на код ответа HTTP/1.1 204 .
curl -v -X DELETE $URL/quotes/6
- Ошибка сервера
Повторный запуск последнего запроса после удаления записи может привести к ошибке.
curl -v -X DELETE $URL/quotes/6
Обратите внимание, что в ответе возвращается HTTP:500 Internal Server Error .
Отладка приложения
В предыдущем разделе вы обнаружили ошибку в приложении при попытке удалить запись, которой не было в базе данных. В этом разделе вы установите точку останова, чтобы найти проблему. Ошибка произошла в операции DELETE, поэтому вы будете работать с классом QuoteController.
- Откройте
src/main/java/com/example/springboot/QuoteController.java - Найдите метод
deleteQuote() - Найдите строку:
Optional<Quote> quote = quoteRepository.findById(id); - Установите точку останова на этой строке, щелкнув по пустому месту слева от номера строки.
- Появится красный индикатор, указывающий на то, что точка останова установлена.
- Повторите команду
delete.
curl -v -X DELETE $URL/quotes/6
- Чтобы вернуться в режим отладки, нажмите на значок в левой колонке.
- Обратите внимание на строку отладки, которая останавливается в классе QuoteController.
- В отладчике нажмите значок "
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 на облачных рабочих станциях.
- Создание нового стартового Java-приложения
- Обзор процесса разработки
- Разработка простого REST-сервиса с операциями CRUD.
- Отладка приложения в кластере GKE.
- Подключение приложения к базе данных CloudSQL
