Estabeleça ligação a partir do ambiente flexível do App Engine

Esta página contém informações e exemplos para estabelecer ligação a uma instância do Cloud SQL a partir de um serviço executado no ambiente flexível do App Engine.

O Cloud SQL é um serviço de base de dados totalmente gerido que ajuda na configuração, manutenção, gestão e administração das suas bases de dados relacionais na nuvem.

O App Engine é uma plataforma sem servidor totalmente gerida para desenvolver e alojar apps Web em grande escala. Pode escolher entre várias linguagens, bibliotecas e frameworks populares para desenvolver as suas apps e, em seguida, deixar que o App Engine se encarregue do aprovisionamento de servidores e do dimensionamento das suas instâncias de apps com base na procura.

Configure uma instância do Cloud SQL

  1. Ative a API Cloud SQL Admin no Google Cloud projeto a partir do qual está a estabelecer ligação, se ainda não o tiver feito:

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

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

    Por predefinição, o Cloud SQL atribui um endereço IP público a uma nova instância. Também tem a opção de atribuir um endereço IP privado. Para mais informações sobre as opções de conetividade para ambos, consulte a página Vista geral da ligação.

  3. Quando cria a instância, pode escolher a hierarquia do certificado do servidor (AC) para a instância e, em seguida, configurar a hierarquia como a serverCaMode para a instância. Tem de selecionar a opção de AC por instância (GOOGLE_MANAGED_INTERNAL_CA) como o modo de AC do servidor para instâncias às quais quer estabelecer ligação a partir de aplicações Web do ambiente flexível do App Engine.

Configure o ambiente flexível do App Engine

Os passos para configurar o ambiente flexível do App Engine dependem do tipo de endereço IP que atribuiu à sua instância do Cloud SQL.

IP público (predefinição)

Para configurar o ambiente flexível do App Engine para ativar as ligações a uma instância do Cloud SQL:

  1. Certifique-se de que a instância tem um endereço IP público. Pode verificar esta situação na página Vista geral da sua instância na Google Cloud consola. Se precisar de adicionar um, consulte a página de configuração do IP público para ver instruções.
  2. Obtenha o INSTANCE_CONNECTION_NAME para a sua instância. Pode encontrar este valor na página Vista geral da sua instância na Google Cloud consola ou executando o seguinte comando: gcloud sql instances describe
    gcloud sql instances describe INSTANCE_NAME
       
    Substitua INSTANCE_NAME pelo nome da sua instância do Cloud SQL.
  3. Certifique-se de que a conta de serviço que a sua app está a usar para autenticar chamadas para o Cloud SQL tem a Cloud SQL Client função do IAM.

    Para obter instruções detalhadas sobre como adicionar funções do IAM a uma conta de serviço, consulte o artigo Conceder funções a contas de serviço.

Por predefinição, a sua app autoriza as ligações através da conta de serviço do ambiente flexível do App Engine. A conta de serviço está no formato PROJECT_ID@appspot.gserviceaccount.com.

Se a conta de serviço de autorização pertencer a um projeto diferente da instância do Cloud SQL, tem de adicionar a API Admin do Cloud SQL e as autorizações de IAM para ambos os projetos.

  • Atualize o ficheiro app.yaml do seu projeto com a opção mais adequada. Pode usar uma lista de instâncias separadas por vírgulas para especificar várias opções em simultâneo.

    Ativar uma porta TCP

    Para ativar uma porta TCP local, adicione um dos seguintes elementos ao ficheiro app.yaml do seu projeto, consoante esteja a estabelecer ligação a uma ou várias instâncias:
    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME=tcp:PORT
    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME_1=tcp:PORT_1,INSTANCE_CONNECTION_NAME_2=tcp:PORT_2,...
  • IP privado

    Para se ligar à sua instância do Cloud SQL através de um IP privado, a implementação do ambiente flexível do App Engine tem de estar na mesma rede VPC que a instância do Cloud SQL. Consulte a documentação de configuração em Definições de rede para ver instruções sobre como especificar uma rede VPC para a sua implementação.

    Após a implementação, a sua aplicação vai poder estabelecer ligação diretamente através do endereço IP privado e da porta 1433 da instância.

    Estabeleça ligação ao Cloud SQL

    Depois de configurar o ambiente flexível do App Engine, pode ligar-se à sua instância do Cloud SQL.

    IP público (predefinição)

    Para caminhos de IP público, o ambiente flexível do App Engine oferece encriptação e liga-se através do proxy Auth do Cloud SQL de duas formas:

    IP privado

    Estabeleça ligação com TCP

    Estabeleça ligação através do endereço IP privado da sua instância do Cloud SQL como anfitrião e porta 1433.

    Python

    Para ver este fragmento no contexto de uma app Web, consulte o ficheiro 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 fragmento no contexto de uma app Web, consulte o ficheiro README no GitHub.

    Nota:

    
    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 fragmento no contexto de uma app Web, consulte o ficheiro 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 fragmento no contexto de uma app Web, consulte o ficheiro 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
    }
    

    C#

    Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no GitHub.

    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

    Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no GitHub.

    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

    Para ver este fragmento no contexto de uma app Web, consulte o ficheiro 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

    Pode usar o proxy Auth do Cloud SQL quando testar a sua aplicação localmente. Consulte o início rápido para usar o proxy Auth do Cloud SQL para ver instruções detalhadas.

    Conjuntos de ligações

    As ligações às bases de dados subjacentes podem ser interrompidas pelo próprio servidor da base de dados ou pela infraestrutura subjacente. Para mitigar esta situação, recomendamos que use uma biblioteca de cliente que suporte pools de ligações e a religação automática.

    Limites de ligação

    Cada instância do App Engine em execução num ambiente padrão não pode ter mais de 100 ligações simultâneas a uma instância. Para apps PHP 5.5, o limite é de 60 ligações simultâneas. Este limite aplica-se por instância da aplicação. Isto significa que cada instância da aplicação do App Engine pode ter esse número de ligações à base de dados e, à medida que é dimensionada, o número total de ligações por implementação pode aumentar. Para mais informações, consulte o artigo Dimensionar elementos.

    Pode limitar o número máximo de ligações usadas por instância através de um conjunto de ligações. Para ver exemplos mais detalhados sobre como limitar o número de associações, consulte a página Gerir associações de bases de dados.

    As aplicações do App Engine estão sujeitas a limites de tempo de solicitação, consoante a utilização e o ambiente. Para mais informações, veja como as instâncias são geridas no ambiente padrão do App Engine padrão e flexível.

    Limites de quota da API

    O App Engine fornece um mecanismo que se liga através do proxy Auth do Cloud SQL, que usa a API Admin do Cloud SQL. Os limites de quota da API aplicam-se ao proxy Auth do Cloud SQL. Quando a API Cloud SQL Admin é iniciada, usa uma quota de dois e, posteriormente, uma média de dois por hora. A quota predefinida é de 180 por minuto por utilizador. As aplicações do App Engine também estão sujeitas a quotas e limites adicionais, conforme abordado na página Quotas do App Engine.