從 Cloud Run 連線

本頁面包含從在 Cloud Run 中執行的服務連線至 Cloud SQL 執行個體的資訊與範例。

如需執行連線至 Cloud SQL 的 Cloud Run 範例網頁應用程式的逐步操作說明,請參閱從 Cloud Run 連線的快速入門

Cloud SQL 是全代管資料庫服務,可協助您在雲端中設定、維護及管理關聯式資料庫。

Cloud Run 是代管運算平台,能讓您在 Google Cloud 基礎架構上直接執行容器。

設定 Cloud SQL 執行個體

  1. 在您要連線的 Google Cloud 專案中啟用 Cloud SQL Admin API (如果尚未啟用):

    Enable the API

  2. 建立 SQL Server 適用的 Cloud SQL 執行個體。建議您選擇與 Cloud Run 服務位於相同區域的 Cloud SQL 執行個體位置,以縮短延遲時間、避免部分網路費用,並降低跨區域故障風險。

    根據預設,Cloud SQL 會為新執行個體指派公開 IP 位址。您也可以選擇指派私人 IP 位址。如要進一步瞭解這兩種連線選項,請參閱「 連線總覽」頁面。

設定 Cloud Run

設定 Cloud Run 的步驟取決於您指派給 Cloud SQL 執行個體的 IP 位址類型。如果您將所有輸出流量都透過直接虛擬私有雲輸出或無伺服器虛擬私有雲存取連接器轉送,請使用私人 IP 位址。比較兩種網路出口方法

公開 IP (預設)

Cloud Run 支援使用 Go、Java 和 Python 連接器,透過公開 IP 連線至 SQL Server 適用的 Cloud SQL。

  • 請確認執行個體有公開 IP 位址。您可以在 Google Cloud 主控台的「總覽」頁面中,查看執行個體的相關資訊。如需新增,請參閱「設定公開 IP 頁面」瞭解操作方式。
  • 取得執行個體的 INSTANCE_CONNECTION_NAME。您可以在 Google Cloud 主控台的「總覽」頁面中找到此值,也可以執行下列 gcloud sql instances describe 指令來取得:
    gcloud sql instances describe INSTANCE_NAME
       
    INSTANCE_NAME 改成 Cloud SQL 執行個體的名稱。
  • 取得 Cloud Run 服務的 CLOUD_RUN_SERVICE_ACCOUNT_NAME。您可以在 Google Cloud 控制台中,在代管 Cloud Run 服務的專案的 IAM 頁面中找到這個值,也可以在代管 Cloud Run 服務的專案中執行下列 gcloud run services describe 指令來取得這個值:
    gcloud run services describe CLOUD_RUN_SERVICE_NAME
    --region CLOUD_RUN_SERVICE_REGION --format="value(spec.template.spec.serviceAccountName)"
       
    替換下列變數:
    • CLOUD_RUN_SERVICE_NAME:Cloud Run 服務名稱
    • CLOUD_RUN_SERVICE_REGION:Cloud Run 服務的區域
  • 為 Cloud Run 服務設定服務帳戶。如要連線至 Cloud SQL,請確認服務帳戶具有 Cloud SQL Client IAM 角色
  • 如果您要將 Cloud SQL 連線新增至新服務,就必須將服務封裝並上傳至 Container Registry 或 Artifact Registry。如果您尚未建立連線,請參閱有關建構及部署容器映像檔的操作說明。

就像變更任何設定一樣,為 Cloud SQL 連線設定新設定會建立新的 Cloud Run 修訂版本。除非您明確做出更新,變更這項設定,否則後續的修訂版本也會自動取得這個 Cloud SQL 連線。

