使用 Cloud SQL 語言連接器建立連線

Cloud SQL 連接器是程式庫,可提供加密功能,以及連線至 Cloud SQL 執行個體時以 Identity and Access Management (IAM) 為基礎的授權。如果沒有網路路徑,他們就無法提供 Cloud SQL 執行個體的網路路徑。

如要以其他方式連線至 Cloud SQL 執行個體,可以使用資料庫用戶端Cloud SQL 驗證 Proxy。如要進一步瞭解如何連線至 Cloud SQL 執行個體,請參閱「關於連線選項」頁面。

本頁將討論下列 Cloud SQL 連接器:

  • Cloud SQL Java 連接器
  • Cloud SQL Python 連接器 (在 Colab 中開啟)
  • Cloud SQL Go 連接器
  • Cloud SQL Node.js 連接器

優點

使用 Cloud SQL 連接器具有下列優點:

  • IAM 授權:使用 IAM 權限控管可連線至 Cloud SQL 執行個體的使用者或項目。
  • 便利性:不必管理 SSL 憑證、設定防火牆規則或啟用授權網路。
  • IAM 資料庫驗證:支援 Cloud SQL 的自動 IAM 資料庫驗證功能。

事前準備

  • 啟用 Cloud SQL Admin API。

    Enable the API

  • 建立 Cloud SQL 執行個體,包括設定預設使用者。

    如要進一步瞭解如何建立執行個體,請參閱建立執行個體

    如要進一步瞭解如何設定預設使用者,請參閱設定預設使用者帳戶的密碼

  • 設定連線至 Cloud SQL 執行個體所需的角色和權限

設定

Java

Cloud SQL Java 連接器是連線至 Cloud SQL 執行個體時,提供以 IAM 為基礎的授權和加密功能的程式庫。如果沒有網路路徑,則無法提供連往 Cloud SQL 執行個體的路徑。

安裝

如要瞭解如何使用 Cloud SQL Java 連接器,建構及使用 JDBC 和 R2DBC 的驅動程式,請參閱下列連結:

如需在應用程式中使用這個程式庫的範例,請參閱這些範例應用程式

驗證

這個程式庫會使用應用程式預設憑證,驗證與 Cloud SQL 伺服器的連線。

如要在本機啟用憑證,請使用下列 gcloud 指令:

    gcloud auth application-default login
    

連結至 IntelliJ

如要將 IntelliJ 連線至 Cloud SQL 執行個體,您必須在驅動程式設定頁面的「Additional Files」部分,以 JAR 格式新增程式庫和依附元件。舉例來說,您可以在這個頁面找到預先建構的 Fat JAR。

Python

Cloud SQL Python 連接器是可與資料庫驅動程式搭配使用的程式庫,讓具備足夠權限的使用者連線至 Cloud SQL 資料庫,不必手動將 IP 加入許可清單或管理 SSL 憑證。

如需使用 Cloud SQL Python 連接器的互動式範例,請開啟 Cloud SQL Python 連接器筆記本

PostgreSQL 支援的驅動程式為 pg8000asyncpg

安裝

如要安裝最新版本的 Cloud SQL Python 連接器,請使用 pip install 指令,並為資料庫指定 pg8000asyncpg 驅動程式:

    pip install "cloud-sql-python-connector[pg8000]"
    

    pip install "cloud-sql-python-connector[asyncpg]"
    

驗證

這個程式庫會使用應用程式預設憑證,驗證與 Cloud SQL 伺服器的連線。

如要在本機啟用憑證,請使用下列 gcloud 指令:

    gcloud auth application-default login
    

Go

Cloud SQL Go 連接器是專為 Go 語言設計的 Cloud SQL 連接器。為提升安全性,這個連接器會在用戶端連接器和伺服器端 Proxy 之間,使用手動驗證的強大 TLS 1.3 加密,與資料庫通訊協定無關。

安裝

您可以使用 go get 安裝這個存放區:

    go get cloud.google.com/go/cloudsqlconn
    

Node.js

Node.js 連接器是專為 Node.js 執行階段設計的程式庫,可讓您安全連線至 Cloud SQL 執行個體。

安裝

您可以使用 npm install 安裝程式庫:

    npm install @google-cloud/cloud-sql-connector
    

使用

Java

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README

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 INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET");
  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 URL is equivalent to setting the config options below:
    // jdbc:postgresql:///<DB_NAME>?cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&
    // socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=<DB_USER>&password=<DB_PASS>
    // 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.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", _postgres"
    config.setPassword(DB_PASS); // e.g. "my-password"

    config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
    config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME);


    // The ipTypes argument can be used to specify a comma delimited list of preferred IP types
    // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the
    // SocketFactory to connect with an instance's associated private IP.
    config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE");

    // 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);
  }
}

Python

如需使用程式庫的詳細操作說明,請參閱「 如何使用這個連接器」。在 GitHub 上查看連線測試程式碼範例。

import os

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

