توسعه InnerLoop با جاوا - SpringBoot

1. بررسی اجمالی

این آزمایشگاه ویژگی‌ها و قابلیت‌هایی را نشان می‌دهد که برای ساده کردن گردش کار توسعه برای مهندسان نرم‌افزاری که وظیفه توسعه برنامه‌های جاوا را در یک محیط کانتینری دارند، طراحی شده است. توسعه کانتینر معمولی به کاربر نیاز دارد که جزئیات کانتینرها و فرآیند ساخت کانتینر را درک کند. علاوه بر این، توسعه دهندگان معمولاً باید جریان خود را قطع کنند و از IDE خود خارج شوند تا برنامه های خود را در محیط های راه دور آزمایش و اشکال زدایی کنند. با ابزارها و فناوری های ذکر شده در این آموزش، توسعه دهندگان می توانند بدون خروج از IDE خود، با برنامه های کانتینری به طور موثر کار کنند.

آنچه خواهید آموخت

در این آزمایشگاه روش هایی برای توسعه با کانتینرها در GCP از جمله:

  • راه اندازی و الزامات
  • ایجاد یک برنامه استارت جدید جاوا
  • قدم زدن در فرآیند توسعه
  • توسعه یک سرویس استراحت ساده CRUD
  • پاکسازی

2. راه اندازی و الزامات

تنظیم محیط خود به خود

  1. به Google Cloud Console وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. اگر قبلاً یک حساب Gmail یا Google Workspace ندارید، باید یک حساب ایجاد کنید .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • نام پروژه نام نمایشی برای شرکت کنندگان این پروژه است. این یک رشته کاراکتری است که توسط API های Google استفاده نمی شود و می توانید هر زمان که بخواهید آن را به روز کنید.
  • شناسه پروژه باید در تمام پروژه‌های Google Cloud منحصربه‌فرد باشد و تغییرناپذیر باشد (پس از تنظیم نمی‌توان آن را تغییر داد). Cloud Console به طور خودکار یک رشته منحصر به فرد تولید می کند. معمولاً برای شما مهم نیست که چیست. در اکثر کدها، باید به شناسه پروژه ارجاع دهید (و معمولاً به عنوان PROJECT_ID شناخته می‌شود)، بنابراین اگر آن را دوست ندارید، یک نمونه تصادفی دیگر ایجاد کنید، یا می‌توانید شناسه پروژه را امتحان کنید و ببینید در دسترس است. سپس پس از ایجاد پروژه "یخ زده" می شود.
  • یک مقدار سوم وجود دارد، یک شماره پروژه که برخی از API ها از آن استفاده می کنند. در مورد هر سه این مقادیر در مستندات بیشتر بیاموزید.
  1. در مرحله بعد، برای استفاده از منابع Cloud/APIها، باید صورتحساب را در کنسول Cloud فعال کنید . اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. برای اینکه منابع را خاموش کنید تا بیش از این آموزش متحمل صورتحساب نشوید، دستورالعمل‌های «پاک‌سازی» را که در انتهای Codelab یافت می‌شود دنبال کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.

ویرایشگر Cloudshell را شروع کنید

این آزمایشگاه برای استفاده با Google Cloud Shell Editor طراحی و آزمایش شده است. برای دسترسی به ویرایشگر،

  1. به پروژه Google خود در https://console.cloud.google.com دسترسی پیدا کنید.
  2. در گوشه بالا سمت راست روی نماد ویرایشگر پوسته ابری کلیک کنید

8560cc8d45e8c112.png

  1. یک صفحه جدید در پایین پنجره شما باز می شود
  2. بر روی دکمه Open Editor کلیک کنید

9e504cb98a6a8005.png

  1. ویرایشگر با یک کاوشگر در سمت راست و ویرایشگر در ناحیه مرکزی باز می شود
  2. یک صفحه ترمینال نیز باید در پایین صفحه موجود باشد
  3. اگر ترمینال باز نیست، از کلید ترکیبی «ctrl+» برای باز کردن پنجره ترمینال جدید استفاده کنید

gcloud را راه اندازی کنید

در Cloud Shell، شناسه پروژه و منطقه ای را که می خواهید برنامه خود را در آن مستقر کنید، تنظیم کنید. آنها را به عنوان متغیرهای PROJECT_ID و REGION ذخیره کنید.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

