Cloud Workstations ve Cloud Code ile Geliştirme

1. Genel Bakış

Bu laboratuvarda, container mimarisine alınmış bir ortamda Java uygulamaları geliştirmekle görevli yazılım mühendislerinin geliştirme iş akışını kolaylaştırmak için tasarlanmış özellikler ve işlevler gösterilmektedir. Tipik container geliştirme sürecinde kullanıcının container'larla ve container derleme işlemiyle ilgili ayrıntıları anlaması gerekir. Ayrıca geliştiriciler, uygulamalarını uzak ortamlarda test edip hata ayıklama yapmak için genellikle akışlarını kesip IDE'lerinden çıkmak zorunda kalırlar. Bu eğitimde bahsedilen araçlar ve teknolojiler sayesinde geliştiriciler, IDE'lerinden ayrılmadan kapsayıcılı uygulamalarla etkili bir şekilde çalışabilir.

Öğrenecekleriniz

Bu laboratuvarda, GCP'de kapsayıcılarla geliştirme yöntemlerini öğreneceksiniz. Bu yöntemler arasında şunlar yer alır:

  • Cloud Workstations ile InnerLoop geliştirme
  • Yeni bir Java başlangıç uygulaması oluşturma
  • Geliştirme sürecini inceleme
  • Basit bir CRUD Rest hizmeti geliştirme
  • GKE kümesinde uygulamada hata ayıklama
  • Uygulamayı Cloud SQL veritabanına bağlama

58a4cdd3ed7a123a.png

2. Kurulum ve Gereksinimler

Yönlendirmesiz ortam kurulumu

  1. Google Cloud Console'da oturum açın ve yeni bir proje oluşturun veya mevcut bir projeyi yeniden kullanın. Gmail veya Google Workspace hesabınız yoksa hesap oluşturmanız gerekir.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Proje adı, bu projenin katılımcıları için görünen addır. Google API'leri tarafından kullanılmayan bir karakter dizesidir. Dilediğiniz zaman bunu güncelleyebilirsiniz.
  • Proje kimliği, tüm Google Cloud projelerinde benzersizdir ve sabittir (ayarlandıktan sonra değiştirilemez). Cloud Console, benzersiz bir dizeyi otomatik olarak oluşturur. Genellikle bu dizenin ne olduğuyla ilgilenmezsiniz. Çoğu codelab'de proje kimliğine (genellikle PROJECT_ID olarak tanımlanır) başvurmanız gerekir. Oluşturulan kimliği beğenmezseniz başka bir rastgele kimlik oluşturabilirsiniz. Dilerseniz kendi adınızı deneyerek kullanılabilir olup olmadığını kontrol edebilirsiniz. Bu adımdan sonra değiştirilemez ve proje süresince geçerli kalır.
  • Bazı API'lerin kullandığı üçüncü bir değer olan Proje Numarası da vardır. Bu üç değer hakkında daha fazla bilgiyi belgelerde bulabilirsiniz.
  1. Ardından, Cloud kaynaklarını/API'lerini kullanmak için Cloud Console'da faturalandırmayı etkinleştirmeniz gerekir. Bu codelab'i tamamlamak neredeyse hiç maliyetli değildir. Bu eğitimin ötesinde faturalandırma ücreti alınmaması için kaynakları kapatmak üzere oluşturduğunuz kaynakları veya projenin tamamını silebilirsiniz. Google Cloud'un yeni kullanıcıları 300 ABD doları değerinde ücretsiz deneme programından yararlanabilir.

Cloud Shell Düzenleyici'yi başlatma

Bu laboratuvar, Google Cloud Shell Düzenleyici ile kullanılmak üzere tasarlanmış ve test edilmiştir. Düzenleyiciye erişmek için:

  1. https://console.cloud.google.com adresinden Google projenize erişin.
  2. Sağ üst köşedeki Cloud Shell düzenleyici simgesini tıklayın.

8560cc8d45e8c112.png

  1. Pencerenizin alt kısmında yeni bir bölme açılır.
  2. Open Editor (Düzenleyiciyi Aç) düğmesini tıklayın.

