Jak połączyć aplikację w języku Go w Cloud Run z bazą danych Cloud SQL for PostgreSQL

1. Przegląd

Oprogramowanie sprzęgające Cloud SQL Go to najprostszy sposób na bezpieczne połączenie aplikacji Go z bazą danych Cloud SQL. Cloud Run to w pełni zarządzana platforma bezserwerowa, która umożliwia uruchamianie bezstanowych kontenerów wywoływanych przez żądania HTTP. W tym laboratorium dowiesz się, jak bezpiecznie połączyć aplikację Go w Cloud Run z bazą danych Cloud SQL for PostgreSQL za pomocą konta usługi z uwierzytelnianiem IAM.

Czego się nauczysz

W tym module nauczysz się:

  • Tworzenie bazy danych Cloud SQL for PostgreSQL
  • Wdrażanie aplikacji w Go w Cloud Run
  • Łączenie aplikacji z Cloud SQL za pomocą łącznika Go

Wymagania wstępne

  • Zakładamy, że użytkownik zna środowiska konsoli Cloud i Cloud Shell.

2. Zanim zaczniesz

Konfigurowanie projektu w Google Cloud

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Google, musisz je utworzyć.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Możesz ją zaktualizować w dowolnym momencie.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu (zwykle jest on oznaczony jako PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować własnej nazwy i sprawdzić, czy jest dostępna. Po tym kroku nie można go zmienić i będzie obowiązywać przez cały czas trwania projektu.
  • Warto wiedzieć, że istnieje też trzecia wartość, czyli numer projektu, z której korzystają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Ukończenie tego laboratorium nie powinno wiązać się z dużymi kosztami, a nawet z żadnymi. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.

Konfiguracja środowiska

Aktywuj Cloud Shell, klikając ikonę po prawej stronie paska wyszukiwania.

ecdc43ada29e91b.png

W Cloud Shell włącz interfejsy API:

gcloud services enable compute.googleapis.com sqladmin.googleapis.com \
  run.googleapis.com artifactregistry.googleapis.com \
  cloudbuild.googleapis.com servicenetworking.googleapis.com

Jeśli pojawi się prośba o autoryzację, kliknij „Autoryzuj”, aby kontynuować.

6356559df3eccdda.png

Wykonanie tego polecenia może potrwać kilka minut, ale powinno ostatecznie wyświetlić komunikat o sukcesie podobny do tego:

Operation "operations/acf.p2-327036483151-73d90d00-47ee-447a-b600-a6badf0eceae" finished successfully.

3. Konfigurowanie konta usługi

Utwórz i skonfiguruj konto usługi Google Cloud, które będzie używane przez Cloud Run, aby miało odpowiednie uprawnienia do łączenia się z Cloud SQL.

  1. Aby utworzyć nowe konto usługi, uruchom polecenie gcloud iam service-accounts create w ten sposób:
    gcloud iam service-accounts create quickstart-service-account \
      --display-name="Quickstart Service Account"
    
  2. Aby dodać rolę Klient Cloud SQL do utworzonego właśnie konta usługi Google Cloud, uruchom to polecenie: W Cloud Shell wyrażenie ${GOOGLE_CLOUD_PROJECT} zostanie zastąpione nazwą Twojego projektu. Możesz też dokonać wymiany ręcznie, jeśli wolisz.
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.client"
    
  3. Aby dodać rolę Użytkownik instancji Cloud SQL do utworzonego właśnie konta usługi Google Cloud, uruchom polecenie gcloud projects add-iam-policy-binding w ten sposób:
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/cloudsql.instanceUser"
    
  4. Aby dodać rolę Zapisujący dzienniki do utworzonego przed chwilą konta usługi Google Cloud, uruchom polecenie gcloud projects add-iam-policy-binding w ten sposób:
    gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
      --member="serviceAccount:quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
      --role="roles/logging.logWriter"
    

4. Konfigurowanie Cloud SQL

Uruchom polecenie gcloud sql instances create, aby utworzyć instancję Cloud SQL.

  • –database-version: typ i wersja silnika bazy danych. Jeśli nie zostanie określony, używana będzie wartość domyślna interfejsu API. Aktualnie dostępne wersje znajdziesz w dokumentacji gcloud dotyczącej wersji bazy danych.
  • –cpu: liczba rdzeni, które mają być dostępne na maszynie.
  • –memory: liczba całkowita wskazująca ilość pamięci, która ma być dostępna na maszynie. Należy podać jednostkę rozmiaru (np. 3072 MB lub 9 GB). Jeśli nie podasz jednostek, przyjmuje się, że są to GB.
  • –region: lokalizacja regionalna instancji (np. us-central1, asia-east1, us-east1).
  • –database-flags: umożliwia ustawianie flag. W tym przypadku włączamy cloudsql.iam_authentication, aby umożliwić Cloud Run łączenie się z Cloud SQL za pomocą utworzonego wcześniej konta usługi.
    gcloud sql instances create quickstart-instance \
      --database-version=POSTGRES_14 \
      --cpu=1 \
      --memory=4GB \
      --region=us-central1 \
      --database-flags=cloudsql.iam_authentication=on
    

Wykonanie tego polecenia może potrwać kilka minut.

Aby utworzyć bazę danych Cloud SQL w quickstart-instance, uruchom polecenie gcloud sql databases create.

gcloud sql databases create quickstart_db \
  --instance=quickstart-instance

Utwórz użytkownika bazy danych PostgreSQL dla utworzonego wcześniej konta usługi, aby uzyskać dostęp do bazy danych.

gcloud sql users create quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam \
  --instance=quickstart-instance \
  --type=cloud_iam_service_account

5. Przygotowywanie aplikacji

Przygotuj aplikację w Go, która odpowiada na żądania HTTP.

  1. W Cloud Shell utwórz nowy katalog o nazwie helloworld, a następnie przejdź do niego:
    mkdir helloworld
    cd helloworld
    
  2. Uruchom go mod init, aby zainicjować nową aplikację w języku Go.
    go mod init github.com/GoogleCloudPlatform/golang-samples/run/helloworld
    
  3. Zainstaluj zależność Cloud SQL Go Connector.
    go get cloud.google.com/go/cloudsqlconn
    go get cloud.google.com/go/cloudsqlconn/postgres/pgxv4
    
  4. Utwórz plik main.go z kodem aplikacji. Ten kod może:
    • Akceptowanie żądań HTTP
    • Łączenie z bazą danych
    • Przechowywanie czasu żądania HTTP w bazie danych
    • Zwraca czasy ostatnich 5 żądań
    Uruchom w Cloud Shell to polecenie:
    cat > main.go << "EOF"
    package main
    
    import (
      "database/sql"
      "encoding/json"
      "fmt"
      "log"
      "net/http"
      "os"
      "time"
    
      "cloud.google.com/go/cloudsqlconn"
      "cloud.google.com/go/cloudsqlconn/postgres/pgxv4"
    )
    
    // visitData is used to pass data to the HTML template.
    type visitData struct {
      RecentVisits []visit
    }
    
    // visit contains a single row from the visits table in the database.
    // Each visit includes a timestamp.
    type visit struct {
      VisitTime time.Time
    }
    
    // getDB creates a connection to the database
    // based on environment variables.
    func getDB() (*sql.DB, func() error) {
      cleanup, err := pgxv4.RegisterDriver("cloudsql-postgres", cloudsqlconn.WithIAMAuthN())
      if err != nil {
        log.Fatalf("Error on pgxv4.RegisterDriver: %v", err)
      }
    
      dsn := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable", os.Getenv("INSTANCE_CONNECTION_NAME"), os.Getenv("DB_USER"), os.Getenv("DB_NAME"))
      db, err := sql.Open("cloudsql-postgres", dsn)
      if err != nil {
        log.Fatalf("Error on sql.Open: %v", err)
      }
    
      createVisits := `CREATE TABLE IF NOT EXISTS visits (
        id SERIAL NOT NULL,
        created_at timestamp NOT NULL,
        PRIMARY KEY (id)
      );`
      _, err = db.Exec(createVisits)
      if err != nil {
        log.Fatalf("unable to create table: %s", err)
      }
    
      return db, cleanup
    }
    
    func main() {
      port := os.Getenv("PORT")
      if port == "" {
        port = "8080"
      }
      log.Printf("Listening on port %s", port)
      db, cleanup := getDB()
      defer cleanup()
    
      http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
        // Insert current visit
        _, err := db.Exec("INSERT INTO visits(created_at) VALUES(NOW())")
        if err != nil {
          log.Fatalf("unable to save visit: %v", err)
        }
    
        // Get the last 5 visits
        rows, err := db.Query("SELECT created_at FROM visits ORDER BY created_at DESC LIMIT 5")
        if err != nil {
          log.Fatalf("DB.Query: %v", err)
        }
        defer rows.Close()
    
        var visits []visit
        for rows.Next() {
          var visitTime time.Time
          err := rows.Scan(&visitTime)
          if err != nil {
            log.Fatalf("Rows.Scan: %v", err)
          }
          visits = append(visits, visit{VisitTime: visitTime})
        }
        response, err := json.Marshal(visitData{RecentVisits: visits})
        if err != nil {
          log.Fatalf("renderIndex: failed to parse totals with json.Marshal: %v", err)
        }
        w.Write(response)
      })
      if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
      }
    }
    
    EOF
    