کد منبع را دریافت کنید

کد منبع این آزمایشگاه در کارگاه توسعه دهنده کانتینر در GoogleCloudPlatform در GitHub قرار دارد. با دستور زیر آن را کلون کنید سپس به دایرکتوری تغییر دهید.

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

زیرساخت های مورد استفاده در این آزمایشگاه را فراهم کنید

در این آزمایشگاه شما کد را در GKE مستقر خواهید کرد و به داده های ذخیره شده در پایگاه داده CloudSql دسترسی خواهید داشت. اسکریپت راه اندازی زیر این زیرساخت را برای شما آماده می کند. فرآیند تهیه بیش از 10 دقیقه طول خواهد کشید. در حالی که تنظیمات در حال پردازش است، می توانید چند مرحله بعدی را ادامه دهید.

./setup.sh

3. ایجاد یک برنامه جدید Java starter

در این بخش با استفاده از یک نمونه برنامه ارائه شده توسط spring.io یک برنامه Java Spring Boot جدید از ابتدا ایجاد می کنید.

نمونه برنامه را کلون کنید

  1. یک برنامه شروع ایجاد کنید
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
  1. برنامه را از حالت فشرده خارج کنید
unzip sample-app.zip -d sample-app
  1. به پوشه نمونه برنامه تغییر دهید و پوشه را در فضای کاری Cloud Shell IDE باز کنید
cd sample-app && cloudshell workspace .

Spring-boot-devtools & Jib را اضافه کنید

برای فعال کردن Spring Boot DevTools، pom.xml را از کاوشگر در ویرایشگر خود پیدا کرده و باز کنید. سپس کد زیر را بعد از خط توضیحات قرار دهید که <description>پروژه آزمایشی برای Spring Boot</description> را می‌خواند.

  1. Spring-boot-devtools را در pom.xml اضافه کنید

pom.xml را در ریشه پروژه باز کنید. پیکربندی زیر را بعد از ورودی Description اضافه کنید.

pom.xml

  <!--  Spring profiles-->
  <profiles>
    <profile>
      <id>sync</id>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
        </dependency>
      </dependencies>
    </profile>
  </profiles>
  1. پلاگین jib-maven-plugin را در pom.xml فعال کنید

Jib یک ابزار جاوا منبع باز از Google است که به توسعه دهندگان جاوا اجازه می دهد با استفاده از ابزارهای جاوا که می شناسند، کانتینر بسازند. Jib یک سازنده تصویر ظرف سریع و ساده است که تمام مراحل بسته بندی برنامه شما را در یک تصویر ظرف انجام می دهد. نیازی به نوشتن Dockerfile یا نصب داکر ندارد و مستقیماً با 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>

در صورت درخواست در مورد تغییر فایل ساخت، Always را انتخاب کنید.

447a90338f51931f.png

ایجاد مانیفست

Skaffold ابزارهای یکپارچه ای را برای ساده سازی توسعه کانتینر فراهم می کند. در این مرحله شما skaffold را مقداردهی اولیه می کنید که به طور خودکار فایل های پایه Kubernetes YAML را ایجاد می کند. این فرآیند سعی می‌کند دایرکتوری‌هایی را با تعریف تصویر کانتینر، مانند Dockerfile شناسایی کند، و سپس برای هر کدام یک مانیفست استقرار و سرویس ایجاد می‌کند.

دستور زیر را برای شروع فرآیند اجرا کنید.

  1. دستور زیر را در ترمینال اجرا کنید
skaffold init --generate-manifests
  1. وقتی از شما خواسته شد:
  • از فلش ها برای انتقال مکان نما به Jib Maven Plugin استفاده کنید
  • برای انتخاب گزینه، کلید فاصله را فشار دهید.
  • برای ادامه اینتر را فشار دهید
  1. 8080 را برای پورت وارد کنید
  2. برای ذخیره تنظیمات y را وارد کنید

دو فایل skaffold.yaml و deployment.yaml به فضای کاری اضافه می شوند

نام برنامه را به روز کنید

