در حال توسعه با ایستگاه های کاری ابری و کد ابری

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

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

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

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

  • توسعه InnerLoop با ایستگاه های کاری ابری
  • ایجاد یک برنامه استارت جدید جاوا
  • قدم زدن در فرآیند توسعه
  • توسعه یک سرویس استراحت ساده CRUD
  • برنامه اشکال زدایی در خوشه GKE
  • اتصال برنامه به پایگاه داده CloudSQL

58a4cdd3ed7a123a.png

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 فعال کنید . اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. برای اینکه منابع را خاموش کنید تا بیش از این آموزش متحمل صورتحساب نشوید، می توانید منابعی را که ایجاد کرده اید حذف کنید یا کل پروژه را حذف کنید. کاربران جدید 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 REGION=us-central1
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 دسترسی خواهید داشت. اسکریپت راه اندازی زیر این زیرساخت را برای شما آماده می کند. فرآیند تهیه بیش از 25 دقیقه طول خواهد کشید. قبل از رفتن به بخش بعدی منتظر بمانید تا اسکریپت کامل شود.

./setup_with_cw.sh &

Cloud Workstation Cluster

ایستگاه های کاری Cloud را در Cloud Console باز کنید. منتظر بمانید تا خوشه در وضعیت READY باشد.

305e1a3d63ac7ff6.png

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

اگر جلسه Cloud Shell شما قطع شد، روی "Reconnect" کلیک کنید و سپس دستور gcloud cli را برای تنظیم ID پروژه اجرا کنید. قبل از اجرای دستور، شناسه پروژه نمونه زیر را با ID پروژه qwiklabs خود جایگزین کنید.

gcloud config set project qwiklabs-gcp-project-id

برای ایجاد تنظیمات Cloud Workstations، اسکریپت زیر را در ترمینال اجرا کنید.

cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh

نتایج را در بخش تنظیمات تأیید کنید. انتقال به وضعیت آماده 2 دقیقه طول می کشد.

7a6af5aa2807a5f2.png

ایستگاه های کاری Cloud را در کنسول باز کنید و نمونه جدیدی ایجاد کنید.

a53adeeac81a78c8.png

نام را به my-workstation تغییر دهید و پیکربندی موجود را انتخاب کنید: codeoss-java .

f21c216997746097.png

نتایج را در بخش ایستگاه های کاری بررسی کنید.

66a9fc8b20543e32.png

ایستگاه کاری را راه اندازی کنید

ایستگاه کاری را راه اندازی و راه اندازی کنید.

c91bb69b61ec8635.png

با کلیک بر روی نماد در نوار آدرس، کوکی‌های شخص ثالث را مجاز کنید. 1b8923e2943f9bc4.png

fcf9405b6957b7d7.png

روی "سایت کار نمی کند؟" کلیک کنید.

36a84c0e2e3b85b.png

روی "Allow cookies" کلیک کنید.

2259694328628fba.png

پس از راه اندازی ایستگاه کاری، کد OSS IDE را مشاهده خواهید کرد. روی "علامت گذاری انجام شده" در صفحه شروع به کار در یکی از IDE ایستگاه کاری کلیک کنید

94874fba9b74cc22.png

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

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

c31d48f2e4938c38.png

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

  1. یک برنامه شروع ایجاد کنید
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

در صورت مشاهده این پیغام روی دکمه Allow کلیک کنید تا بتوانید در ایستگاه کاری کپی کنید.

58149777e5cc350a.png

  1. برنامه را از حالت فشرده خارج کنید
unzip sample-app.zip -d sample-app
  1. پوشه "sample-app" را باز کنید
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

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

برای فعال کردن Spring Boot DevTools، pom.xml را از کاوشگر در ویرایشگر خود پیدا کرده و باز کنید. سپس کد زیر را بعد از خط توضیحات قرار دهید که <description>Demo project for 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>

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

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

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

d869e0cd38e983d7.png

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

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

خروجی داربست:

b33cc1e0c2077ab8.png

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

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

  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/java17-debian11:debug
    sync:
      auto: true

یک مسیر پیش فرض اضافه کنید

فایلی به نام HelloController.java در پوشه /src/main/java/com/example/springboot/ ایجاد کنید.

a624f5dd0c477c09.png

برای ایجاد یک مسیر پیش‌فرض 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 برای توسعه مبتنی بر کانتینر، فرآیند توسعه شما را افزایش می‌دهد.

به Google Cloud وارد شوید

روی نماد Cloud Code کلیک کنید و "Sign in to Google Cloud" را انتخاب کنید:

1769afd39be372ff.png

روی "ادامه برای ورود به سیستم" کلیک کنید.

923bb1c8f63160f9.png

خروجی را در ترمینال بررسی کنید و پیوند را باز کنید:

517fdd579c34aa21.png

با اعتبارنامه دانشجویان Qwiklabs خود وارد شوید.

db99b345f7a8e72c.png

"مجاز" را انتخاب کنید:

a5376553c430ac84.png

کد تأیید را کپی کنید و به تب Workstation برگردید.

6719421277b92eac.png

کد تایید را بچسبانید و Enter را بزنید.

e9847cfe3fa8a2ce.png

خوشه Kubernetes را اضافه کنید

  1. یک خوشه اضافه کنید

62a3b97bdbb427e5.png

  1. موتور Google Kubernetes را انتخاب کنید:

9577de423568bbaa.png

  1. پروژه را انتخاب کنید

c5202fcbeebcd41c.png

  1. "quote-cluster" را که در تنظیمات اولیه ایجاد شده است انتخاب کنید.

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

