Conectar a partir de funções do Cloud Run

Nesta página, você verá informações e exemplos de conexão a uma instância do Cloud SQL por meio de um serviço em execução nas funções do Cloud Run.

Para instruções detalhadas sobre como executar um aplicativo da Web de amostra das funções do Cloud Run conectado ao Cloud SQL, consulte o Guia de início rápido para se conectar a partir das funções do Cloud Run.

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

Funções do Cloud Run é uma solução de computação leve para desenvolvedores. Ele cria funções autônomas com uma finalidade única que respondem a eventos do Cloud sem a necessidade de gerenciar um servidor ou um ambiente de execução.

Configurar uma instância do Cloud SQL

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

    Enable the API

  2. Crie uma instância do Cloud SQL for SQL Server. Recomendamos que você escolha um local da instância do Cloud SQL na mesma região do serviço do Cloud Run para melhorar a latência, evitar alguns custos de rede e reduzir os riscos de falha 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 mais informações sobre as opções de conectividade de 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 atribuído à instância do Cloud SQL.

IP público (padrão)

O Cloud Run functions dá suporte à conexão com o Cloud SQL para SQL Server por IP público usando os conectores Go, Java e Python.

Para configurar o Cloud Run functions e ativar conexões com uma instância do Cloud SQL:
  • Confirme se a instância criada acima tem um endereço IP público. Confirme essa informação na página Visão geral da instância no Console do Google Cloud. Se você precisar adicionar um endereço IP público, consulte Configurar IP público.
  • Consiga o INSTANCE_CONNECTION_NAME da instância. Esse valor está disponível:
    • Na página Visão geral da instância, no Console do Google Cloud
    • Ao executar o seguinte comando: gcloud sql instances describe [INSTANCE_NAME]
  • Configure a conta de serviço para a função. Se a conta de serviço que autorizará pertencer a um projeto diferente da instância do Cloud SQL, ative a API Cloud SQL Admin e adicione as permissões de IAM listadas acima em ambos os projetos. Confirme se a conta de serviço tem os papéis e permissões adequados para se conectar ao Cloud SQL.
    • Para se conectar ao Cloud SQL, a conta de serviço precisa de um dos papéis do IAM a seguir:
      • Cloud SQL Client (recomendável)
      • Cloud SQL Editor
      • Cloud SQL Admin
      Também é possível atribuir manualmente as seguintes permissões do IAM:
      • cloudsql.instances.connect
      • cloudsql.instances.get
  • Se você estiver usando Funções do Cloud Run e não Cloud Run functions (1ª geração), os itens a seguir são obrigatórios (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 Console do Google Cloud, o serviço subjacente do Cloud Run ainda não foi criado. Não é possível configurar uma conexão do Cloud SQL até que esse serviço seja criado (implantando a função do Cloud Run).
    2. No console do Google Cloud, no canto superior direito da página Detalhes da função, em Tecnologia do Cloud Run, clique no link para acessar or serviço subjacente do Cloud Run.
    3. Na página Detalhes do serviço do Cloud Run, selecione a guia Editar e implantar uma 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. As revisões subsequentes receberão automaticamente essa conexão do Cloud SQL, a menos que você a altere explicitamente.

IP particular

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 isto:

  • Nos dois projetos, ative a API Cloud SQL Admin.
  • Na 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 particulares para processar a comunicação com a rede VPC. Para se conectar diretamente com endereços IP particulares, faça isto:
  1. Verifique se a instância do Cloud SQL criada anteriormente tem um endereço IP particular. Se você precisar adicionar um, consulte Configurar IP particular para ver 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 a VPC compartilhada, é necessário que um conector esteja no mesmo projeto e na mesma região do recurso que o utiliza, mas ele pode enviar tráfego para recursos em regiões diferentes.
    • O acesso VPC sem servidor é compatível com a comunicação com redes VPC conectadas usando o Cloud VPN e o peering de rede VPC.
    • O acesso VPC sem servidor não é compatível com redes legadas.
  3. Configure as funções do Cloud Run para usar o conector.
  4. Conecte-se usando o endereço IP particular e a porta 1433 da instância.

Conecte-se ao Cloud SQL

Depois de configurar as funções do Cloud Run, conecte-se à instância do Cloud SQL.

IP público (padrão)

No caso de caminhos de IP público, o Cloud Run functions fornece criptografia e conecta-se usando os conectores do Cloud SQL.

Conectar-se com conectores do Cloud SQL

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

Python

Para conferir esse snippet no contexto de um aplicativo da Web, consulte 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

    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

Java

Para conferir esse snippet no contexto de um aplicativo da Web, consulte o README no GitHub.

Observação:

  • CLOUD_SQL_CONNECTION_NAME precisa ser representado como <MEU-PROJETO>:<REGIÃO-DA-INSTÂNCIA>:<NOME-DA-INSTÂNCIA>
  • Consulte os requisitos de versão de fábrica do 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");

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

    // Initialize the connection pool using the configuration object.
    return new HikariDataSource(config);
  }
}