Ten kod tworzy podstawowy serwer WWW, który nasłuchuje na porcie określonym przez zmienną środowiskową PORT. Aplikacja jest teraz gotowa do wdrożenia.

6. Wdrażanie aplikacji Cloud Run

Aby wdrożyć aplikację, uruchom to polecenie:

  • –region: lokalizacja regionalna instancji (np. us-central1, asia-east1, us-east1).
  • –source: kod źródłowy do wdrożenia. W tym przypadku . odnosi się do kodu źródłowego w bieżącym folderze helloworld.
  • –set-env-vars: ustawia zmienne środowiskowe używane przez aplikację do kierowania jej do bazy danych Cloud SQL.
  • –service-account: łączy wdrożenie Cloud Run z kontem usługi z uprawnieniami do łączenia się z bazą danych Cloud SQL utworzoną na początku tego ćwiczenia.
  • –allow-unauthenticated: zezwala na nieuwierzytelnione żądania, dzięki czemu aplikacja jest dostępna w internecie.
gcloud run deploy helloworld \
  --region=us-central1 \
  --source=. \
  --set-env-vars INSTANCE_CONNECTION_NAME="${GOOGLE_CLOUD_PROJECT}:us-central1:quickstart-instance" \
  --set-env-vars DB_NAME="quickstart_db" \
  --set-env-vars DB_USER="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam" \
  --service-account="quickstart-service-account@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
  --allow-unauthenticated

Jeśli pojawi się prośba, naciśnij yEnter, aby potwierdzić, że chcesz kontynuować:

Do you want to continue (Y/n)? y

Po kilku minutach aplikacja powinna podać adres URL, który możesz otworzyć.

Otwórz adres URL, aby zobaczyć działanie aplikacji. Za każdym razem, gdy otworzysz adres URL lub odświeżysz stronę, zobaczysz 5 ostatnich wizyt zwróconych w formacie JSON.

7. Gratulacje

Masz wdrożoną w Cloud Run aplikację w Go, która może łączyć się z bazą danych PostgreSQL działającą w Cloud SQL.

Omówione zagadnienia:

  • Tworzenie bazy danych Cloud SQL for PostgreSQL
  • Wdrażanie aplikacji w Go w Cloud Run
  • Łączenie aplikacji z Cloud SQL za pomocą łącznika Go

Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby zużyte w tym samouczku, możesz usunąć projekt zawierający te zasoby lub zachować projekt i usunąć poszczególne zasoby. Jeśli chcesz usunąć cały projekt, możesz uruchomić to polecenie:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}