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 データベースを作成する
  • Go アプリケーションを Cloud Run にデプロイする
  • Go Connector を使用してアプリケーションを Cloud SQL に接続する

前提条件

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

2. 始める前に

Cloud プロジェクトの設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Google アカウントをまだお持ちでない場合は、アカウントを作成してください。

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この値はいつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常、それが何であるかは関係ありません。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常は PROJECT_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 Client ロールを追加します。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 Connector の依存関係をインストールします。
    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: 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

数分後、アプリケーションからアクセスするための 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}