مقادیر پیش‌فرض موجود در پیکربندی در حال حاضر با نام برنامه شما مطابقت ندارد. فایل ها را برای ارجاع به نام برنامه خود به جای مقادیر پیش فرض به روز کنید.

  1. ورودی‌ها را در پیکربندی Skaffold تغییر دهید
  • skaffold.yaml باز کنید
  • نام تصویری که در حال حاضر به عنوان pom-xml-image تنظیم شده را انتخاب کنید
  • کلیک راست کرده و Change All Occurrences را انتخاب کنید
  • نام جدید را به عنوان demo-app تایپ کنید
  1. ورودی‌های پیکربندی Kubernetes را تغییر دهید
  • فایل deployment.yaml را باز کنید
  • نام تصویری که در حال حاضر به عنوان pom-xml-image تنظیم شده را انتخاب کنید
  • کلیک راست کرده و Change All Occurrences را انتخاب کنید
  • نام جدید را به عنوان demo-app تایپ کنید

همگام سازی داغ را فعال کنید

برای تسهیل تجربه بارگیری مجدد داغ بهینه شده، از ویژگی Sync ارائه شده توسط Jib استفاده خواهید کرد. در این مرحله شما Skaffold را برای استفاده از آن ویژگی در فرآیند ساخت پیکربندی خواهید کرد.

توجه داشته باشید که نمایه "همگام سازی" که در پیکربندی skaffold پیکربندی می کنید از نمایه "همگام سازی" Spring که در مرحله قبل پیکربندی کرده اید، استفاده می کند، جایی که پشتیبانی از Spring-dev-tools را فعال کرده اید.

  1. پیکربندی skaffold را به روز کنید

در فایل skaffold.yaml کل بخش ساخت فایل را با مشخصات زیر جایگزین کنید. بخش های دیگر فایل را تغییر ندهید.

skaffold.yaml

build:
  artifacts:
  - image: demo-app
    jib:
      project: com.example:demo
      type: maven
      args: 
      - --no-transfer-progress
      - -Psync
      fromImage: gcr.io/distroless/java: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 همچنین با ارائه قابلیت‌های سنتی Debug و Hotsync برای توسعه مبتنی بر کانتینر، فرآیند توسعه شما را افزایش می‌دهد.

به Kubernetes مستقر شوید

  1. در قسمت پایین Cloud Shell Editor، Cloud Code  را انتخاب کنید

fdc797a769040839.png

  1. در پنلی که در بالا ظاهر می شود، Debug on Kubernetes را انتخاب کنید. اگر از شما خواسته شد، بله را برای استفاده از زمینه فعلی Kubernetes انتخاب کنید.

cfce0d11ef307087.png

  1. اولین باری که دستور را اجرا می‌کنید، یک فرمان در بالای صفحه ظاهر می‌شود که از شما می‌پرسد آیا زمینه Kubernetes فعلی را می‌خواهید، «بله» را برای پذیرش و استفاده از زمینه فعلی انتخاب کنید.

817ee33b5b412ff8.png

  1. سپس یک درخواست نمایش داده می شود که از کدام رجیستری کانتینری استفاده کنید. اینتر را فشار دهید تا مقدار پیش فرض ارائه شده را بپذیرید

eb4469aed97a25f6.png

  1. برای مشاهده پیشرفت و اعلان ها، تب Output را در قسمت پایین انتخاب کنید

f95b620569ba96c5.png

  1. "Kubernetes: Run/Debug - Detailed" را در منوی کشویی کانال به سمت راست انتخاب کنید تا جزئیات بیشتر و گزارش‌ها را به صورت زنده از کانتینرها مشاهده کنید.

