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

۱. مرور کلی

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

آنچه یاد خواهید گرفت

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

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

58a4cdd3ed7a123a.png

۲. تنظیمات و الزامات

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

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

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

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

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

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

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

8560cc8d45e8c112.png

  1. یک پنل جدید در پایین پنجره شما باز خواهد شد
  2. روی دکمه باز کردن ویرایشگر کلیک کنید

9e504cb98a6a8005.png

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

جی کلود را تنظیم کنید

در 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 در GoogleCloudPlatform در GitHub قرار دارد. آن را با دستور زیر کلون کنید و سپس به دایرکتوری بروید.

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

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

در این آزمایش، شما کد را در GKE مستقر خواهید کرد و به داده‌های ذخیره شده در پایگاه داده CloudSQL دسترسی خواهید داشت. اسکریپت راه‌اندازی زیر این زیرساخت را برای شما آماده می‌کند. فرآیند آماده‌سازی بیش از ۲۵ دقیقه طول خواهد کشید. قبل از رفتن به بخش بعدی، منتظر بمانید تا اسکریپت تکمیل شود.

./setup_with_cw.sh &

خوشه ایستگاه‌های کاری ابری

ایستگاه‌های کاری ابری را در کنسول ابری باز کنید. منتظر بمانید تا کلاستر در وضعیت READY قرار گیرد.

305e1a3d63ac7ff6.png

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

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

gcloud config set project qwiklabs-gcp-project-id

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

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

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

7a6af5aa2807a5f2.png

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

a53adeeac81a78c8.png

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

f21c216997746097.png

نتایج را در بخش Workstations تأیید کنید.

66a9fc8b20543e32.png

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

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

c91bb69b61ec8635.png

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

fcf9405b6957b7d7.png

روی «سایت کار نمی‌کند؟» کلیک کنید.

36a84c0e2e3b85b.png

روی «اجازه دادن به کوکی‌ها» کلیک کنید.

۲۲۵۹۶۹۴۳۲۸۶۲۸fba.png

پس از راه‌اندازی ایستگاه کاری، خواهید دید که Code OSS IDE ظاهر می‌شود. در صفحه شروع به کار، روی «Mark Done» در صفحه اول IDE ایستگاه کاری کلیک کنید.

۹۴۸۷۴fba9b74cc22.png

۳. ایجاد یک برنامه‌ی آغازین جاوای جدید

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

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

اگر این پیام را مشاهده کردید، روی دکمه‌ی «اجازه دادن» کلیک کنید تا بتوانید در ایستگاه کاری کپی پیست کنید.

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

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

d869e0cd38e983d7.png

  1. دستور زیر را در ترمینال اجرا کنید
skaffold init --generate-manifests
  1. وقتی از شما خواسته شد:
  • از فلش‌ها برای انتقال مکان‌نما به Jib Maven Plugin استفاده کنید.
  • برای انتخاب گزینه مورد نظر، کلید فاصله (spacebar) را فشار دهید.
  • برای ادامه، اینتر را فشار دهید
  1. برای پورت، عدد ۸۰۸۰ را وارد کنید.
  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 تایپ کنید.

فعال کردن حالت همگام‌سازی خودکار

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

توجه داشته باشید که پروفایل "sync" که در پیکربندی Skaffold پیکربندی می‌کنید، از پروفایل "sync" 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);
    }
}

۴. گام برداشتن در مسیر توسعه

در این بخش، چند مرحله را با استفاده از افزونه Cloud Code طی خواهید کرد تا فرآیندهای اولیه را بیاموزید و پیکربندی و راه‌اندازی برنامه اولیه خود را تأیید کنید.

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

وارد فضای ابری گوگل شوید

روی آیکون Cloud Code کلیک کنید و گزینه «ورود به سیستم Google Cloud» را انتخاب کنید:

۱۷۶۹afd39be372ff.png

روی «ادامه برای ورود» کلیک کنید.

۹۲۳bb1c8f63160f9.png

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

517fdd579c34aa21.png

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

db99b345f7a8e72c.png

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

a5376553c430ac84.png

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

6719421277b92eac.png

کد تأیید را جایگذاری کنید و Enter را بزنید.

e9847cfe3fa8a2ce.png

اضافه کردن کلاستر Kubernetes

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

62a3b97bdbb427e5.png

  1. موتور گوگل کوبرنتیز را انتخاب کنید:

9577de423568bbaa.png

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

c5202fcbeebcd41c.png

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

۳۶۶cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

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

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

fcff2d10007ec5bc.png

دستور 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. گزینه "quote-cluster" که در هنگام راه‌اندازی اولیه ایجاد شده بود را انتخاب کنید.

faebabf372e3caf0.png

  1. مخزن کانتینر را انتخاب کنید.

fabc6dce48bae1b4.png

  1. برای مشاهده پیشرفت و اعلان‌ها، برگه خروجی را در پنل پایین انتخاب کنید.
  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، نشانگر ماوس را روی URL موجود در خروجی (http://localhost:8080) نگه دارید و سپس در ابزار نمایش داده شده، گزینه Follow link را انتخاب کنید.

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. در بخش متغیرها کلیک کنید تا متغیر "Target" را پیدا کنید.
  2. مقدار فعلی را به عنوان "محلی" مشاهده کنید

a1160d2ed2bb5c82.png

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

مقدار را به "ایستگاه‌های کاری ابری" تغییر دهید

e597a556a5c53f32.png

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

ec17086191770d0d.png

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

6698a9db9e729925.png

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

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

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

a541f928ec8f430e.pngc2752bb28d82af86.png

گزینه «بله، بعد از هر اجرا تمیز می‌شود» را انتخاب کنید.

984eb2fa34867d70.png

۵. توسعه یک سرویس ساده CRUD Rest

در این مرحله، برنامه شما به طور کامل برای توسعه کانتینری پیکربندی شده است و شما گردش کار اولیه توسعه را با 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 برای ذخیره داده‌ها استفاده می‌کند. این کلاس از رابط JPARepository در Spring ارث‌بری می‌کند و امکان ایجاد کد سفارشی را فراهم می‌کند. در این کد، شما یک متد سفارشی 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);
        }
    }

}

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

برنامه.yaml

پیکربندی مربوط به پایگاه داده backend که توسط سرویس قابل دسترسی است را اضافه کنید. فایلی به نام 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 مشخص شده است - لطفاً مقدار را تغییر دهید!
  • DB_USER و DB_PASS - همانطور که در پیکربندی نمونه CloudSQL تنظیم شده است، به عنوان یک راز در GCP ذخیره می‌شود.

فایل deployment.yaml خود را با محتویات زیر به‌روزرسانی کنید.

استقرار.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 با Instance IP به‌روزرسانی شده باشد.

fd63c0aede14beba.png

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

  1. در پنل پایین ویرایشگر Cloud Shell، گزینه 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. اضافه کردن نقل قول

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

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. حذف نقل قول

حالا نقل قولی که با متد delete اضافه کردید را حذف کنید و کد پاسخ 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 not found را برگرداند.

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

  1. در حالی که جلسه اشکال‌زدایی هنوز در حال اجرا است، با فشردن دکمه «ادامه» در پنل کنترل اشکال‌زدایی، درخواست را تکمیل کنید.
  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. دستور حذف را دوباره اجرا کنید
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. با کلیک روی مربع قرمز در نوار ابزار اشکال‌زدایی، جلسه اشکال‌زدایی را متوقف کنید.

۱۲bc3c82f63dcd8a.png

6f19c0f855832407.png

۶. تبریک

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

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

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