9e504cb98a6a8005.png

  1. Düzenleyici, sağda bir gezgin ve merkezi alanda bir düzenleyiciyle açılır.
  2. Ekranın alt kısmında bir terminal bölmesi de bulunmalıdır.
  3. Terminal açık DEĞİLSE yeni bir terminal penceresi açmak için `ctrl+`` tuş kombinasyonunu kullanın.

gcloud'u ayarlama

Cloud Shell'de proje kimliğinizi ve uygulamanızı dağıtmak istediğiniz bölgeyi ayarlayın. Bunları PROJECT_ID ve REGION değişkenleri olarak kaydedin.

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

Kaynak kodu klonlama

Bu laboratuvarın kaynak kodu, GitHub'daki GoogleCloudPlatform'da bulunan container-developer-workshop'ta yer alır. Aşağıdaki komutla klonlayın ve dizine gidin.

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

Bu laboratuvarda kullanılan altyapıyı sağlama

Bu laboratuvarda, GKE'ye kod dağıtacak ve Cloud SQL veritabanında depolanan verilere erişeceksiniz. Aşağıdaki kurulum komut dosyası bu altyapıyı sizin için hazırlar. Hazırlama işlemi 25 dakikadan uzun sürer. Sonraki bölüme geçmeden önce komut dosyasının tamamlanmasını bekleyin.

./setup_with_cw.sh &

Cloud Workstations kümesi

Cloud Console'da Cloud Workstations'ı açın. Kümenin READY durumuna gelmesini bekleyin.

305e1a3d63ac7ff6.png

İş istasyonu yapılandırması oluşturma

Cloud Shell oturumunuzun bağlantısı kesildiyse "Yeniden bağlan"ı tıklayın ve ardından proje kimliğini ayarlamak için gcloud CLI komutunu çalıştırın. Komutu çalıştırmadan önce aşağıdaki örnek proje kimliğini Qwiklabs proje kimliğinizle değiştirin.

gcloud config set project qwiklabs-gcp-project-id

Cloud Workstations yapılandırması oluşturmak için terminalde aşağıdaki komut dosyasını çalıştırın.

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

Yapılandırmalar bölümündeki sonuçları doğrulayın. READY durumuna geçiş 2 dakika sürer.

7a6af5aa2807a5f2.png

Console'da Cloud Workstations'ı açın ve yeni bir örnek oluşturun.

a53adeeac81a78c8.png

Adı my-workstation olarak değiştirin ve mevcut yapılandırmayı seçin: codeoss-java.

f21c216997746097.png

İş istasyonları bölümündeki sonuçları doğrulayın.

66a9fc8b20543e32.png

İş istasyonunu başlatma

İş istasyonunu başlatın.

c91bb69b61ec8635.png

Adres çubuğundaki simgeyi tıklayarak üçüncü taraf çerezlerine izin verin. 1b8923e2943f9bc4.png

fcf9405b6957b7d7.png

"Site çalışmıyor mu?" seçeneğini tıklayın.

36a84c0e2e3b85b.png

"Çerezlere izin ver"i tıklayın.

2259694328628fba.png

İş istasyonu başlatıldıktan sonra Code OSS IDE'nin açıldığını görürsünüz. İş istasyonu IDE'sindeki Başlarken sayfasında "Bitti olarak işaretle"yi tıklayın.

94874fba9b74cc22.png

3. Yeni bir Java başlangıç uygulaması oluşturma

Bu bölümde, spring.io tarafından sağlanan örnek uygulamayı kullanarak sıfırdan yeni bir Java Spring Boot uygulaması oluşturacaksınız. Yeni bir Terminal açın.

c31d48f2e4938c38.png

Örnek uygulamayı klonlama

  1. Başlangıç uygulaması oluşturma
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

Bu mesajı görürseniz iş istasyonuna kopyalayıp yapıştırabilmek için İzin ver düğmesini tıklayın.

58149777e5cc350a.png

  1. Uygulamanın sıkıştırmasını açın
unzip sample-app.zip -d sample-app
  1. "sample-app" klasörünü açın.
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

spring-boot-devtools ve Jib'i ekleyin

Spring Boot Geliştirici Araçları'nı etkinleştirmek için düzenleyicinizdeki gezginden pom.xml dosyasını bulup açın. Ardından, aşağıdaki kodu <description>Demo project for Spring Boot</description> açıklama satırının sonrasına yapıştırın.

  1. pom.xml dosyasına spring-boot-devtools ekleyin

Projenin kök dizinindeki pom.xml dosyasını açın. Description girişinden sonra aşağıdaki yapılandırmayı ekleyin.

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. pom.xml dosyasında jib-maven-plugin'i etkinleştirin.

Jib, Google'ın açık kaynaklı bir Java container oluşturma aracıdır. Java geliştiricilerin, bildikleri Java araçlarını kullanarak container oluşturmasına olanak tanır. Jib, uygulamanızı container görüntüsüne paketleme adımlarının tümünü yöneten hızlı ve basit bir container görüntüsü oluşturucudur. Dockerfile yazmanızı veya Docker'ın yüklü olmasını gerektirmez ve doğrudan Maven ve Gradle'e entegre edilmiştir.

pom.xml dosyasında aşağı kaydırın ve Build bölümünü Jib eklentisini içerecek şekilde güncelleyin. Tamamlandığında derleme bölümü aşağıdaki gibi olmalıdır.

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>

Manifest Dosyaları Oluşturma

Skaffold, container geliştirmeyi basitleştirmek için entegre araçlar sunar. Bu adımda, temel Kubernetes YAML dosyalarını otomatik olarak oluşturacak olan Skaffold'u başlatacaksınız. Bu işlem, Dockerfile gibi container görüntüsü tanımlarının bulunduğu dizinleri tanımlamaya çalışır ve ardından her biri için bir dağıtım ve hizmet manifestosu oluşturur.

İşlemi başlatmak için Terminal'de aşağıdaki komutu çalıştırın.

d869e0cd38e983d7.png

  1. Terminalde aşağıdaki komutu çalıştırın.
skaffold init --generate-manifests
  1. İstendiğinde:
  • İmlecinizi Jib Maven Plugin simgesine taşımak için okları kullanın.
  • Seçeneği belirlemek için boşluk tuşuna basın.
  • Devam etmek için Enter tuşuna basın
  1. Bağlantı noktası olarak 8080 girin.
  2. Yapılandırmayı kaydetmek için y girin.

Çalışma alanına skaffold.yaml ve deployment.yaml olmak üzere iki dosya eklenir.

Skaffold çıkışı:

b33cc1e0c2077ab8.png

Uygulama adını güncelleme

Yapılandırmaya dahil edilen varsayılan değerler şu anda uygulamanızın adıyla eşleşmiyor. Dosyaları, varsayılan değerler yerine uygulama adınıza referans verecek şekilde güncelleyin.

  1. Skaffold yapılandırmasındaki girişleri değiştirme
  • skaffold.yaml uygulamasını aç
  • Şu anda pom-xml-image olarak ayarlanmış resim adını seçin.
  • Sağ tıklayın ve Tüm Oluşumları Değiştir'i seçin.
  • Yeni adı demo-app olarak yazın.
  1. Kubernetes yapılandırmasındaki girişleri değiştirme
  • deployment.yaml dosyasını aç
  • Şu anda pom-xml-image olarak ayarlanmış resim adını seçin.
  • Sağ tıklayın ve Tüm Oluşumları Değiştir'i seçin.
  • Yeni adı demo-app olarak yazın.

Otomatik senkronizasyon modunu etkinleştirme

Optimize edilmiş bir sıcak yeniden yükleme deneyimi sağlamak için Jib tarafından sunulan Senkronizasyon özelliğini kullanacaksınız. Bu adımda, derleme işlemi sırasında bu özellikten yararlanmak için Skaffold'u yapılandıracaksınız.

Skaffold yapılandırmasında yapılandırdığınız "sync" profilinin, önceki adımda yapılandırdığınız Spring "sync" profilinden yararlandığını unutmayın. Bu adımda spring-dev-tools desteğini etkinleştirmiştiniz.

  1. Skaffold yapılandırmasını güncelleme

skaffold.yaml dosyasında, dosyanın derleme bölümünün tamamını aşağıdaki spesifikasyonla değiştirin. Dosyanın diğer bölümlerini değiştirmeyin.

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

Varsayılan rota ekleme

/src/main/java/com/example/springboot/ klasöründe HelloController.java adlı bir dosya oluştur.

a624f5dd0c477c09.png

Varsayılan bir http rotası oluşturmak için aşağıdaki içerikleri dosyaya yapıştırın.

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. Geliştirme sürecini inceleme

Bu bölümde, temel süreçleri öğrenmek ve başlangıç uygulamanızın yapılandırmasını ve kurulumunu doğrulamak için Cloud Code eklentisini kullanarak birkaç adımı inceleyeceksiniz.

Cloud Code, geliştirme sürecinizi kolaylaştırmak için Skaffold ile entegre olur. Aşağıdaki adımlarda GKE'ye dağıtım yaptığınızda Cloud Code ve Skaffold, container görüntünüzü otomatik olarak oluşturur, Container Registry'ye aktarır ve ardından uygulamanızı GKE'ye dağıtır. Bu işlem, geliştirici akışından ayrıntıları soyutlayarak arka planda gerçekleşir. Cloud Code, kapsayıcı tabanlı geliştirmeye geleneksel hata ayıklama ve anında senkronizasyon özellikleri sunarak geliştirme sürecinizi de iyileştirir.

Google Cloud'da oturum açma

Cloud Code simgesini tıklayın ve "Google Cloud'da oturum aç"ı seçin:

1769afd39be372ff.png

"Oturum açmaya devam et"i tıklayın.

923bb1c8f63160f9.png

Terminal'deki çıktıyı kontrol edin ve bağlantıyı açın:

517fdd579c34aa21.png

Qwiklabs öğrenci kimlik bilgilerinizle giriş yapın.

db99b345f7a8e72c.png

"İzin ver"i seçin:

a5376553c430ac84.png

Doğrulama kodunu kopyalayın ve iş istasyonu sekmesine dönün.

6719421277b92eac.png

Doğrulama kodunu yapıştırıp Enter tuşuna basın.

e9847cfe3fa8a2ce.png

Kubernetes kümesi ekleme

  1. Küme ekleme

62a3b97bdbb427e5.png

  1. Google Kubernetes Engine'i seçin:

9577de423568bbaa.png

  1. Projeyi seçin.

c5202fcbeebcd41c.png

  1. İlk kurulumda oluşturulan "quote-cluster"ı seçin.

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

gcloud CLI'yı kullanarak mevcut proje kimliğini ayarlama

Bu laboratuvarın proje kimliğini Qwiklabs sayfasından kopyalayın.

fcff2d10007ec5bc.png

Proje kimliğini ayarlamak için gcloud CLI komutunu çalıştırın. Komutu çalıştırmadan önce örnek proje kimliğini değiştirin.

gcloud config set project qwiklabs-gcp-project-id

Örnek çıktı:

f1c03d01b7ac112c.png

Kubernetes'te hata ayıklama

  1. Sol bölmenin en alt kısmında Cloud Code'u seçin.

60b8e4e95868b561.png

  1. DEVELOPMENT SESSIONS (GELİŞTİRME OTURUMLARI) altında görünen panelde Debug on Kubernetes'i (Kubernetes'te Hata Ayıklama) seçin.

Seçenek görünmüyorsa aşağı kaydırın.

7d30833d96632ca0.png

  1. Mevcut bağlamı kullanmak için "Evet"i seçin.

a024a69b64de7e9e.png

  1. İlk kurulum sırasında oluşturulan "quote-cluster"ı seçin.

faebabf372e3caf0.png

  1. Container Repository'yi (Kapsayıcı Deposu) seçin.

fabc6dce48bae1b4.png

  1. İlerleme durumunu ve bildirimleri görüntülemek için alt bölmede Çıkış sekmesini seçin.
  2. Ek ayrıntıları ve kapsayıcılardan canlı olarak yayınlanan günlükleri görüntülemek için sağdaki kanal açılır listesinde "Kubernetes: Run/Debug - Detailed"ı seçin.

86b44c59db58f8f3.png

Uygulamanın dağıtılmasını bekleyin.

9f37706a752829fe.png

  1. GKE'de dağıtılan uygulamayı Cloud Console'da inceleyin.

6ad220e5d1980756.png

  1. OUTPUT sekmesindeki açılır listeden "Kubernetes: Run/Debug" (Kubernetes: Çalıştır/Hata Ayıkla) seçeneğini belirleyerek basitleştirilmiş görünüme dönün.
  2. Derleme ve testler tamamlandığında Çıkış sekmesinde Resource deployment/demo-app status completed successfully ifadesi gösterilir ve "Forwarded URL from service demo-app: http://localhost:8080" URL'si listelenir.
  3. Cloud Code terminalinde, çıkıştaki URL'nin (http://localhost:8080) üzerine gelin ve ardından görüntülenen araç ipucunda Bağlantıyı takip et'i seçin.

28c5539880194a8e.png

Yeni sekme açılır ve aşağıdaki çıktıyı görürsünüz:

d67253ca16238f49.png

Kesme noktalarından yararlanma

  1. /src/main/java/com/example/springboot/HelloController.java adresindeki HelloController.java uygulamasını açın.
  2. Kök yol için return String.format("Hello from your %s environment!", target); ifadesini içeren dönüş ifadesini bulun.
  3. Satır numarasının solundaki boş alanı tıklayarak bu satıra kesme noktası ekleyin. Kesme noktasının ayarlandığını belirtmek için kırmızı bir gösterge gösterilir.

5027dc6da2618a39.png

  1. Tarayıcınızı yeniden yükleyin. Hata ayıklayıcının, işlemi kesme noktasında durdurduğunu ve GKE'de uzaktan çalışan uygulamanın değişkenlerini ve durumunu incelemenize olanak tanıdığını unutmayın.

71acfb426623cec2.png

  1. "Hedef" değişkenini bulana kadar değişkenler bölümünü tıklayın.
  2. Mevcut değeri "local" olarak gözlemleyin.

a1160d2ed2bb5c82.png

  1. "Hedef" değişken adını çift tıklayın ve pop-up pencerede

değeri "Cloud Workstations" olarak değiştirin.

e597a556a5c53f32.png

  1. Hata ayıklama kontrol panelinde Devam düğmesini tıklayın.

ec17086191770d0d.png

  1. Tarayıcınızda, az önce girdiğiniz güncellenmiş değeri gösteren yanıtı inceleyin.

6698a9db9e729925.png

  1. Satır numarasının solundaki kırmızı göstergeyi tıklayarak kesme noktasını kaldırın. Bu işlem, laboratuvarda ilerlerken kodunuzun bu satırda yürütmeyi durdurmasını önler.

Hot Reload

  1. İfadeyi "Merhaba %s Kod" gibi farklı bir değer döndürecek şekilde değiştirin.
  2. Dosya otomatik olarak kaydedilir ve GKE'deki uzak kapsayıcılara senkronize edilir.
  3. Güncellenen sonuçları görmek için tarayıcınızı yenileyin.
  4. Hata ayıklama araç çubuğundaki kırmızı kareyi tıklayarak hata ayıklama oturumunu durdurun.

a541f928ec8f430e.png c2752bb28d82af86.png

"Evet, her çalıştırmadan sonra temizle"yi seçin.

984eb2fa34867d70.png

5. Basit bir CRUD Rest hizmeti geliştirme

Bu noktada uygulamanız, container'lı geliştirme için tamamen yapılandırılmış ve Cloud Code ile temel geliştirme iş akışını tamamlamış olursunuz. Aşağıdaki bölümlerde, Google Cloud'da yönetilen bir veritabanına bağlanan REST hizmeti uç noktaları ekleyerek öğrendiklerinizi uygulayacaksınız.

Bağımlılıkları Yapılandırma

Uygulama kodu, geri kalan hizmet verilerini kalıcı hale getirmek için bir veritabanı kullanır. pom.xl dosyasına aşağıdakileri ekleyerek bağımlılıkların kullanılabilir olduğundan emin olun.

  1. pom.xml dosyasını açın ve yapılandırmanın bağımlılıklar bölümüne aşağıdakileri ekleyin.

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>

Code REST hizmeti

Quote.java

/src/main/java/com/example/springboot/ içinde Quote.java adlı bir dosya oluşturun ve aşağıdaki kodu kopyalayın. Bu, uygulamada kullanılan Fiyat Teklifi nesnesinin Varlık modelini tanımlar.

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

src/main/java/com/example/springboot konumunda QuoteRepository.java adlı bir dosya oluşturun ve aşağıdaki kodu kopyalayın.

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();
}

Bu kod, verileri kalıcı hale getirmek için JPA'yı kullanır. Bu sınıf, Spring JPARepository arayüzünü genişletir ve özel kod oluşturulmasına olanak tanır. Eklediğiniz koda findRandomQuote özel bir yöntem ekleyin.

QuoteController.java

Hizmetin uç noktasını kullanıma sunmak için QuoteController sınıfı bu işlevi sağlar.

src/main/java/com/example/springboot konumunda QuoteController.java adlı bir dosya oluşturun ve aşağıdaki içerikleri kopyalayın.

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);
        }
    }

}

Veritabanı Yapılandırmaları Ekleme

application.yaml

Hizmetin eriştiği arka uç veritabanı için yapılandırma ekleyin. application.yaml adlı dosyayı src/main/resources altında düzenleyin (yoksa oluşturun) ve arka uç için parametrelendirilmiş bir Spring yapılandırması ekleyin.

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

Veritabanı Taşıma Ekleme

db/migration altında klasör oluşturma src/main/resources

SQL dosyası oluşturun: V1__create_quotes_table.sql

Aşağıdaki içeriği dosyaya yapıştırın.

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 Yapılandırması

deployment.yaml dosyasına yapılan aşağıdaki eklemeler, uygulamanın Cloud SQL örneklerine bağlanmasına olanak tanır.

  • TARGET: Değişkeni, uygulamanın yürütüldüğü ortamı belirtecek şekilde yapılandırır.
  • SPRING_PROFILES_ACTIVE: cloud-dev olarak yapılandırılacak etkin Spring profilini gösterir.
  • DB_HOST: Veritabanı örneği oluşturulurken not edilen veya Google Cloud Console'un gezinme menüsünde SQL simgesi tıklanarak elde edilen veritabanının özel IP'si. Lütfen değeri değiştirin.
  • DB_USER ve DB_PASS: Cloud SQL örneği yapılandırmasında ayarlandığı gibi, GCP'de Secret olarak depolanır.

deployment.yaml dosyanızı aşağıdaki içeriklerle güncelleyin.

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

Terminalde aşağıdaki komutları çalıştırarak DB_HOST değerini veritabanınızın adresiyle değiştirin:

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 dosyasını açın ve DB_HOST değerinin Instance IP ile güncellendiğini doğrulayın.

fd63c0aede14beba.png

Uygulamayı Dağıtma ve Doğrulama

  1. Cloud Shell Düzenleyici'nin alt kısmındaki bölmede Cloud Code'u, ardından ekranın üst kısmında Kubernetes'te hata ayıklama'yı seçin.

33a5cf41aae91adb.png

  1. Derleme ve testler tamamlandığında Çıkış sekmesinde Resource deployment/demo-app status completed successfully ifadesi gösterilir ve "Forwarded URL from service demo-app: http://localhost:8080" URL'si listelenir. Bazen bağlantı noktasının 8081 gibi farklı olabileceğini unutmayın. Bu durumda uygun değeri ayarlayın. Terminalde URL değerini ayarlayın.
export URL=localhost:8080
  1. Rastgele Alıntıları Görüntüleme

Terminalden, aşağıdaki komutu random-quote uç noktasına karşı birden çok kez çalıştırın. Tekrarlanan aramaların farklı alıntılar döndürdüğünü gözlemleme

curl $URL/random-quote | jq
  1. Alıntı ekleme

Aşağıda listelenen komutu kullanarak id=6 ile yeni bir fiyat teklifi oluşturun ve isteğin tekrar gönderildiğini gözlemleyin.

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. Alıntı silme

Şimdi, az önce eklediğiniz alıntıyı silme yöntemiyle silin ve HTTP/1.1 204 yanıt kodunu gözlemleyin.

curl -v -X DELETE $URL/quotes/6
  1. Sunucu Hatası

Giriş silindikten sonra son isteği tekrar çalıştırarak hata durumunu deneyimleme

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

Yanıtın HTTP:500 Internal Server Error döndürdüğünü fark edin.

Uygulamada hata ayıklama

Önceki bölümde, veritabanında olmayan bir girişi silmeye çalıştığınızda uygulamada bir hata durumuyla karşılaştınız. Bu bölümde, sorunu bulmak için bir kesme noktası ayarlayacaksınız. Hata, DELETE işleminde oluştuğu için QuoteController sınıfıyla çalışacaksınız.

  1. src/main/java/com/example/springboot/QuoteController.java uygulamasını aç
  2. deleteQuote() yöntemini bulma
  3. Şu satırı bulun: Optional<Quote> quote = quoteRepository.findById(id);
  4. Satır numarasının solundaki boş alanı tıklayarak ilgili satırda bir kesme noktası ayarlayın.
  5. Kesme noktasının ayarlandığını gösteren kırmızı bir gösterge görünür.
  6. delete komutunu tekrar çalıştırın.
curl -v -X DELETE $URL/quotes/6
  1. Sol sütundaki simgeyi tıklayarak hata ayıklama görünümüne geri dönün.
  2. Hata ayıklama satırının QuoteController sınıfında durdurulduğunu gözlemleyin.
  3. Hata ayıklayıcıda step over simgesini tıklayın. b814d39b2e5f3d9e.png
  4. Kodun, istemciye Dahili Sunucu Hatası HTTP 500 döndürdüğünü gözlemleyin. Bu durum ideal değildir.
   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

Kodu güncelleme

Kod yanlış ve else bloğu, HTTP 404 bulunamadı durum kodunu geri gönderecek şekilde yeniden düzenlenmelidir.

Hatayı düzeltin.

  1. Hata ayıklama oturumu çalışmaya devam ederken hata ayıklama denetim panelinde "devam" düğmesine basarak isteği tamamlayın.
  2. Ardından else bloğunu kodla değiştirin:
       else {
                return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
            }

Yöntem aşağıdaki gibi görünmelidir:

@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. Silme komutunu yeniden çalıştırma
curl -v -X DELETE $URL/quotes/6
  1. Hata ayıklayıcıda adımları izleyin ve arayana döndürülen HTTP 404 Not Found yanıtını gözlemleyin.
   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. Hata ayıklama araç çubuğundaki kırmızı kareyi tıklayarak hata ayıklama oturumunu durdurun.

12bc3c82f63dcd8a.png

6f19c0f855832407.png

6. Tebrikler

Tebrikler! Bu laboratuvarda sıfırdan yeni bir Java uygulaması oluşturdunuz ve bu uygulamayı kapsayıcılarla etkili bir şekilde çalışacak şekilde yapılandırdınız. Ardından, geleneksel uygulama yığınlarında bulunan geliştirici akışını izleyerek uygulamanızı uzak bir GKE kümesine dağıtıp hatalarını ayıklarsınız.

Öğrendikleriniz

  • Cloud Workstations ile InnerLoop geliştirme
  • Yeni bir Java başlangıç uygulaması oluşturma
  • Geliştirme sürecini inceleme
  • Basit bir CRUD REST hizmeti geliştirme
  • GKE kümesinde uygulamada hata ayıklama
  • Uygulamayı Cloud SQL veritabanına bağlama