94acdcdda6d2108.png

  1. با انتخاب "Kubernetes: Run/Debug" از منوی کشویی به نمای ساده شده بازگردید.
  2. وقتی ساخت و آزمایش انجام شد، برگه خروجی می‌گوید: Resource deployment/demo-app status completed successfully ، و یک نشانی اینترنتی فهرست می‌شود: «URL بازارسال شده از برنامه آزمایشی سرویس: http://localhost:8080»
  3. در ترمینال Cloud Code، ماوس را روی URL در خروجی نگه دارید (http://localhost:8080) و سپس در نکته ابزار ظاهر شده Open Web Preview را انتخاب کنید.

پاسخ این خواهد بود:

Hello from your local environment!

از نقاط شکست استفاده کنید

  1. برنامه HelloController.java واقع در /src/main/java/com/example/springboot/HelloController.java را باز کنید
  2. عبارت return را برای مسیر ریشه پیدا کنید که return String.format("Hello from your %s environment!", target);
  3. با کلیک بر روی فضای خالی سمت چپ شماره خط، یک نقطه شکست به آن خط اضافه کنید. یک نشانگر قرمز نشان می دهد تا توجه داشته باشید که نقطه شکست تنظیم شده است
  4. مرورگر خود را مجدداً بارگیری کنید و توجه داشته باشید که دیباگر فرآیند را در نقطه شکست متوقف می کند و به شما امکان می دهد وضعیت متغیر ماسه برنامه را که از راه دور در GKE اجرا می شود بررسی کنید.
  5. در قسمت متغیرها کلیک کنید تا متغیر "هدف" را پیدا کنید.
  6. مقدار فعلی را به عنوان "محلی" مشاهده کنید
  7. روی نام متغیر "target" دوبار کلیک کنید و در پنجره بازشو مقدار را به چیزی متفاوت مانند "Cloud" تغییر دهید.
  8. روی دکمه Continue در کنترل پنل اشکال زدایی کلیک کنید
  9. پاسخ را در مرورگر خود مرور کنید که اکنون مقدار به روز شده ای را که وارد کرده اید نشان می دهد.

بارگذاری مجدد داغ

  1. عبارت را تغییر دهید تا مقدار دیگری مانند "Hello from %s Code" را برگردانید
  2. فایل به طور خودکار در کانتینرهای راه دور در GKE ذخیره و همگام سازی می شود
  3. مرورگر خود را به روز کنید تا نتایج به روز شده را ببینید.
  4. با کلیک بر روی مربع قرمز در نوار ابزار اشکال زدایی، جلسه اشکال زدایی را متوقف کنید a13d42d726213e6c.png

5. ایجاد یک سرویس استراحت ساده CRUD

در این مرحله برنامه شما به طور کامل برای توسعه کانتینری پیکربندی شده است و شما در جریان کار توسعه اولیه با Cloud Code قدم زده اید. در بخش‌های بعدی، آنچه را که آموخته‌اید، با افزودن نقاط پایانی سرویس استراحت برای اتصال به پایگاه داده مدیریت‌شده در Google Cloud تمرین می‌کنید.

پیکربندی وابستگی ها

کد برنامه از یک پایگاه داده برای حفظ بقیه داده های سرویس استفاده می کند. با افزودن موارد زیر در pom.xl، اطمینان حاصل کنید که وابستگی ها در دسترس هستند

  1. فایل pom.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>

سرویس بقیه را کد کنید

نقل قول.java

یک فایل به نام Quote.java در /src/main/java/com/example/springboot/ ایجاد کنید و در کد زیر کپی کنید. این مدل Entity را برای شی Quote استفاده شده در برنامه تعریف می کند.

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

فایلی به نام 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) {
        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);
        }
    }    
}

افزودن تنظیمات پایگاه داده

application.yaml

پیکربندی را برای پایگاه داده باطن مورد دسترسی سرویس اضافه کنید. فایلی به نام فایل application.yaml را در زیر src/main/resources ویرایش کنید (یا ایجاد کنید) و یک پیکربندی Spring پارامتری برای backend اضافه کنید.

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

افزودن مهاجرت پایگاه داده

ایجاد یک پوشه در src/main/resources/db/migration/

یک فایل 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 Console ذکر شده است - لطفا مقدار را تغییر دهید!
  • DB_USER و DB_PASS - همانطور که در پیکربندی نمونه CloudSQL تنظیم شده است، به عنوان یک Secret در 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

استقرار و اعتبارسنجی برنامه

  1. در قسمت پایین Cloud Shell Editor، Cloud Code را انتخاب کنید و سپس Debug on Kubernetes را در بالای صفحه انتخاب کنید.
  2. وقتی ساخت و آزمایش انجام شد، برگه خروجی می‌گوید: Resource deployment/demo-app status completed successfully ، و یک نشانی اینترنتی فهرست می‌شود: «URL بازارسال شده از برنامه آزمایشی سرویس: http://localhost:8080»
  3. مشاهده نقل قول های تصادفی