控制台

  1. 前往 Cloud Run

  2. 開始設定服務。如要將 Cloud SQL 連線新增至現有服務,請按照下列步驟操作:

    1. 在「服務」清單中,按一下所需服務的名稱。
    2. 按一下「Edit & deploy new revision」
  3. 啟用連線至 Cloud SQL 執行個體的功能:
    1. 依序點選「容器」和「設定」
    2. 捲動至「Cloud SQL 連線」
    3. 按一下「新增連線」
    4. 如果您尚未啟用 Cloud SQL Admin API,請按一下「Enable the Cloud SQL Admin」按鈕。

    新增 Cloud SQL 連線

    • 如果您要新增專案中 Cloud SQL 執行個體的連線,請從選單中選取所需的 Cloud SQL 執行個體。
    • 如果您使用的是其他專案的 Cloud SQL 執行個體,請在選單中選取「自訂連線字串」,然後輸入完整的執行個體連線名稱,格式為 PROJECT-ID:REGION:INSTANCE-ID
    • 如要刪除連線,請將游標停留在連線右側,等到 「刪除」圖示出現後,按一下該圖示。
  4. 按一下 [Create] (建立) 或 [Deploy] (部署)

指令列

使用下列任何指令之前,請先替換以下項目:

  • IMAGE 與您要部署的映像檔
  • SERVICE_NAME 改為 Cloud Run 服務名稱
  • INSTANCE_CONNECTION_NAME 改成 Cloud SQL 執行個體的執行個體連線名稱,或連線名稱清單 (以逗號分隔)。

    如果您要部署新的容器,請使用下列指令:

    gcloud run deploy \
      --image=IMAGE \
      --add-cloudsql-instances=INSTANCE_CONNECTION_NAME
    如果您要更新現有服務,請使用下列指令:
    gcloud run services update SERVICE_NAME \
      --add-cloudsql-instances=INSTANCE_CONNECTION_NAME

Terraform

以下程式碼會建立基礎 Cloud Run 容器,並連結 Cloud SQL 執行個體。

resource "google_cloud_run_v2_service" "default" {
  name     = "cloudrun-service"
  location = "us-central1"

  deletion_protection = false # set to "true" in production

  template {
    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello:latest" # Image to deploy

      volume_mounts {
        name       = "cloudsql"
        mount_path = "/cloudsql"
      }
    }
    volumes {
      name = "cloudsql"
      cloud_sql_instance {
        instances = [google_sql_database_instance.default.connection_name]
      }
    }
  }
  client     = "terraform"
  depends_on = [google_project_service.secretmanager_api, google_project_service.cloudrun_api, google_project_service.sqladmin_api]
}

  1. 請輸入 terraform apply 套用變更。
  2. 如要確認變更,請檢查 Cloud Run 服務,然後依序點選「Revisions」分頁標籤和「Connections」分頁標籤。

私人 IP

如果授權服務帳戶屬於與 Cloud SQL 執行個體所在專案不同的專案,請執行下列操作:

  • 在兩個專案中啟用 Cloud SQL Admin API。
  • 針對含有 Cloud SQL 執行個體的專案中服務帳戶,新增 IAM 權限
直接 VPC 出口和連接器會使用私人 IP 位址,處理與虛擬私有雲網路的通訊。如要使用其中一種出口方法直接連線至私人 IP 位址,請按照下列步驟操作:
  1. 請確認先前建立的 Cloud SQL 執行個體具有私人 IP 位址。如要新增內部 IP 位址,請參閱「設定私人 IP」。
  2. 設定出口方法,連線至與 Cloud SQL 執行個體相同的虛擬私人雲端網路。請注意下列條件:
    • 直接虛擬私有雲輸出和無伺服器虛擬私有雲存取都支援與透過 Cloud VPN虛擬私人雲端網路對等互連連線的虛擬私人雲端網路通訊。
    • 直接虛擬私有雲 egress 和無伺服器虛擬私人雲端存取不支援舊版網路
    • 除非您使用 共用虛擬私有雲,否則連接器所在的專案和地區,必須與使用該連接器的資源相同,但連接器可將流量傳送至位於不同地區的資源。
  3. 使用執行個體的私人 IP 位址和通訊埠 1433 連線。

連線至 Cloud SQL

設定 Cloud Run 後,您就能連線至 Cloud SQL 執行個體。