با استفاده از gcloud cli شناسه پروژه فعلی را تنظیم کنید

شناسه پروژه را برای این آزمایشگاه از صفحه qwiklabs کپی کنید.

fcff2d10007ec5bc.png

برای تنظیم ID پروژه، دستور gcloud cli را اجرا کنید. قبل از اجرای دستور، شناسه پروژه نمونه را جایگزین کنید.

gcloud config set project qwiklabs-gcp-project-id

خروجی نمونه:

f1c03d01b7ac112c.png

اشکال زدایی در Kubernetes

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

60b8e4e95868b561.png

  1. در پانلی که در زیر DEVELOPMENT SESSIONS ظاهر می شود، Debug on Kubernetes را انتخاب کنید.

اگر گزینه قابل مشاهده نیست، به پایین بروید.

7d30833d96632ca0.png

  1. برای استفاده از زمینه فعلی "بله" را انتخاب کنید.

a024a69b64de7e9e.png

  1. "Cluster-Cluster" را انتخاب کنید که در طول راه اندازی اولیه ایجاد شده است.

faebabf372e3caf0.png

  1. Container Repository را انتخاب کنید.

fabc6dce48bae1b4.png

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

86b44c59db58f8f3.png

منتظر بمانید تا برنامه اجرا شود.

9f37706a752829fe.png

  1. برنامه مستقر شده در GKE را در Cloud Console مرور کنید.

6ad220e5d1980756.png

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

28c5539880194a8e.png

تب جدید باز می شود و خروجی را در زیر می بینید:

d67253ca16238f49.png

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

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

5027dc6da2618a39.png

  1. مرورگر خود را دوباره بارگیری کنید و توجه داشته باشید که دیباگر فرآیند را در نقطه شکست متوقف می کند و به شما امکان می دهد متغیرها و وضعیت برنامه ای را که از راه دور در GKE اجرا می شود بررسی کنید.

71acfb426623cec2.png

  1. در قسمت متغیرها کلیک کنید تا متغیر "هدف" را پیدا کنید.
  2. مقدار فعلی را به عنوان "محلی" مشاهده کنید

a1160d2ed2bb5c82.png

  1. روی نام متغیر "target" دوبار کلیک کنید و در پنجره بازشو،

مقدار را به "Cloud Workstations" تغییر دهید

e597a556a5c53f32.png

  1. روی دکمه Continue در کنترل پنل اشکال زدایی کلیک کنید

ec17086191770d0d.png

  1. پاسخ را در مرورگر خود مرور کنید که اکنون مقدار به روز شده ای را که وارد کرده اید نشان می دهد.

6698a9db9e729925.png

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

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

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

a541f928ec8f430e.pngc2752bb28d82af86.png

"Yes clean up after every run" را انتخاب کنید.

984eb2fa34867d70.png

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

در این مرحله برنامه شما به طور کامل برای توسعه کانتینری پیکربندی شده است و شما در جریان کار توسعه اولیه با Cloud Code قدم زده اید. در بخش‌های بعدی، آنچه را که آموخته‌اید با افزودن نقاط پایانی سرویس REST که به پایگاه داده مدیریت‌شده در 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>
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>javax.persistence-api</artifactId>
      <version>2.2</version>
    </dependency>

سرویس کد REST

نقل قول.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 پارامتری برای 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

مهاجرت پایگاه داده را اضافه کنید

پوشه های 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 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

deployment.yaml را باز کنید و بررسی کنید که مقدار DB_HOST با IP نمونه به روز شده باشد.

fd63c0aede14beba.png

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

  1. در قسمت پایین Cloud Shell Editor، Cloud Code را انتخاب کنید و سپس Debug on Kubernetes را در بالای صفحه انتخاب کنید.

33a5cf41aae91adb.png

  1. وقتی ساخت و آزمایش انجام شد، برگه خروجی می‌گوید: Resource deployment/demo-app status completed successfully ، و یک نشانی اینترنتی فهرست می‌شود: «URL فوروارد شده از برنامه آزمایشی سرویس: http://localhost:8080 ». توجه داشته باشید که گاهی اوقات پورت ممکن است مانند 8081 متفاوت باشد. در این صورت مقدار مناسب را تنظیم کنید. مقدار URL را در ترمینال تنظیم کنید
export URL=localhost:8080
  1. مشاهده نقل قول های تصادفی

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

curl $URL/random-quote | jq
  1. یک نقل قول اضافه کنید

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

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

curl -v -X DELETE $URL/quotes/6
  1. خطای سرور

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

curl -v -X DELETE $URL/quotes/6

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

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

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

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

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

  1. در حالی که جلسه Debug هنوز در حال اجرا است، با فشار دادن دکمه "ادامه" در کنترل پنل اشکال زدایی، درخواست را تکمیل کنید.
  2. بعد بلوک 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);
        }
    }
  1. دستور delete را دوباره اجرا کنید
curl -v -X DELETE $URL/quotes/6
  1. از اشکال‌زدا عبور کنید و مشاهده کنید که 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. با کلیک بر روی مربع قرمز در نوار ابزار اشکال زدایی، جلسه اشکال زدایی را متوقف کنید

12bc3c82f63dcd8a.png

6f19c0f855832407.png

6. تبریک می گویم

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

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

  • توسعه InnerLoop با ایستگاه های کاری ابری
  • ایجاد یک برنامه استارت جدید جاوا
  • قدم زدن در فرآیند توسعه
  • توسعه یک سرویس ساده CRUD REST
  • برنامه اشکال زدایی در خوشه GKE
  • اتصال برنامه به پایگاه داده CloudSQL