连接选项简介

本页面简要介绍了可用于连接到 Cloud SQL 实例的方式,并介绍了可用的身份验证和授权选项。

概览

在考虑如何连接到 Cloud SQL 实例时,您需要考虑多项选择,其中包括:

  • 您是否希望可以从互联网访问您的 Cloud SQL 实例并且/或者希望在 Virtual Private Cloud (VPC) 网络中保持其私密状态?
  • 您打算编写自己的连接代码,或使用 Cloud SQL Auth 代理或 sqlcmd 客户端等公开提供的工具进行连接吗?
  • 您希望要求通过 SSL/TLS 加密,还是希望允许未加密的流量?

在以下部分中,我们将讨论 Cloud SQL 为数据库连接、授权和身份验证提供的选项。

  • 如何连接 - 使用哪条网络路径访问您的实例:
    • 仅限 VPC 的内部(专用)IP 地址。
    • 可通过互联网访问的外部(公共)IP 地址。
  • 如何进行授权 - 哪些连接已获授权且可以连接到您的 Cloud SQL 实例:
    • Cloud SQL Auth 代理以及 Java 版和 Python 版 Cloud SQL 连接器库 - 这些基于 IAM 提供访问权限。
    • 自行管理的 SSL/TLS 证书 - 仅允许基于特定公钥的连接。
    • 已获授权的网络 - 可以连接的 IP 地址列表。
  • 如何进行身份验证 - 登录数据库的方法。
    • 内置数据库身份验证 - 使用在数据库引擎中设置的用户名/密码登录。

以下信息可用于决定哪些连接、授权和身份验证选项最适合您。

开始前须知

授予对一个应用的访问权限不会自动允许数据库用户账号连接到该实例。您必须首先具备可用于连接的数据库用户账号,之后才能连接到实例。对于新实例,这就表示您必须已配置好默认用户账号。如需了解详情,请参阅使用内置身份验证管理用户

如何连接到 Cloud SQL

数据库连接会消耗服务器和连接应用上的资源。请始终采用最佳连接管理做法,以最大限度减少应用的占用空间,并降低超出 Cloud SQL 连接限制的可能性。 如需了解详情,请参阅管理数据库连接

公共 IP 和专用 IP

在 Cloud SQL 中,公共 IP 地址表示可通过公共互联网访问实例。相比之下,仅使用专用 IP 地址的实例不能通过公共互联网访问,但可以通过 Virtual Private Cloud (VPC) 访问。Cloud SQL 实例可以同时拥有公共 IP 地址和专用 IP 地址。

专用 IP

专用 IP 是可在 Virtual Private Cloud (VPC) 上访问的 IPv4 地址。

您可以使用此地址从能够访问 VPC 的其他资源进行连接。通过专用 IP 地址建立的连接通常可缩短延迟时间并限制攻击途径,因为它们不需要遍历互联网。或者,您可以要求所有连接都使用 Cloud SQL 代理自行管理的 SSL 证书

从可访问 VPC 的资源上的客户端连接时,最好使用专用 IP 配置实例。如需详细了解哪些资源可以使用专用 IP,请参阅专用 IP 的要求

对于专用 IP 路径,以下服务和应用通过无服务器 VPC 访问通道直接连接到您的实例:

  • App Engine 标准环境
  • App Engine 柔性环境
  • Cloud Functions
  • Cloud Run

详细了解如何将专用 IP 与 Cloud SQL 搭配使用

如需了解如何将专用 IP 添加到实例,请参阅以下内容之一:

公共 IP

公共 IP 是可在外部通过公共互联网使用的 IPv4 地址。此类地址可以接收来自 Google 网络内部和外部设备的连接,包括来自您家中或办公室等位置的连接。

为了帮助确保实例的安全性,必须使用 Cloud SQL Auth 代理已获授权的网络对使用公共 IP 与 Cloud SQL 实例建立的所有连接进行授权。

从不符合 VPC 要求的客户端连接时,最好使用公共 IP 配置实例。

如需了解如何向实例添加公共 IP 地址,请参阅配置公共 IP 连接

如需了解如何使用公共 IP 地址将 sqlcmd 客户端连接到 Cloud SQL 实例,请参阅使用数据库客户端进行连接

如何为 Cloud SQL 连接授权

Cloud SQL 语言连接器

Cloud SQL 语言连接器是在连接到 Cloud SQL 实例时提供加密和 IAM 授权的客户端库。Cloud SQL 建议使用 Cloud SQL 语言连接器,通过其他连接选项连接到 Cloud SQL 实例。

您可以直接在支持的编程语言中使用这些库。它们提供与 Cloud SQL 身份验证代理相同的身份验证,而无需外部流程。这提高了安全性并减少了连接到 Cloud SQL 的配置要求。使用公共 IP 地址或专用 IP 地址进行连接时,Cloud SQL 语言连接器也会使用相同的代码。