公開 IP (預設)

針對公開 IP 路徑,Cloud Run 會提供加密功能,並使用 Cloud SQL 連接器建立連線。

使用 Cloud SQL 連接器建立連線

Cloud SQL 連接器是特定語言的程式庫,可在連線至 Cloud SQL 執行個體時提供加密和 IAM 型授權。

Python

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

import os

from google.cloud.sql.connector import Connector, IPTypes
import pytds

import sqlalchemy


def connect_with_connector() -> sqlalchemy.engine.base.Engine:
    """
    Initializes a connection pool for a Cloud SQL instance of SQL Server.

    Uses the Cloud SQL Python Connector package.
    """
    # Note: Saving credentials in environment variables is convenient, but not
    # secure - consider a more secure solution such as
    # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
    # keep secrets safe.

    instance_connection_name = os.environ[
        "INSTANCE_CONNECTION_NAME"
    ]  # e.g. 'project:region:instance'
    db_user = os.environ.get("DB_USER", "")  # e.g. 'my-db-user'
    db_pass = os.environ["DB_PASS"]  # e.g. 'my-db-password'
    db_name = os.environ["DB_NAME"]  # e.g. 'my-database'

    ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

    # initialize Cloud SQL Python Connector object
    connector = Connector(ip_type=ip_type, refresh_strategy="LAZY")

    connect_args = {}
    # If your SQL Server instance requires SSL, you need to download the CA
    # certificate for your instance and include cafile={path to downloaded
    # certificate} and validate_host=False. This is a workaround for a known issue.
    if os.environ.get("DB_ROOT_CERT"):  # e.g. '/path/to/my/server-ca.pem'
        connect_args = {
            "cafile": os.environ["DB_ROOT_CERT"],
            "validate_host": False,
        }

    def getconn() -> pytds.Connection:
        conn = connector.connect(
            instance_connection_name,
            "pytds",
            user=db_user,
            password=db_pass,
            db=db_name,
            **connect_args
        )
        return conn

    pool = sqlalchemy.create_engine(
        "mssql+pytds://",
        creator=getconn,
        # ...
    )
    return pool

Java

如要在網頁應用程式的內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

注意:

  • CLOUD_SQL_CONNECTION_NAME 應以 <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME> 的格式表示
  • 請參閱 這裡 的 JDBC 通訊端處理站版本需求,瞭解 pom.xml 檔案的相關規定。


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

public class ConnectorConnectionPoolFactory extends ConnectionPoolFactory {

  // Note: Saving credentials in environment variables is convenient, but not
  // secure - consider a more secure solution such as
  // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  // keep secrets safe.
  private static final String INSTANCE_CONNECTION_NAME =
      System.getenv("INSTANCE_CONNECTION_NAME");
  private static final String DB_USER = System.getenv("DB_USER");
  private static final String DB_PASS = System.getenv("DB_PASS");
  private static final String DB_NAME = System.getenv("DB_NAME");

  public static DataSource createConnectionPool() {
    // The configuration object specifies behaviors for the connection pool.
    HikariConfig config = new HikariConfig();

    // The following is equivalent to setting the config options below:
    // jdbc:sqlserver://;user=<DB_USER>;password=<DB_PASS>;databaseName=<DB_NAME>;
    // socketFactoryClass=com.google.cloud.sql.sqlserver.SocketFactory;
    // socketFactoryConstructorArg=<INSTANCE_CONNECTION_NAME>

    // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory
    // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url

    // Configure which instance and what database user to connect with.
    config
        .setDataSourceClassName("com.microsoft.sqlserver.jdbc.SQLServerDataSource");
    config.setUsername(DB_USER); // e.g. "root", "sqlserver"
    config.setPassword(DB_PASS); // e.g. "my-password"
    config.addDataSourceProperty("databaseName", DB_NAME);

    config.addDataSourceProperty("socketFactoryClass",
        "com.google.cloud.sql.sqlserver.SocketFactory");
    config.addDataSourceProperty("socketFactoryConstructorArg", INSTANCE_CONNECTION_NAME);

    // The Java Connector provides SSL encryption, so it should be disabled
    // at the driver level.
    config.addDataSourceProperty("encrypt", "false");

    // cloudSqlRefreshStrategy set to "lazy" is used to perform a
    // refresh when needed, rather than on a scheduled interval.
    // This is recommended for serverless environments to
    // avoid background refreshes from throttling CPU.
    config.addDataSourceProperty("cloudSqlRefreshStrategy", "lazy");

    // ... Specify additional connection properties here.
    // ...

    // Initialize the connection pool using the configuration object.
    return new HikariDataSource(config);
  }
}