import sqlalchemy


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

    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["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(refresh_strategy="LAZY")

    def getconn() -> pg8000.dbapi.Connection:
        conn: pg8000.dbapi.Connection = connector.connect(
            instance_connection_name,
            "pg8000",
            user=db_user,
            password=db_pass,
            db=db_name,
            ip_type=ip_type,
        )
        return conn

    # The Cloud SQL Python Connector can be used with SQLAlchemy
    # using the 'creator' argument to 'create_engine'
    pool = sqlalchemy.create_engine(
        "postgresql+pg8000://",
        creator=getconn,
        # ...
    )
    return pool

Go

如需程式庫的詳細使用說明,請參閱「 使用方式」。在 GitHub 上查看連線測試程式碼範例。

package cloudsql

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

	"cloud.google.com/go/cloudsqlconn"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/stdlib"
)

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 passwords and other 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")
	)

	dsn := fmt.Sprintf("user=%s password=%s database=%s", dbUser, dbPwd, dbName)
	config, err := pgx.ParseConfig(dsn)
	if err != nil {
		return nil, err
	}
	var opts []cloudsqlconn.Option
	if usePrivate != "" {
		opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPrivateIP()))
	}
	// 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.
	opts = append(opts, cloudsqlconn.WithLazyRefresh())
	d, err := cloudsqlconn.NewDialer(context.Background(), opts...)
	if err != nil {
		return nil, err
	}
	// Use the Cloud SQL connector to handle connecting to the instance.
	// This approach does *NOT* require the Cloud SQL proxy.
	config.DialFunc = func(ctx context.Context, network, instance string) (net.Conn, error) {
		return d.Dial(ctx, instanceConnectionName)
	}
	dbURI := stdlib.RegisterConnConfig(config)
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}
	return dbPool, nil
}

Node.js

如需使用程式庫的詳細操作說明,請參閱「 使用方式」。

const Knex = require('knex');
const {Connector} = require('@google-cloud/cloud-sql-connector');

// In case the PRIVATE_IP environment variable is defined then we set
// the ipType=PRIVATE for the new connector instance, otherwise defaults
// to public ip type.
const getIpType = () =>
  process.env.PRIVATE_IP === '1' || process.env.PRIVATE_IP === 'true'
    ? 'PRIVATE'
    : 'PUBLIC';

// connectWithConnector initializes connection pool for a Cloud SQL instance
// of Postgres using the Cloud SQL Node.js Connector.
const connectWithConnector = 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 connector = new Connector();
  const clientOpts = await connector.getOptions({
    instanceConnectionName: process.env.INSTANCE_CONNECTION_NAME,
    ipType: getIpType(),
  });
  const dbConfig = {
    client: 'pg',
    connection: {
      ...clientOpts,
      user: process.env.DB_USER, // e.g. 'my-user'
      password: process.env.DB_PASS, // e.g. 'my-user-password'
      database: process.env.DB_NAME, // e.g. 'my-database'
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return Knex(dbConfig);
};

強制執行

透過連接器強制執行,您可以強制規定只能使用 Cloud SQL Auth Proxy 或 Cloud SQL 語言連接器連線至 Cloud SQL 執行個體。強制執行連接器後,Cloud SQL 會拒絕直接連線至資料庫。

如果您使用已啟用 Private Service Connect 的執行個體,則會受到限制。如果執行個體已啟用連接器強制執行功能,就無法為該執行個體建立唯讀備用資源。同樣地,如果執行個體有唯讀備用資源,您就無法為該執行個體啟用連接器強制執行功能。

gcloud

如要強制僅使用 Cloud SQL Auth Proxy 或 Cloud SQL 語言連接器連線至執行個體,請使用 gcloud sql instances patch 指令:

gcloud sql instances patch INSTANCE_NAME \
--connector-enforcement=REQUIRED

INSTANCE_NAME 改成 Cloud SQL 執行個體的名稱。

REST

使用任何要求資料之前,請先替換以下項目:

  • PROJECT_ID:包含執行個體的專案 ID 或專案編號 Google Cloud
  • INSTANCE_NAME:Cloud SQL 執行個體的名稱

HTTP 方法和網址:

PATCH https://sqladmin.googleapis.com/v1/projects/PROJECT_ID/instances/INSTANCE_NAME

JSON 要求主體:

{
  "kind": "sql#instance",
  "name": INSTANCE_NAME,
  "project": PROJECT_ID,
  "settings": {
  "connectorEnforcement": "REQUIRED",
  "kind": "sql#settings"
  }
}

如要傳送要求,請展開以下其中一個選項:

您應該會收到如下的 JSON 回應:

{
  "kind": "sql#operation",
  "targetLink": "https://sqladmin.googleapis.com/v1/projects/PROJECT_ID/instances/INSTANCE_NAME",
  "status": "PENDING",
  "user": "user@example.com",
  "insertTime": "2020-01-16T02:32:12.281Z",
  "operationType": "UPDATE",
  "name": "OPERATION_ID",
  "targetId": "INSTANCE_NAME",
  "selfLink": "https://sqladmin.googleapis.com/v1/projects/PROJECT_ID/operations/OPERATION_ID",
  "targetProject": "PROJECT_ID"
}

疑難排解

驅動程式版本

請務必使用最新版本的 Cloud SQL 連接器和資料庫驅動程式,以免發生不相容問題。系統不支援部分舊版驅動程式。

連線路徑

Cloud SQL 連接器會授權連線,但不會提供新的連線路徑。舉例來說,如要使用私人 IP 位址連線至 Cloud SQL 執行個體,您的應用程式必須已具備虛擬私有雲存取權。

針對連線問題進行偵錯

如需連線問題的其他說明,請參閱「疑難排解」和「偵錯連線問題」頁面。

後續步驟