Go

Para conferir esse snippet no contexto de um aplicativo da Web, consulte 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)
	}
	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
}

IP particular

Para caminhos IP particulares, o aplicativo se conecta diretamente à instância por uma rede VPC. Esse método usa TCP para a conexão direta com a instância do Cloud SQL sem usar o proxy de autenticação do Cloud SQL.

Conectar com TCP

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

Python

Para conferir esse snippet no contexto de um aplicativo da Web, consulte 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 conferir esse snippet no contexto de um aplicativo da Web, consulte o README no GitHub.

Observação:

  • CLOUD_SQL_CONNECTION_NAME precisa ser representado como <MEU-PROJETO>:<REGIÃO-DA-INSTÂNCIA>:<NOME-DA-INSTÂNCIA>
  • O uso do argumento ipTypes=PRIVATE forçará o SocketFactory a se conectar ao IP privado associado de uma instância
  • Consulte os requisitos de versão de fábrica do 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 conferir esse snippet no contexto de um aplicativo da Web, consulte 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);
};

Go

Para conferir esse snippet no contexto de um aplicativo da Web, consulte 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 conferir esse snippet no contexto de um aplicativo da Web, consulte 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;
    }
}

Práticas recomendadas e outras informações

Use o proxy do Cloud SQL Auth ao testar seu aplicativo localmente. Consulte o guia de início rápido para o uso do proxy de autenticação do Cloud SQL e confira instruções detalhadas.

Pools de conexões

As conexões com bancos de dados subjacentes podem ser descartadas pelo próprio servidor de banco de dados ou pela infraestrutura subjacente às funções do Cloud Run. Recomendamos o uso de uma biblioteca de cliente compatível com pools de conexão que reconectam automaticamente conexões de cliente corrompidas. Além disso, recomendamos o uso de um pool de conexão de escopo global. Com ele, é mais provável que sua função reutilize a mesma conexão para as próximas invocações da função e feche a conexão de maneira natural quando a instância for removida (reduzida automaticamente). Para exemplos mais detalhados sobre como usar pools de conexão, consulte Como gerenciar 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 esse limite pode variar dependendo do mecanismo de banco de dados escolhido (consulte a página "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, inicialize um pool de conexões apenas para funções que precisem de acesso ao banco de dados. Alguns pools de conexões criam conexões preventivamente, que podem consumir recursos excessivos e contar nos limites de conexão. Por esse motivo, recomendamos usar a Inicialização lenta para atrasar a criação de um pool de conexões até ser necessário e incluir somente o pool em funções em que ele é usado.

Para exemplos mais detalhados sobre como limitar o número de conexões de bancos de dados, consulte Como gerenciar conexões de bancos de dados.

Limites de cota da API

As funções do Cloud Run fornecem um mecanismo que se conecta usando o proxy do Cloud SQL Auth, por meio da API Cloud SQL Admin. Os limites de cota da API se aplicam ao proxy do Cloud SQL Auth. A cota da API Cloud SQL Admin usada é aproximadamente duas vezes o número de instâncias do Cloud SQL configuradas vezes o número total de funções implantadas. É possível 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.

A seguir