Go

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

package cloudsql

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net"
	"os"

	"cloud.google.com/go/cloudsqlconn"
	mssql "github.com/denisenkom/go-mssqldb"
)

type csqlDialer struct {
	dialer     *cloudsqlconn.Dialer
	connName   string
	usePrivate bool
}

// DialContext adheres to the mssql.Dialer interface.
func (c *csqlDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
	var opts []cloudsqlconn.DialOption
	if c.usePrivate {
		opts = append(opts, cloudsqlconn.WithPrivateIP())
	}
	return c.dialer.Dial(ctx, c.connName, opts...)
}

func connectWithConnector() (*sql.DB, error) {
	mustGetenv := func(k string) string {
		v := os.Getenv(k)
		if v == "" {
			log.Fatalf("Fatal Error in connect_connector.go: %s environment variable not set.\n", k)
		}
		return v
	}
	// Note: Saving credentials in environment variables is convenient, but not
	// secure - consider a more secure solution such as
	// Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
	// keep secrets safe.
	var (
		dbUser                 = mustGetenv("DB_USER")                  // e.g. 'my-db-user'
		dbPwd                  = mustGetenv("DB_PASS")                  // e.g. 'my-db-password'
		dbName                 = mustGetenv("DB_NAME")                  // e.g. 'my-database'
		instanceConnectionName = mustGetenv("INSTANCE_CONNECTION_NAME") // e.g. 'project:region:instance'
		usePrivate             = os.Getenv("PRIVATE_IP")
	)

	dbURI := fmt.Sprintf("user id=%s;password=%s;database=%s;", dbUser, dbPwd, dbName)
	c, err := mssql.NewConnector(dbURI)
	if err != nil {
		return nil, fmt.Errorf("mssql.NewConnector: %w", err)
	}
	// WithLazyRefresh() Option is used to perform refresh
	// when needed, rather than on a scheduled interval.
	// This is recommended for serverless environments to
	// avoid background refreshes from throttling CPU.
	dialer, err := cloudsqlconn.NewDialer(context.Background(), cloudsqlconn.WithLazyRefresh())
	if err != nil {
		return nil, fmt.Errorf("cloudsqlconn.NewDailer: %w", err)
	}
	c.Dialer = &csqlDialer{
		dialer:     dialer,
		connName:   instanceConnectionName,
		usePrivate: usePrivate != "",
	}

	dbPool := sql.OpenDB(c)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}
	return dbPool, nil
}

使用 Secret Manager

Google 建議您使用 Secret Manager 儲存 SQL 憑證等機密資訊。您可以使用 Cloud Run 將密碼傳遞為環境變數或掛接為磁碟區

在 Secret Manager 中建立密鑰後,請使用下列指令更新現有服務:

指令列

gcloud run services update SERVICE_NAME \
  --add-cloudsql-instances=INSTANCE_CONNECTION_NAME
  --update-env-vars=INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME_SECRET \
  --update-secrets=DB_USER=DB_USER_SECRET:latest \
  --update-secrets=DB_PASS=DB_PASS_SECRET:latest \
  --update-secrets=DB_NAME=DB_NAME_SECRET:latest

Terraform

以下會使用 google_secret_manager_secretgoogle_secret_manager_secret_version 建立密鑰資源,以便安全地儲存資料庫使用者、密碼和名稱值。請注意,您必須更新專案的 Compute Engine 服務帳戶,才能存取每個機密金鑰。


