从 Cloud Run(全托管式)连接到 Cloud SQL

本页面介绍从 Cloud Run(全托管式)中运行的服务连接到 Cloud SQL 实例的相关信息和示例。

Cloud SQL 是一项全代管式数据库服务,可帮助您在云端设置、维护、管理和控制关系型数据库。

设置 Cloud SQL 实例

  1. 在您要从中进行连接的项目中启用 Cloud SQL Admin API(如果您尚未执行此操作):

    启用 API

  2. 创建 Cloud SQL for MySQL 实例

    默认情况下,Cloud SQL 会为新实例分配公共 IP 地址。您还可以选择分配专用 IP 地址。如需详细了解这两种地址的连接方案,请参阅连接概览页面。

配置 Cloud Run(全代管式)

配置 Cloud Run(全代管式)的步骤取决于您分配给 Cloud SQL 实例的 IP 地址类型。

公共 IP(默认)

  • 确保之前创建的实例具有公共 IP 地址。您可以在 Google Cloud Console 中的实例概览页面上验证这一点。如果您需要添加 IP 地址,请参阅配置公共 IP 页面来查看相关说明。
  • 获取实例的 INSTANCE_CONNECTION_NAME。您可以在 Google Cloud Console 中的实例概览页面上找到它,也可以通过运行命令 gcloud sql instances describe [INSTANCE_NAME] 来获取。
  • 为您的服务配置服务帐号。请确保该服务帐号具有适当的 Cloud SQL 角色和权限以连接到 Cloud SQL
      您的服务的服务帐号需要以下某个 IAM 角色
      • Cloud SQL Client(首选)
      • Cloud SQL Editor
      • Cloud SQL Admin
      或者,您也可以手动分配下列 IAM 权限:
      • cloudsql.instances.connect
      • cloudsql.instances.get

    如果授权服务帐号与 Cloud SQL 实例分处不同的项目,则需要为这两个项目添加 Cloud SQL Admin API 和 IAM 权限。

与任何配置更改一样,为 Cloud SQL 连接设置新配置会导致创建新的 Cloud Run 修订版本。除非您进行了明确更新,否则后续修订版本也将自动采用此 Cloud SQL 连接。

控制台

  1. 转到 Cloud Run

  2. 配置服务:

    • 如果您要向新服务添加 Cloud SQL 连接,请执行以下操作:

      • 您需要将服务容器化并上传到 Container Registry。如果您还没有容器映像,请参阅这些有关构建和部署容器映像的说明。

      • 点击创建服务

    • 如果您要向现有服务添加 Cloud SQL 连接,请执行以下操作:

      • 点击服务名称。

      • 点击部署新的修订版本

  3. 启用连接到 Cloud SQL:

    • 点击显示可选设置

    添加 Cloud SQL 连接

    • 如果您是在向自己项目中的 Cloud SQL 实例添加连接,请从下拉菜单中选择所需的 Cloud SQL 实例。
    • 如果您使用的是来自其他项目的 Cloud SQL 实例,请在下拉列表中选择自定义连接字符串,然后以 PROJECT-ID:REGION:INSTANCE-ID 格式输入完整实例连接名称。

    • 如果您要删除连接,请将光标悬停在连接右侧以显示垃圾箱图标,然后点击该图标。

  4. 点击创建部署

命令行

在使用下面的任何命令之前,请先进行以下替换:

  • IMAGE 替换为您要部署的映像
  • SERVICE-NAME 替换为您的 Cloud Run 服务的名称
  • INSTANCE-CONNECTION-NAME 替换为 Cloud SQL 实例的实例连接名称,或以英文逗号分隔的连接名称列表。

    如果要部署新容器,请使用以下命令:

    gcloud run deploy --image IMAGE \
      --add-cloudsql-instances INSTANCE-CONNECTION-NAME \
      --update-env-vars INSTANCE_CONNECTION_NAME="INSTANCE-CONNECTION-NAME"
    如果要更新现有服务,请使用以下命令:
    gcloud run services update SERVICE-NAME \
      --add-cloudsql-instances INSTANCE-CONNECTION-NAME \
      --update-env-vars INSTANCE_CONNECTION_NAME="INSTANCE-CONNECTION-NAME"

专用 IP

