如何將 Cloud Run 中的 Go 應用程式連線至 PostgreSQL 適用的 Cloud SQL 資料庫

1. 總覽

Cloud SQL Go 連接器是將 Go 應用程式安全連線至 Cloud SQL 資料庫最簡單的方式。Cloud Run 是全代管無伺服器平台,能讓您執行可透過 HTTP 要求叫用的無狀態容器。本程式碼研究室將示範如何使用 IAM 驗證機制,透過服務帳戶將 Cloud Run 上的 Go 應用程式安全連線至 PostgreSQL 適用的 Cloud SQL 資料庫。

學習目標

在本實驗室中,您將瞭解如何執行下列操作:

  • 建立 PostgreSQL 適用的 Cloud SQL 資料庫
  • 將 Go 應用程式部署至 Cloud Run
  • 使用 Go 連接器將應用程式連結至 Cloud SQL

必要條件

  • 本實驗室假設您已熟悉 Cloud 控制台和 Cloud Shell 環境。

2. 事前準備

設定 Cloud 專案

  1. 登入 Google Cloud 控制台,然後建立新專案或重複使用現有專案。如果沒有 Google 帳戶,請先建立帳戶

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 專案名稱是這個專案參與者的顯示名稱。這是 Google API 未使用的字元字串。你隨時可以更新該位置資訊。
  • 專案 ID 在所有 Google Cloud 專案中都是不重複的,而且設定後即無法變更。Cloud 控制台會自動產生不重複的字串,通常您不需要在意這個字串。在大多數程式碼研究室中,您需要參照專案 ID (通常會標示為 PROJECT_ID)。如果您不喜歡產生的 ID,可以產生另一個隨機 ID。你也可以嘗試自訂名稱,看看是否可用。完成這個步驟後就無法變更,且專案期間都會維持這個設定。
  • 請注意,部分 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三種值,請參閱說明文件
  1. 接著,您需要在 Cloud 控制台中啟用帳單,才能使用 Cloud 資源/API。完成本程式碼研究室的費用應該不高,甚至完全免費。如要關閉資源,避免產生本教學課程以外的費用,您可以刪除自己建立的資源,或刪除整個專案。Google Cloud 新使用者可參加價值$300 美元的免費試用計畫。

環境設定

按一下搜尋列右側的圖示,啟用 Cloud Shell。

ecdc43ada29e91b.png

在 Cloud Shell 中啟用 API:

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

如果系統提示您授權,請點選「授權」繼續操作。

6356559df3eccdda.png

這個指令可能需要幾分鐘才能完成,但最終應該會產生類似以下的成功訊息:

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

3. 設定服務帳戶

建立及設定 Google Cloud 服務帳戶,供 Cloud Run 使用,確保該帳戶具備連線至 Cloud SQL 的正確權限。

  1. 執行下列 gcloud iam service-accounts create 指令,建立新的服務帳戶:
    gcloud iam service-accounts create quickstart-service-account \
      --display-name="Quickstart Service Account"
    
  2. 執行 gcloud projects add-iam-policy-binding 指令,將「Cloud SQL Client」(Cloud SQL 用戶端) 角色新增至您剛建立的 Google Cloud 服務帳戶。在 Cloud Shell 中,運算式 ${GOOGLE_CLOUD_PROJECT} 會替換為專案名稱。如果覺得手動更換比較方便,也可以這麼做。
    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. 執行下列 gcloud projects add-iam-policy-binding 指令,將「Cloud SQL Instance User」(Cloud SQL 執行個體使用者) 角色新增至您剛建立的 Google Cloud 服務帳戶。
    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. 執行下列 gcloud projects add-iam-policy-binding 指令,將「記錄檔寫入者」角色新增至您剛建立的 Google Cloud 服務帳戶。
    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. 設定 Cloud SQL

執行 gcloud sql instances create 指令,建立 Cloud SQL 執行個體。

  • --database-version:資料庫引擎類型和版本。如未指定,系統會使用 API 預設值。如要查看目前可用的版本,請參閱 gcloud 資料庫版本說明文件。
  • –cpu:機器中所需的核心數量。
  • –memory:整數值,表示機器所需的記憶體量。請提供大小單位 (例如 3072MB 或 9GB)。如未指定單位,系統會假設您使用的是 GB。
  • –region:執行個體所在的區域位置 (例如:us-central1、asia-east1、us-east1)。
  • --database-flags:可設定旗標。在本例中,我們開啟 cloudsql.iam_authentication,讓 Cloud Run 使用先前建立的服務帳戶連線至 Cloud SQL。
    gcloud sql instances create quickstart-instance \
      --database-version=POSTGRES_14 \
      --cpu=1 \
      --memory=4GB \
      --region=us-central1 \
      --database-flags=cloudsql.iam_authentication=on
    

這個指令可能需要幾分鐘才能完成。

執行 gcloud sql databases create 指令,在 quickstart-instance 中建立 Cloud SQL 資料庫。

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

為先前建立的服務帳戶建立 PostgreSQL 資料庫使用者,以便存取資料庫。

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

5. 準備應用程式

準備可回應 HTTP 要求的 Go 應用程式。

  1. 在 Cloud Shell 建立名為 helloworld 的新目錄,然後前往該目錄:
    mkdir helloworld
    cd helloworld
    
  2. 執行 go mod init 來初始化新的 Go 應用程式。
    go mod init github.com/GoogleCloudPlatform/golang-samples/run/helloworld
    
  3. 安裝 Cloud SQL Go 連接器依附元件。
    go get cloud.google.com/go/cloudsqlconn
    go get cloud.google.com/go/cloudsqlconn/postgres/pgxv4
    
  4. 建立包含應用程式程式碼的 main.go 檔案。這組代碼可執行下列操作:
    • 接受 HTTP 要求
    • 連線至資料庫
    • 將 HTTP 要求的時間儲存在資料庫中
    • 傳回最近五次要求的時間
    在 Cloud Shell 中執行下列指令:
    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
    

這段程式碼會建立基本的網路伺服器,用於監聽 PORT 環境變數定義的通訊埠。應用程式現在已可部署。

6. 部署 Cloud Run 應用程式

執行下列指令來部署應用程式:

  • –region:執行個體所在的區域位置 (例如:us-central1、asia-east1、us-east1)。
  • –source:要部署的原始碼。在本例中,. 是指目前資料夾 helloworld 中的原始碼。
  • --set-env-vars:設定應用程式使用的環境變數,將應用程式導向 Cloud SQL 資料庫。
  • --service-account:將 Cloud Run 部署作業繫結至服務帳戶,該帳戶具有連線至本 Codelab 開頭建立的 Cloud SQL 資料庫的權限。
  • --allow-unauthenticated:允許未經驗證的請求,讓應用程式可從網際網路存取。
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

如果系統提示您確認是否要繼續,請按下 yEnter

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

幾分鐘後,應用程式應會提供網址供您造訪。

前往該網址,即可查看應用程式的運作情形。每次造訪網址或重新整理頁面時,系統都會以 JSON 格式傳回最近五次造訪記錄。

7. 恭喜

您已在 Cloud Run 上部署 Go 應用程式,並連線至 Cloud SQL 上執行的 PostgreSQL 資料庫。

涵蓋內容:

  • 建立 PostgreSQL 適用的 Cloud SQL 資料庫
  • 將 Go 應用程式部署至 Cloud Run
  • 使用 Go 連接器將應用程式連線至 Cloud SQL

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程所用資源的費用,請刪除含有相關資源的專案,或者保留專案但刪除個別資源。如要刪除整個專案,請執行:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}