# Create dbuser secret
resource "google_secret_manager_secret" "dbuser" {
  secret_id = "dbusersecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbuser secret
resource "google_secret_manager_secret_version" "dbuser_data" {
  secret      = google_secret_manager_secret.dbuser.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbuser secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbuser" {
  secret_id = google_secret_manager_secret.dbuser.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}[email protected]" # Project's compute service account
}


# Create dbpass secret
resource "google_secret_manager_secret" "dbpass" {
  secret_id = "dbpasssecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbpass secret
resource "google_secret_manager_secret_version" "dbpass_data" {
  secret      = google_secret_manager_secret.dbpass.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbpass secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbpass" {
  secret_id = google_secret_manager_secret.dbpass.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}[email protected]" # Project's compute service account
}


# Create dbname secret
resource "google_secret_manager_secret" "dbname" {
  secret_id = "dbnamesecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbname secret
resource "google_secret_manager_secret_version" "dbname_data" {
  secret      = google_secret_manager_secret.dbname.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbname secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbname" {
  secret_id = google_secret_manager_secret.dbname.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}[email protected]" # Project's compute service account
}

更新主要 Cloud Run 資源,加入新的密鑰。

resource "google_cloud_run_v2_service" "default" {
  name     = "cloudrun-service"
  location = "us-central1"

  deletion_protection = false # set to "true" in production

  template {
    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello:latest" # Image to deploy

      # Sets a environment variable for instance connection name
      env {
        name  = "INSTANCE_CONNECTION_NAME"
        value = google_sql_database_instance.default.connection_name
      }
      # Sets a secret environment variable for database user secret
      env {
        name = "DB_USER"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbuser.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }
      # Sets a secret environment variable for database password secret
      env {
        name = "DB_PASS"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbpass.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }
      # Sets a secret environment variable for database name secret
      env {
        name = "DB_NAME"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbname.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }

      volume_mounts {
        name       = "cloudsql"
        mount_path = "/cloudsql"
      }
    }
    volumes {
      name = "cloudsql"
      cloud_sql_instance {
        instances = [google_sql_database_instance.default.connection_name]
      }
    }
  }
  client     = "terraform"
  depends_on = [google_project_service.secretmanager_api, google_project_service.cloudrun_api, google_project_service.sqladmin_api]
}

輸入 terraform apply 即可套用變更。

範例指令使用密鑰版本 latest,但 Google 建議將密鑰釘選至特定版本 SECRET_NAME:v1

私人 IP

對於私人 IP 路徑,應用程式會透過虛擬私有雲網路直接連線至執行個體。這個方法會使用 TCP 直接連線至 Cloud SQL 執行個體,不必使用 Cloud SQL 驗證 Proxy。

連線至 TCP

使用 Cloud SQL 執行個體的私人 IP 位址做為主機和通訊埠 1433 進行連線。

Python

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

import os

import sqlalchemy


def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
    """Initializes a TCP connection pool for a Cloud SQL instance of SQL Server."""
    # Note: Saving credentials in environment variables is convenient, but not
    # secure - consider a more secure solution such as
    # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
    # keep secrets safe.
    db_host = os.environ[
        "INSTANCE_HOST"
    ]  # e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
    db_user = os.environ["DB_USER"]  # e.g. 'my-db-user'
    db_pass = os.environ["DB_PASS"]  # e.g. 'my-db-password'
    db_name = os.environ["DB_NAME"]  # e.g. 'my-database'
    db_port = os.environ["DB_PORT"]  # e.g. 1433

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # mssql+pytds://<db_user>:<db_pass>@<db_host>:<db_port>/<db_name>
        sqlalchemy.engine.url.URL.create(
            drivername="mssql+pytds",
            username=db_user,
            password=db_pass,
            database=db_name,
            host=db_host,
            port=db_port,
        ),
        # ...
    )

    return pool

Java

如要在網頁應用程式的內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