无服务器 VPC 访问通道连接器会处理与您的 VPC 网络的通信。 如需直接使用专用 IP 进行连接,您需要:

  1. 确保上面创建的 Cloud SQL 实例具有专用 IP 地址。如果您需要添加 IP 地址,请参阅配置专用 IP 页面来查看相关说明。
  2. 在 Cloud SQL 实例所在的同一 VPC 网络中创建无服务器 VPC 访问通道连接器。
  3. 除非您使用共享 VPC,否则连接器必须与使用它的资源位于同一项目和区域中,但该连接器可向其他区域中的资源发送流量。

    无服务器 VPC 访问通道支持与通过 Cloud VPNVPC 网络对等互连连接的 VPC 网络进行通信。

    无服务器 VPC 访问通道不支持旧版网络

  4. 将 Cloud Run(全代管式)配置为使用连接器
  5. 使用实例的专用 IP 和端口 3306 进行连接。

连接到 Cloud SQL

配置 Cloud Run(全代管式)后,您可以连接到 Cloud SQL 实例。

公共 IP(默认)

对于公共 IP 路径,Cloud Run(全代管式)通过 Cloud SQL Auth 代理为连接提供加密/授权

使用 Unix 套接字进行连接

正确配置后,您就可以将服务连接到在以下路径上通过环境文件系统访问的 Cloud SQL 实例的 Unix 域套接字:/cloudsql/INSTANCE_CONNECTION_NAME

可以在 Google Cloud Console 中实例的概览页面上或者透过运行以下命令来找到 INSTANCE_CONNECTION_NAME

gcloud sql instances describe [INSTANCE_NAME]

这些连接会自动加密,无需任何额外配置。

下面显示的代码示例摘自 GitHub 网站上提供的更完整示例。如需了解详情,请点击 View on GitHub

Python

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

# Remember - storing secrets in plaintext is potentially unsafe. Consider using
# something like https://cloud.google.com/secret-manager/docs/overview to help keep
# secrets secret.
db_user = os.environ["DB_USER"]
db_pass = os.environ["DB_PASS"]
db_name = os.environ["DB_NAME"]
db_socket_dir = os.environ.get("DB_SOCKET_DIR", "/cloudsql")
cloud_sql_connection_name = os.environ["CLOUD_SQL_CONNECTION_NAME"]

pool = sqlalchemy.create_engine(
    # Equivalent URL:
    # mysql+pymysql://<db_user>:<db_pass>@/<db_name>?unix_socket=<socket_path>/<cloud_sql_instance_name>
    sqlalchemy.engine.url.URL(
        drivername="mysql+pymysql",
        username=db_user,  # e.g. "my-database-user"
        password=db_pass,  # e.g. "my-database-password"
        database=db_name,  # e.g. "my-database-name"
        query={
            "unix_socket": "{}/{}".format(
                db_socket_dir,  # e.g. "/cloudsql"
                cloud_sql_connection_name)  # i.e "<PROJECT-NAME>:<INSTANCE-REGION>:<INSTANCE-NAME>"
        }
    ),
    **db_config
)

Java

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

// Note: For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections
// which is preferred to using the Cloud SQL Proxy with Unix sockets.
// See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details.

// 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:mysql:///<DB_NAME>?cloudSqlInstance=<CLOUD_SQL_CONNECTION_NAME>&
// socketFactory=com.google.cloud.sql.mysql.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:mysql:///%s", DB_NAME));
config.setUsername(DB_USER); // e.g. "root", "mysql"
config.setPassword(DB_PASS); // e.g. "my-password"

