Conectar a partir das funções do Cloud Run

Esta página contém informações e exemplos para conectar-se a uma instância do Cloud SQL a partir de um serviço em execução nas funções do Cloud Run.

Para obter instruções passo a passo sobre como executar um aplicativo Web de exemplo do Cloud Run Functions conectado ao Cloud SQL, consulte o início rápido para conexão a partir do Cloud Run Functions .

O Cloud SQL é um serviço de banco de dados totalmente gerenciado que ajuda você a configurar, manter, gerenciar e administrar seus bancos de dados relacionais na nuvem.

As funções do Cloud Run são uma solução de computação leve para desenvolvedores criarem funções autônomas e de propósito único que respondem a eventos do Cloud sem a necessidade de gerenciar um servidor ou ambiente de tempo de execução.

Configurar uma instância do Cloud SQL

  1. Habilite a API de administração do Cloud SQL no Google Cloud projeto do qual você está se conectando, caso ainda não tenha feito isso:

    Enable the API

  2. Crie uma instância do Cloud SQL para SQL Server . Recomendamos que você escolha um local de instância do Cloud SQL na mesma região do seu serviço Cloud Run para obter melhor latência, evitar custos de rede e reduzir os riscos de falhas entre regiões.

    Por padrão, o Cloud SQL atribui um endereço IP público a uma nova instância. Você também tem a opção de atribuir um endereço IP privado. Para obter mais informações sobre as opções de conectividade para ambos, consulte a página "Visão geral da conexão" .

Configurar funções do Cloud Run

As etapas para configurar as funções do Cloud Run dependem do tipo de endereço IP que você atribuiu à sua instância do Cloud SQL.

IP público (padrão)

As funções do Cloud Run oferecem suporte à conexão com o Cloud SQL para SQL Server por meio de IP público usando os conectores Go, Java e Python .

Para configurar as funções do Cloud Run para habilitar conexões com uma instância do Cloud SQL:
  • Confirme se a instância criada acima possui um endereço IP público. Você pode confirmar isso na página Visão geral da instância no Google Cloud console . Se precisar adicionar um endereço IP público, consulte Configurar IP público .
  • Obtenha o INSTANCE_CONNECTION_NAME da instância. Este valor está disponível:
    • Na página Visão geral da instância, no Google Cloud console , ou
    • Executando o seguinte comando: gcloud sql instances describe [INSTANCE_NAME]
  • Configure a conta de serviço para sua função. Se a conta de serviço de autorização pertencer a um projeto diferente da instância do Cloud SQL, você deverá habilitar a API de administração do Cloud SQL e adicionar a função IAM Cloud SQL Client a ambos os projetos.
  • Confirme se a conta de serviço tem essa função para que a conta possa se conectar ao Cloud SQL.
  • Se você estiver usando funções do Cloud Run e não funções do Cloud Run (1ª geração) , o seguinte será necessário (consulte também Configurar o Cloud Run ):
    1. Inicialmente implante sua função.
      Quando você começa a criar uma função do Cloud Run no Google Cloud console, o serviço Cloud Run subjacente ainda não foi criado. Não é possível configurar uma conexão com o Cloud SQL até que o serviço seja criado (implantando a função Cloud Run).
    2. No Google Cloud console, no canto superior direito da página Detalhes da função , em Desenvolvido pelo Cloud Run , clique no link para acessar o serviço subjacente do Cloud Run.
    3. Na página de detalhes do Cloud Run Service , selecione a guia Editar e implantar nova revisão .
    4. Siga as etapas padrão (como no caso de qualquer alteração de configuração) para definir uma nova configuração para uma conexão do Cloud SQL.
      Isso cria uma nova revisão do Cloud Run, e as revisões subsequentes recebem automaticamente essa conexão do Cloud SQL, a menos que você a altere explicitamente.

IP privado

Se a conta de serviço de autorização pertencer a um projeto diferente daquele que contém a instância do Cloud SQL, faça o seguinte:

  • Em ambos os projetos, ative a API de administração do Cloud SQL.
  • Para a conta de serviço no projeto que contém a instância do Cloud SQL, adicione as permissões do IAM .
Um conector de acesso VPC sem servidor usa endereços IP privados para gerenciar a comunicação com sua rede VPC. Para se conectar diretamente com endereços IP privados, você deve fazer o seguinte:
  1. Certifique-se de que a instância do Cloud SQL criada anteriormente tenha um endereço IP privado. Se precisar adicionar um, consulte Configurar IP privado para obter instruções.
  2. Crie um conector de acesso VPC sem servidor na mesma rede VPC da sua instância do Cloud SQL. Observe as seguintes condições:
    • A menos que você esteja usando uma VPC compartilhada , seu conector deve estar no mesmo projeto e região que o recurso que o utiliza, mas pode enviar tráfego para recursos em regiões diferentes.
    • O acesso VPC sem servidor oferece suporte à comunicação com redes VPC conectadas usando Cloud VPN e VPC Network Peering .
    • O acesso VPC sem servidor não oferece suporte a redes legadas .
  3. Configure as funções do Cloud Run para usar o conector.
  4. Conecte-se usando o endereço IP privado da sua instância e a porta 1433 .