注意:

  • CLOUD_SQL_CONNECTION_NAME 應以 <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME> 的格式表示
  • 使用引數 ipTypes=PRIVATE 會強制 SocketFactory 連線至執行個體的相關聯私人 IP
  • 請參閱 這裡 的 JDBC 通訊端處理站版本需求,瞭解 pom.xml 檔案的相關規定。


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

public class TcpConnectionPoolFactory extends ConnectionPoolFactory {

  // Note: Saving credentials in environment variables is convenient, but not
  // secure - consider a more secure solution such as
  // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  // keep secrets safe.
  private static final String DB_USER = System.getenv("DB_USER");
  private static final String DB_PASS = System.getenv("DB_PASS");
  private static final String DB_NAME = System.getenv("DB_NAME");

  private static final String INSTANCE_HOST = System.getenv("INSTANCE_HOST");
  private static final String DB_PORT = System.getenv("DB_PORT");


  public static DataSource createConnectionPool() {
    // The configuration object specifies behaviors for the connection pool.
    HikariConfig config = new HikariConfig();

    // Configure which instance and what database user to connect with.
    config.setJdbcUrl(
        String.format("jdbc:sqlserver://%s:%s;databaseName=%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "sqlserver"
    config.setPassword(DB_PASS); // e.g. "my-password"


    // ... Specify additional connection properties here.
    // ...

    // Initialize the connection pool using the configuration object.
    return new HikariDataSource(config);
  }
}

Node.js

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

const mssql = require('mssql');

// createTcpPool initializes a TCP connection pool for a Cloud SQL
// instance of SQL Server.
const createTcpPool = async config => {
  // Note: Saving credentials in environment variables is convenient, but not
  // secure - consider a more secure solution such as
  // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  // keep secrets safe.
  const dbConfig = {
    server: process.env.INSTANCE_HOST, // e.g. '127.0.0.1'
    port: parseInt(process.env.DB_PORT), // e.g. 1433
    user: process.env.DB_USER, // e.g. 'my-db-user'
    password: process.env.DB_PASS, // e.g. 'my-db-password'
    database: process.env.DB_NAME, // e.g. 'my-database'
    options: {
      trustServerCertificate: true,
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return mssql.connect(dbConfig);
};

Go

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

package cloudsql

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"

	_ "github.com/denisenkom/go-mssqldb"
)

// connectTCPSocket initializes a TCP connection pool for a Cloud SQL
// instance of SQL Server.
func connectTCPSocket() (*sql.DB, error) {
	mustGetenv := func(k string) string {
		v := os.Getenv(k)
		if v == "" {
			log.Fatalf("Fatal Error in connect_tcp.go: %s environment variable not set.\n", k)
		}
		return v
	}
	// Note: Saving credentials in environment variables is convenient, but not
	// secure - consider a more secure solution such as
	// Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
	// keep secrets safe.
	var (
		dbUser    = mustGetenv("DB_USER")       // e.g. 'my-db-user'
		dbPwd     = mustGetenv("DB_PASS")       // e.g. 'my-db-password'
		dbTCPHost = mustGetenv("INSTANCE_HOST") // e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
		dbPort    = mustGetenv("DB_PORT")       // e.g. '1433'
		dbName    = mustGetenv("DB_NAME")       // e.g. 'my-database'
	)

	dbURI := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%s;database=%s;",
		dbTCPHost, dbUser, dbPwd, dbPort, dbName)


	// dbPool is the pool of database connections.
	dbPool, err := sql.Open("sqlserver", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}

	// ...

	return dbPool, nil
}

Ruby

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

tcp: &tcp
  adapter: sqlserver
  # Configure additional properties here.
  # Note: Saving credentials in environment variables is convenient, but not
  # secure - consider a more secure solution such as
  # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  # keep secrets safe.
  username: <%= ENV["DB_USER"] %>  # e.g. "my-database-user"
  password: <%= ENV["DB_PASS"] %> # e.g. "my-database-password"
  database: <%= ENV.fetch("DB_NAME") { "vote_development" } %>
  host: <%= ENV.fetch("INSTANCE_HOST") { "127.0.0.1" }%> # '172.17.0.1' if deployed to GAE Flex
  port: <%= ENV.fetch("DB_PORT") { 1433 }%> 

PHP

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

namespace Google\Cloud\Samples\CloudSQL\SQLServer;

use PDO;
use PDOException;
use RuntimeException;
use TypeError;

class DatabaseTcp
{
    public static function initTcpDatabaseConnection(): PDO
    {
        try {
            // Note: Saving credentials in environment variables is convenient, but not
            // secure - consider a more secure solution such as
            // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
            // keep secrets safe.
            $username = getenv('DB_USER'); // e.g. 'your_db_user'
            $password = getenv('DB_PASS'); // e.g. 'your_db_password'
            $dbName = getenv('DB_NAME'); // e.g. 'your_db_name'
            $instanceHost = getenv('INSTANCE_HOST'); // e.g. '127.0.0.1' ('172.17.0.1' for GAE Flex)

            // Connect using TCP
            $dsn = sprintf(
                'sqlsrv:server=%s;Database=%s',
                $instanceHost,
                $dbName
            );

            // Connect to the database
            $conn = new PDO(
                $dsn,
                $username,
                $password,
                # ...
            );
        } catch (TypeError $e) {
            throw new RuntimeException(
                sprintf(
                    'Invalid or missing configuration! Make sure you have set ' .
                        '$username, $password, $dbName, and $instanceHost (for TCP mode). ' .
                        'The PHP error was %s',
                    $e->getMessage()
                ),
                $e->getCode(),
                $e
            );
        } catch (PDOException $e) {
            throw new RuntimeException(
                sprintf(
                    'Could not connect to the Cloud SQL Database. Check that ' .
                        'your username and password are correct, that the Cloud SQL ' .
                        'proxy is running, and that the database exists and is ready ' .
                        'for use. For more assistance, refer to %s. The PDO error was %s',
                    'https://cloud.google.com/sql/docs/sqlserver/connect-external-app',
                    $e->getMessage()
                ),
                (int) $e->getCode(),
                $e
            );
        }

        return $conn;
    }
}

最佳做法和其他資訊

您可以在本機測試應用程式時使用 Cloud SQL 驗證 Proxy。如需詳細操作說明,請參閱快速入門導覽課程:使用 Cloud SQL 驗證 Proxy

您也可以使用 透過 Docker 容器的 Cloud SQL Proxy 進行測試。

連線集區

資料庫伺服器本身或平台基礎架構,都可能會中斷與基礎資料庫的連線。建議您使用支援連線集區的用戶端程式庫,自動重新連線中斷的用戶端連線。如需使用連線集區的詳細範例,請參閱「 管理資料庫連線」頁面。

連線限制

Cloud SQL 的 MySQL 和 PostgreSQL 版本都會對並行連線設下上限,而這些上限可能會因所選資料庫引擎而異 (請參閱「Cloud SQL 配額和限制」頁面)。

Cloud Run 容器執行個體會將 Cloud SQL 資料庫連線數限制在 100 個以內。每個 Cloud Run 服務或工作的執行個體與資料庫之間的連線數不得超過 100 個。如果擴充服務或工作,每項部署作業的總連線數可隨之增加。

您可以使用連線集區,限制每個執行個體使用的連線數量上限。如需限制連線數量的詳細範例,請參閱「 管理資料庫連線」頁面。

API 配額限制

Cloud Run 提供使用 Cloud SQL 驗證 Proxy 的連線機制,該 Proxy 會使用 Cloud SQL 管理員 API。 API 配額限制適用於 Cloud SQL 驗證 Proxy。使用的 Cloud SQL Admin API 配額約為 Cloud SQL 執行個體數量的兩倍,而這會根據每次部署特定服務的 Cloud Run 執行個體數量來設定。您可以設定上限或增加 Cloud Run 執行個體的數量,藉此修改預期的 API 用量配額。

後續步驟