config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
config.addDataSourceProperty("cloudSqlInstance", CLOUD_SQL_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");

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

// Initialize the connection pool using the configuration object.
DataSource pool = new HikariDataSource(config);

Node.js

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

const createUnixSocketPool = async config => {
  const dbSocketPath = process.env.DB_SOCKET_PATH || '/cloudsql';

  // Establish a connection to the database
  return await mysql.createPool({
    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'
    // If connecting via unix domain socket, specify the path
    socketPath: `${dbSocketPath}/${process.env.CLOUD_SQL_CONNECTION_NAME}`,
    // Specify additional properties here.
    ...config,
  });
};

C#

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

// Equivalent connection string:
// "Server=<dbSocketDir>/<INSTANCE_CONNECTION_NAME>;Uid=<DB_USER>;Pwd=<DB_PASS>;Database=<DB_NAME>;Protocol=unix"
String dbSocketDir = Environment.GetEnvironmentVariable("DB_SOCKET_PATH") ?? "/cloudsql";
String instanceConnectionName = Environment.GetEnvironmentVariable("INSTANCE_CONNECTION_NAME");
var connectionString = new MySqlConnectionStringBuilder()
{
    // The Cloud SQL proxy provides encryption between the proxy and instance.
    SslMode = MySqlSslMode.None,
    // Remember - storing secrets in plain text is potentially unsafe. Consider using
    // something like https://cloud.google.com/secret-manager/docs/overview to help keep
    // secrets secret.
    Server = String.Format("{0}/{1}", dbSocketDir, instanceConnectionName),
    UserID = Environment.GetEnvironmentVariable("DB_USER"),   // e.g. 'my-db-user
    Password = Environment.GetEnvironmentVariable("DB_PASS"), // e.g. 'my-db-password'
    Database = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'
    ConnectionProtocol = MySqlConnectionProtocol.UnixSocket
};
connectionString.Pooling = true;
// Specify additional properties here.
return connectionString;

Go

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

var (
	dbUser                 = mustGetenv("DB_USER")                  // e.g. 'my-db-user'
	dbPwd                  = mustGetenv("DB_PASS")                  // e.g. 'my-db-password'
	instanceConnectionName = mustGetenv("INSTANCE_CONNECTION_NAME") // e.g. 'project:region:instance'
	dbName                 = mustGetenv("DB_NAME")                  // e.g. 'my-database'
)

socketDir, isSet := os.LookupEnv("DB_SOCKET_DIR")
if !isSet {
	socketDir = "/cloudsql"
}

var dbURI string
dbURI = fmt.Sprintf("%s:%s@unix(/%s/%s)/%s?parseTime=true", dbUser, dbPwd, socketDir, instanceConnectionName, dbName)

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

// ...

return dbPool, nil

Ruby

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

production:
  adapter: mysql2
  # Configure additional properties here.
  username: <%= ENV["DB_USER"] %>  # e.g. "my-database-user"
  password: <%= ENV["DB_PASS"] %> # e.g. "my-database-password"
  database: <%= ENV.fetch("DB_NAME") { "vote_production" } %>
  socket: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["INSTANCE_CONNECTION_NAME"] %>"

PHP

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

// $username = 'your_db_user';
// $password = 'yoursupersecretpassword';
// $dbName = 'your_db_name';
// $connectionName = getenv("CLOUD_SQL_CONNECTION_NAME");
// $socketDir = getenv('DB_SOCKET_DIR') ?: '/cloudsql';

// Connect using UNIX sockets
$dsn = sprintf(
    'mysql:dbname=%s;unix_socket=%s/%s',
    $dbName,
    $socketDir,
    $connectionName
);

// Connect to the database.
$conn = new PDO($dsn, $username, $password, $conn_config);

专用 IP

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

使用 TCP 进行连接

直接使用您实例的专用 IP 地址和端口 3306 进行连接。

Python

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

# Remember - storing secrets in plaintext is potentially unsafe. Consider using
# something like https://cloud.google.com/secret-manager/docs/overview to help keep
# secrets secret.
db_user = os.environ["DB_USER"]
db_pass = os.environ["DB_PASS"]
db_name = os.environ["DB_NAME"]
db_host = os.environ["DB_HOST"]

# Extract host and port from db_host
host_args = db_host.split(":")
db_hostname, db_port = host_args[0], int(host_args[1])

pool = sqlalchemy.create_engine(
    # Equivalent URL:
    # mysql+pymysql://<db_user>:<db_pass>@<db_host>:<db_port>/<db_name>
    sqlalchemy.engine.url.URL(
        drivername="mysql+pymysql",
        username=db_user,  # e.g. "my-database-user"
        password=db_pass,  # e.g. "my-database-password"
        host=db_hostname,  # e.g. "127.0.0.1"
        port=db_port,  # e.g. 3306
        database=db_name,  # e.g. "my-database-name"
    ),
    **db_config
)

Java

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

// Note: For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections
// which is preferred to using the Cloud SQL Proxy with Unix sockets.
// See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details.

// 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:mysql:///<DB_NAME>?cloudSqlInstance=<CLOUD_SQL_CONNECTION_NAME>&
// socketFactory=com.google.cloud.sql.mysql.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:mysql:///%s", DB_NAME));
config.setUsername(DB_USER); // e.g. "root", "mysql"
config.setPassword(DB_PASS); // e.g. "my-password"

config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
config.addDataSourceProperty("cloudSqlInstance", CLOUD_SQL_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");

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

// Initialize the connection pool using the configuration object.
DataSource pool = new HikariDataSource(config);

Node.js

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

const createTcpPool = async config => {
  // Extract host and port from socket address
  const dbSocketAddr = process.env.DB_HOST.split(':');

  // Establish a connection to the database
  return await mysql.createPool({
    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'
    host: dbSocketAddr[0], // e.g. '127.0.0.1'
    port: dbSocketAddr[1], // e.g. '3306'
    // ... Specify additional properties here.
    ...config,
  });
};

Go

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

var (
	dbUser    = mustGetenv("DB_USER") // e.g. 'my-db-user'
	dbPwd     = mustGetenv("DB_PASS") // e.g. 'my-db-password'
	dbTCPHost = mustGetenv("DB_HOST") // e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
	dbPort    = mustGetenv("DB_PORT") // e.g. '3306'
	dbName    = mustGetenv("DB_NAME") // e.g. 'my-database'
)

var dbURI string
dbURI = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", dbUser, dbPwd, dbTCPHost, dbPort, dbName)

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

// ...

return dbPool, nil

C#

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

            // Equivalent connection string:
            // "Uid=<DB_USER>;Pwd=<DB_PASS>;Host=<DB_HOST>;Database=<DB_NAME>;"
            var connectionString = new MySqlConnectionStringBuilder()
            {
                // The Cloud SQL proxy provides encryption between the proxy and instance.
                SslMode = MySqlSslMode.None,

                // Remember - storing secrets in plain text is potentially unsafe. Consider using
                // something like https://cloud.google.com/secret-manager/docs/overview to help keep
                // secrets secret.
                Server = Environment.GetEnvironmentVariable("DB_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'
                Database = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            return connectionString;

Ruby

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

development:
  adapter: mysql2
  # Configure additional properties here
  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("DB_HOST") { "127.0.0.1" }%>" # '172.17.0.1' if deployed to GAE Flex
  port: <%= ENV.fetch("DB_PORT") { 3306 }%>
  socket: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["INSTANCE_CONNECTION_NAME"] %>"

PHP

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

// $username = 'your_db_user';
// $password = 'yoursupersecretpassword';
// $dbName = 'your_db_name';
// $dbHost = "127.0.0.1";

// Connect using TCP
$dsn = sprintf('mysql:dbname=%s;host=%s', $dbName, $dbHost);

// Connect to the database
$conn = new PDO($dsn, $username, $password, $connConfig);

最佳做法和其他信息

在本地测试应用时,您可以使用 Cloud SQL Auth 代理。如需了解详细说明,请参阅快速入门:使用 Cloud SQL Auth 代理

您也可以通过 Docker 容器使用 Cloud SQL 代理进行测试。

连接池

与底层数据库的连接可能会被数据库服务器本身或平台基础架构断开。我们建议您使用支持连接池的客户端库,因为连接池可自动重新连接断开的客户端连接。如需查看有关如何使用连接池的详细示例,请参阅管理数据库连接页面。

连接限制

MySQL 和 PostgreSQL 版本的 Cloud SQL 都对并发连接数设定了上限,这些限制可能因所选的数据库引擎而异(请参阅 Cloud SQL 配额和限制页面)。

Cloud Run(全代管式)服务与一个 Cloud SQL 数据库之间最多只能建立 100 个连接。此限制适用于单个服务实例。这意味着每个 Cloud Run(全代管式)服务实例可以与数据库建立 100 个连接,并且在扩容时,每个部署的连接总数可以随之增加。

您可以使用连接池来限制每个实例使用的最大连接数。如需查看有关如何限制连接数的详细示例,请参阅管理数据库连接页面。

API 配额限制

Cloud Run(全代管式)提供了一种使用 Cloud SQL Auth 代理进行连接的机制,该代理使用 Cloud SQL Admin API。API 配额限制适用于 Cloud SQL Auth 代理。 使用的 Cloud SQL Admin API 配额大约是任何一次部署的特定服务的 Cloud Run 实例数所配置 Cloud SQL 实例数的 2 倍。您可以设置 Cloud Run 实例数上限,以限制预期使用的 API 配额。

后续步骤