از Cloudshell Terminal، دستور زیر را چندین بار در مقابل نقطه پایانی نقل قول تصادفی اجرا کنید. تماس های مکرر را مشاهده کنید که نقل قول های مختلف را برمی گرداند

curl -v 127.0.0.1:8080/random-quote
  1. یک نقل قول اضافه کنید

یک نقل قول جدید با id=6 با استفاده از دستور فهرست شده در زیر ایجاد کنید و مشاهده کنید که درخواست برگشت داده می شود.

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
  1. یک نقل قول را حذف کنید

اکنون نقل قولی را که با روش حذف اضافه کرده اید حذف کنید و کد پاسخ HTTP/1.1 204 را مشاهده کنید.

curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. خطای سرور

با اجرای مجدد آخرین درخواست پس از حذف ورودی، حالت خطا را تجربه کنید

curl -v -X DELETE 127.0.0.1:8080/quotes/6

توجه داشته باشید که پاسخ یک HTTP:500 Internal Server Error برمی‌گرداند.

برنامه را اشکال زدایی کنید

در بخش قبل، هنگام تلاش برای حذف یک ورودی که در پایگاه داده نبود، یک حالت خطا در برنامه پیدا کردید. در این بخش یک نقطه انفصال برای پیدا کردن مشکل تعیین می کنید. این خطا در عملیات DELETE رخ داده است، بنابراین شما با کلاس QuoteController کار خواهید کرد.

  1. src.main.java.com.example.springboot.QuoteController.java را باز کنید
  2. متد deleteQuote() پیدا کنید
  3. خطی را که در آن یک آیتم از پایگاه داده حذف می شود پیدا کنید: quoteRepository.deleteById(id);
  4. با کلیک بر روی فضای خالی سمت چپ شماره خط، یک نقطه شکست در آن خط تعیین کنید.
  5. یک نشانگر قرمز ظاهر می شود که نشان می دهد نقطه شکست تنظیم شده است
  6. دوباره دستور delete را اجرا کنید
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. با کلیک بر روی نماد در ستون سمت چپ به نمای اشکال زدایی برگردید
  2. خط اشکال زدایی متوقف شده در کلاس QuoteController را مشاهده کنید.
  3. در دیباگر، روی نماد step over کلیک کنید b814d39b2e5f3d9e.png و مشاهده کنید که یک استثنا پرتاب شده است
  4. توجه کنید که یک RuntimeException was caught. این یک خطای سرور داخلی 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

کد را به روز کنید

کد نادرست است و بلوک استثنا باید دوباره ساخته شود تا استثنای EmptyResultDataAccessException را بگیرد و یک کد وضعیت HTTP 404 پیدا نشد.

خطا را تصحیح کنید.

  1. در حالی که جلسه Debug همچنان در حال اجرا است، با فشار دادن دکمه "ادامه" در کنترل پنل اشکال زدایی، درخواست را تکمیل کنید.
  2. بعد بلوک زیر را به کد اضافه کنید:
       } catch (EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }

روش باید به شکل زیر باشد

    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);
        }
    }
  1. دستور delete را دوباره اجرا کنید
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. از طریق دیباگر گام بردارید و مشاهده کنید که EmptyResultDataAccessException دستگیر شده و یک HTTP 404 Not Found به تماس گیرنده باز می گردد.
   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /quotes/6 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404
< Content-Length: 0
< Date: 
<
* Connection #0 to host 127.0.0.1 left intact
  1. با کلیک بر روی مربع قرمز در نوار ابزار اشکال زدایی، جلسه اشکال زدایی را متوقف کنید a13d42d726213e6c.png

6. پاکسازی

تبریک می گویم! در این آزمایشگاه شما یک برنامه جدید جاوا را از ابتدا ایجاد کرده اید و آن را برای کارکرد موثر با کانتینرها پیکربندی کرده اید. سپس برنامه خود را به دنبال همان جریان توسعه‌دهنده موجود در پشته‌های برنامه‌های سنتی، در یک خوشه راه‌دور GKE مستقر کرده و اشکال زدایی کردید.

برای تمیز کردن پس از تکمیل آزمایشگاه:

  1. فایل های مورد استفاده در آزمایشگاه را حذف کنید
cd ~ && rm -rf container-developer-workshop
  1. برای حذف تمام زیرساخت ها و منابع مرتبط، پروژه را حذف کنید