Cloud Run 上の Go アプリケーションを Cloud SQL for PostgreSQL データベースに接続する方法

1. 概要

Cloud SQL Go コネクタは、Go アプリケーションを Cloud SQL データベースに安全に接続する最も簡単な方法です。Cloud Run は、HTTP リクエストを介して呼び出し可能なステートレス コンテナを実行できるフルマネージドのサーバーレス プラットフォームです。この Codelab では、IAM 認証を使用して、Cloud Run の Go アプリケーションを Cloud SQL for PostgreSQL データベースにサービス アカウントで安全に接続する方法について説明します。

学習内容

このラボでは、次の方法について学びます。

  • Cloud SQL for PostgreSQL データベースを作成する
  • Cloud Run に Go アプリケーションをデプロイする
  • Go コネクタを使用してアプリケーションを Cloud SQL に接続する

前提条件

  • このラボは、Cloud コンソール環境と Cloud Shell 環境に精通していることを前提としています。

2. 始める前に

Cloud プロジェクトのセットアップ

  1. Google Cloud コンソール にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Google アカウントをまだお持ちでない場合は、アカウントを作成する必要があります。

b35bf95b8bf3d5d8.pnga99b7ace416376c4.pngbd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この設定はいつでも変更できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常は、この内容を意識する必要はありません。ほとんどの Codelab では、プロジェクト ID(通常は PROJECT_ID と識別されます)を参照する必要があります。生成された ID が好みではない場合は、ランダムに別の ID を生成できます。または、ご自身で試して、利用可能かどうかを確認することもできます。このステップ以降は変更できず、プロジェクトを通して同じ ID になります。
  • なお、3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に請求が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクト全体を削除します。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. サービス アカウントを設定する

Cloud Run で使用する Google Cloud サービス アカウントを作成して構成し、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 コマンドを実行して、作成した Google Cloud サービス アカウントに Cloud SQL クライアント ロールを追加します。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 コマンドを実行して、作成した Google Cloud サービス アカウントに Cloud SQL インスタンス ユーザーのロールを追加します。
    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 database versions ドキュメントをご覧ください。
  • –cpu: マシンに必要なコア数。
  • –memory: マシンに必要なメモリ量を示す整数。サイズ単位を指定する必要があります(例: 3, 072 MB、9 GB)。単位を指定しない場合、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 リクエストの時刻を保存する
    • 最後の 5 件のリクエストの時刻を返す
    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: この Codelab の冒頭で作成した Cloud SQL データベースに接続する権限を持つサービス アカウントに Cloud Run デプロイを関連付けます。
  • --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

数分後、アプリケーションからアクセス可能な URL が提供されます。

URL に移動して、アプリケーションの動作を確認します。URL にアクセスするか、ページを更新するたびに、最近の 5 回のアクセスが JSON として返されます。

7. 完了

Cloud SQL で実行されている PostgreSQL データベースに接続できる Go アプリケーションを Cloud Run にデプロイしました。

学習した内容

  • Cloud SQL for PostgreSQL データベースを作成する
  • Cloud Run への Go アプリケーションのデプロイ
  • Go コネクタを使用してアプリケーションを Cloud SQL に接続する

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。プロジェクト全体を削除する場合は、次のコマンドを実行します。

gcloud projects delete ${GOOGLE_CLOUD_PROJECT}