Conectar ao Cloud SQL

Depois de configurar as funções do Cloud Run, você pode se conectar à sua instância do Cloud SQL.

IP público (padrão)

Para caminhos de IP públicos, as funções do Cloud Run fornecem criptografia e se conectam usando os conectores do Cloud SQL.

Conecte-se com conectores do Cloud SQL

Os conectores do Cloud SQL são bibliotecas específicas de linguagem que fornecem criptografia e autorização baseada em IAM ao se conectar a uma instância do Cloud SQL.

Pitão

Para ver este snippet no contexto de um aplicativo web, veja o README no 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

    # 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

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

Observação:

  • CLOUD_SQL_CONNECTION_NAME deve ser representado como <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • Veja os requisitos de versão do factory de soquete JDBC para o arquivo pom.xml aqui .


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

Ir

Para ver este snippet no contexto de um aplicativo web, veja o README no 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)
	}
	// 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
}

IP privado

Para caminhos IP privados, seu aplicativo se conecta diretamente à sua instância por meio de uma rede VPC. Este método usa TCP para se conectar diretamente à instância do Cloud SQL sem usar o Proxy de Autenticação do Cloud SQL.

Conecte-se com TCP

Conecte-se usando o endereço IP privado da sua instância do Cloud SQL como host e a porta 1433 .

Pitão

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

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

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

Observação:

  • CLOUD_SQL_CONNECTION_NAME deve ser representado como <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • Usar o argumento ipTypes=PRIVATE forçará o SocketFactory a se conectar com o IP privado associado de uma instância
  • Veja os requisitos de versão do factory de soquete JDBC para o arquivo pom.xml aqui .


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

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

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

Ir

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

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
}

PHP

Para ver este snippet no contexto de um aplicativo web, veja o README no GitHub .

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

Melhores práticas e outras informações

Você pode usar o Cloud SQL Auth Proxy ao testar seu aplicativo localmente. Consulte o guia de início rápido sobre como usar o Cloud SQL Auth Proxy para obter instruções detalhadas.

Pools de conexão

As conexões com bancos de dados subjacentes podem ser interrompidas, seja pelo próprio servidor de banco de dados ou pela infraestrutura subjacente às funções do Cloud Run. Recomendamos usar uma biblioteca de cliente compatível com pools de conexão que reconectam automaticamente as conexões de cliente interrompidas. Além disso, recomendamos usar um pool de conexão com escopo global para aumentar a probabilidade de sua função reutilizar a mesma conexão para invocações subsequentes da função e encerrar a conexão naturalmente quando a instância for removida (redução automática). Para obter exemplos mais detalhados sobre como usar pools de conexão, consulte Gerenciando conexões de banco de dados .

Limites de conexão

O Cloud SQL impõe um limite máximo de conexões simultâneas, e esses limites podem variar dependendo do mecanismo de banco de dados escolhido (consulte Cotas e Limites do Cloud SQL ). É recomendável usar uma conexão com as funções do Cloud Run, mas é importante definir o número máximo de conexões como 1.

Sempre que possível, você deve ter o cuidado de inicializar um pool de conexões apenas para funções que precisam de acesso ao seu banco de dados. Alguns pools de conexões criam conexões preventivamente, o que pode consumir recursos em excesso e ser contabilizado nos seus limites de conexão. Por esse motivo, é recomendável usar a Inicialização Preguiçosa para atrasar a criação de um pool de conexões até que seja necessário e incluí-lo apenas nas funções em que for usado.

Para obter exemplos mais detalhados sobre como limitar o número de conexões, consulte Gerenciando conexões de banco de dados .

Limites de cota da API

As funções do Cloud Run fornecem um mecanismo de conexão usando o Proxy de Autenticação do Cloud SQL, que utiliza a API de Administração do Cloud SQL. Os limites de cota da API se aplicam ao Proxy de Autenticação do Cloud SQL. A cota da API de Administração do Cloud SQL usada é aproximadamente o dobro do número de instâncias do Cloud SQL configuradas multiplicado pelo número total de funções implantadas. Você pode definir o número máximo de invocações simultâneas para modificar a cota esperada da API consumida. As funções do Cloud Run também impõem limites de taxa ao número de chamadas de API permitidas a cada 100 segundos.

O que vem a seguir