如需开始使用,请参阅关于 Cloud SQL 语言连接器

Cloud SQL Auth 代理

借助 Cloud SQL Auth 代理,您可以使用 Identity and Access Management (IAM) 权限来授权和保护连接。Cloud SQL Auth 代理通过以下方式验证连接:使用用户或服务账号的凭据,并将连接封装在针对 Cloud SQL 实例授权的 SSL/TLS 层中。如需详细了解 Cloud SQL Auth 代理的工作原理,请参阅 Cloud SQL Auth 代理简介

若要对 Cloud SQL 实例的连接进行身份验证,建议使用 Cloud SQL Auth 代理,因为这是最安全的方法。

Cloud SQL Auth 代理是一个作为可执行二进制文件分发的开源库。Cloud SQL Auth 代理充当中间服务器,负责监听传入连接,将其封装在 SSL/TLS 中,然后将其传递给 Cloud SQL 实例。

某些环境提供了一种使用 Cloud SQL Auth 代理进行连接的机制。如需了解如何使用这些环境进行连接,请参阅以下内容之一:

自行管理的 SSL/TLS 证书

您可以设置特定于 Cloud SQL 实例的客户端/服务器 SSL/TLS 证书,而无需使用 Cloud SQL Auth 代理来加密连接。这些证书用于客户端和服务器之间的相互验证,同时加密它们之间的连接。

不使用 Cloud SQL Auth 代理时,强烈建议使用自行管理的 SSL/TLS 证书来提供加密。否则,您的数据会以不安全的方式传输,并且可能被第三方拦截或查看。

如需开始使用自行管理的 SSL/TLS 证书,请参阅使用 SSL/TLS 证书进行授权

已获授权的网络

除非使用 Cloud SQL Auth 代理,否则只有当连接来自已获授权的网络时,才允许与实例的公共 IP 地址的连接。已获授权的网络是用户已指定有连接权限的 IP 地址或范围。

如需开始使用已获授权的网络,请参阅使用已获授权的网络进行授权

如何向 Cloud SQL 进行身份验证

身份验证通过验证用户的身份来提供访问权限控制。对于最终用户,身份验证在用户输入凭据(用户名和密码)时完成。对于应用,身份验证在用户的凭据分配给服务账号时完成。

Cloud SQL 使用数据库的内置身份验证,该用户名和密码使用用户名和密码进行身份验证。如需了解详情,请参阅创建和管理 SQL Server 用户

用于连接到 Cloud SQL 的工具

下表包含一些连接到 Cloud SQL 的选项:

连接选项 更多信息
Cloud SQL Auth 代理
gcloud CLI
Cloud SQL 语言连接器
Cloud Shell
Cloud Code
使用第三方数据库管理工具连接
SQL Server Management Studio
SSMS 对象探索器
Visual Studio

代码示例

您可以使用支持连接到 TCP 套接字的任何语言连接到 Cloud SQL Auth 代理。以下是 GitHub 上完整示例的一些代码段,可帮助您了解它们在您的应用中如何协同工作。

使用 TCP 进行连接

Cloud SQL Auth 代理调用语句:

./cloud-sql-proxy INSTANCE_CONNECTION_NAME &

Python

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

如需了解 Web 应用环境下的此代码段,请查看 GitHub 上的 README

注意:

  • CLOUD_SQL_CONNECTION_NAME 应表示为 <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • 使用参数 ipTypes=PRIVATE 将强制 SocketFactory 与实例的关联专用 IP 连接
  • 如需查看 pom.xml 文件的 JDBC 套接字工厂版本的要求,请点击此处


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

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

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

C#

如需了解 Web 应用环境下的此代码段,请查看 GitHub 上的 README

using Microsoft.Data.SqlClient;
using System;

namespace CloudSql
{
    public class SqlServerTcp
    {
        public static SqlConnectionStringBuilder NewSqlServerTCPConnectionString()
        {
            // Equivalent connection string:
            // "User Id=<DB_USER>;Password=<DB_PASS>;Server=<INSTANCE_HOST>;Database=<DB_NAME>;"
            var connectionString = new SqlConnectionStringBuilder()
            {
                // 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.
                DataSource = Environment.GetEnvironmentVariable("INSTANCE_HOST"), // e.g. '127.0.0.1'
                // Set Host to 'cloudsql' when deploying to App Engine Flexible environment
                UserID = Environment.GetEnvironmentVariable("DB_USER"),         // e.g. 'my-db-user'
                Password = Environment.GetEnvironmentVariable("DB_PASS"),       // e.g. 'my-db-password'
                InitialCatalog = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'

                // The Cloud SQL proxy provides encryption between the proxy and instance
                Encrypt = false,
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            return connectionString;
        }
    }
}

Ruby

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

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

问题排查

如果您在连接时遇到问题,请查看以下页面,以获取有关调试或查找已知问题的解决方案的相关帮助:

后续步骤