使用 Cloud SQL 语言连接器进行连接

Cloud SQL 连接器是在连接到 Cloud SQL 实例时提供加密和基于 Identity and Access Management (IAM) 的授权的库。Cloud SQL 连接器无法提供 Cloud SQL 实例的网络路径(如果尚不存在)。

连接到 Cloud SQL 实例的其他方法包括使用数据库客户端Cloud SQL Auth 代理。如需详细了解如何连接到 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 证书、配置防火墙规则或启用已获授权的网络。

准备工作

  • 启用 Cloud SQL Admin API。

    Enable the API

  • 创建一个 Cloud SQL 实例,包括配置默认用户。

    如需详细了解如何创建实例,请参阅创建实例

    如需详细了解如何配置默认用户,请参阅为默认用户账号设置密码

  • 配置连接到 Cloud SQL 实例所需的角色和权限

设置

Java

Cloud SQL Java 连接器是在连接到 Cloud SQL 实例时提供基于 IAM 的授权和加密的库。它无法提供 Cloud SQL 实例的网络路径(如果尚不存在)。

安装

如需了解有关构建 JDBC 和 R2DBC 驱动程序并与 Cloud SQL Java 连接器搭配使用的说明,请参阅以下链接:

如需查看在应用的上下文中使用此库的示例,请参阅相关示例应用

身份验证

此库使用应用默认凭据对 Cloud SQL 服务器的连接进行身份验证。

如需在本地激活凭据,请使用以下 gcloud 命令:

    gcloud auth application-default login
    

连接 IntelliJ

如需将 IntelliJ 连接到您的 Cloud SQL 实例,您需要将库作为包含依赖项的 jar 添加到驱动程序设置页面上的其他文件部分中。例如,您可以在 Cloud SQL Java 连接器版本页面上找到预构建的 fat jar 来实现此目的。

Python

Cloud SQL Python 连接器是一个库,可与数据库驱动程序结合使用,以允许具有足够权限的用户连接到 Cloud SQL 数据库,而无需手动将 IP 列入许可名单或管理 SSL 证书。

如需查看使用 Cloud SQL Python 连接器的交互式示例,请打开 Cloud SQL Python 连接器笔记本

SQL Server 支持的驱动程序是 pytds

安装

如需安装最新版本的 Cloud SQL Python 连接器,请使用 pip install 命令并为您的数据库指定 pytds 驱动程序:

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

身份验证

此库使用应用默认凭据对 Cloud SQL 服务器的连接进行身份验证。

如需在本地激活凭据,请使用以下 gcloud 命令:

    gcloud auth application-default login
    

Go

Cloud SQL Go 连接器是专为与 Go 语言搭配使用而设计的 Cloud SQL 连接器。为了提高安全性,该连接器在客户端连接器与服务器端代理之间使用经过手动身份验证的可靠 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

如需了解 Web 应用环境下的此代码段,请查看 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 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");

    // ... 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 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

    connector = Connector(ip_type)

    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

Go

如需详细了解如何使用该库,请参阅用法。查看 GitHub 上的示例连接测试代码。

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)
	}
	dialer, err := cloudsqlconn.NewDialer(context.Background())
	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
}

Node.js

如需详细了解如何使用该库,请参阅用法

const {Connection} = require('tedious');
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 a TCP connection
// to a Cloud SQL instance of SQL Server.
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.getTediousOptions({
    instanceConnectionName: process.env.INSTANCE_CONNECTION_NAME,
    ipType: getIpType(),
  });
  const dbConfig = {
    // Please note that the `server` property here is not used and is only
    // defined due to a bug in the tedious driver
    // (ref: https://github.com/tediousjs/tedious/issues/1541)
    // With that in mind, do not try to change this value since it will have no
    // impact in how the connector works, this sample will be updated to remove
    // this property declaration as soon as the tedious driver bug is fixed
    server: '0.0.0.0', // e.g. '127.0.0.1'
    authentication: {
      type: 'default',
      options: {
        userName: process.env.DB_USER, // e.g. 'my-db-user'
        password: process.env.DB_PASS, // e.g. 'my-db-password'
      },
    },
    options: {
      ...clientOpts,
      // Please note that the `port` property here is not used and is only
      // defined due to a bug in the tedious driver
      // (ref: https://github.com/tediousjs/tedious/issues/1541)
      // With that in mind, do not try to change this value since it will have
      // no impact in how the connector works, this sample will be updated to
      // remove this property declaration as soon as the tedious driver bug is
      // fixed
      port: 9999,
      database: process.env.DB_NAME, // e.g. 'my-database'
      useColumnNames: true,
    },
    // ... Specify additional properties here.
    ...config,
  };

  // Establish a connection to the database.
  return new Connection(dbConfig);
};

强制执行

通过使用连接器强制执行,您可以强制仅使用 Cloud SQL Auth 代理或 Cloud SQL 语言连接器连接到 Cloud SQL 实例。强制使用连接器后,Cloud SQL 会拒绝与数据库的直接连接。

如果您使用的是启用了 Private Service Connect 的实例,则存在以下限制。如果实例已启用连接器强制执行,则您无法为该实例创建读取副本。同样,如果实例具有读取副本,则您无法为该实例启用连接器强制执行。

gcloud

如需强制仅使用 Cloud SQL Auth 代理或 Cloud SQL 语言连接器连接到实例,请使用 gcloud sql instances patch 命令:

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

INSTANCE_NAME 替换为您的 Cloud SQL 实例的名称。

REST

在使用任何请求数据之前,请先进行以下替换:

  • PROJECT_ID:包含实例的 Google Cloud 项目的 ID 或项目编号
  • 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 实例,您的应用必须已具有 VPC 访问权限。

调试连接问题

如需获得连接问题方面的更多帮助,请参阅问题排查调试连接问题页面。

后续步骤