Conectarse desde el entorno flexible de App Engine

En esta página se ofrece información y ejemplos sobre cómo conectarse a una instancia de Cloud SQL desde un servicio que se ejecuta en el entorno flexible de App Engine.

Cloud SQL es un servicio de base de datos totalmente gestionado que te permite configurar, mantener y gestionar tus bases de datos relacionales en la nube.

App Engine es una plataforma sin servidor totalmente gestionada para desarrollar y alojar aplicaciones web a gran escala. Puedes elegir entre varios lenguajes, bibliotecas y frameworks populares para desarrollar tus aplicaciones y, a continuación, dejar que App Engine se encargue de aprovisionar servidores y de escalar las instancias de tu aplicación en función de la demanda.

Configurar una instancia de Cloud SQL

  1. Habilita la API Admin de Cloud SQL en el Google Cloud proyecto desde el que te conectas, si aún no lo has hecho:

    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. Crea una instancia de Cloud SQL para SQL Server. Te recomendamos que elijas una ubicación de instancia de Cloud SQL en la misma región que tu servicio de Cloud Run para mejorar la latencia, evitar algunos costes de redes y reducir los riesgos de fallos entre regiones.

    De forma predeterminada, Cloud SQL asigna una dirección IP pública a una instancia nueva. También puedes asignar una dirección IP privada. Para obtener más información sobre las opciones de conectividad de ambos, consulta la página Información general sobre la conexión.

  3. Cuando crees la instancia, puedes elegir la jerarquía de certificados de servidor (CA) de la instancia y, a continuación, configurar la jerarquía como serverCaMode de la instancia. Debes seleccionar la opción de AC por instancia (GOOGLE_MANAGED_INTERNAL_CA) como modo de AC del servidor para las instancias a las que quieras conectarte desde aplicaciones web del entorno flexible de App Engine.

Configurar el entorno flexible de App Engine

Los pasos para configurar el entorno flexible de App Engine dependen del tipo de dirección IP que hayas asignado a tu instancia de Cloud SQL.

IP pública (predeterminada)

Para configurar el entorno flexible de App Engine de forma que se puedan establecer conexiones con una instancia de Cloud SQL, sigue estos pasos:

  1. Asegúrate de que la instancia tenga una dirección IP pública. Puedes comprobarlo en la página Resumen de tu instancia en la Google Cloud consola. Si necesitas añadir una, consulta las instrucciones en la página Configurar IP pública.
  2. Obtén el INSTANCE_CONNECTION_NAME de tu instancia. Puedes encontrar este valor en la página Resumen de tu instancia en la Google Cloud consola o ejecutando el siguiente comando:gcloud sql instances describe
    gcloud sql instances describe INSTANCE_NAME
       
    Sustituye INSTANCE_NAME por el nombre de tu instancia de Cloud SQL.
  3. Asegúrate de que la cuenta de servicio que usa tu aplicación para autenticar las llamadas a Cloud SQL tenga el Cloud SQL Client rol de gestión de identidades y accesos.

    Para obtener instrucciones detalladas sobre cómo añadir roles de gestión de identidades y accesos a una cuenta de servicio, consulta el artículo Conceder roles a cuentas de servicio.

De forma predeterminada, tu aplicación autorizará tus conexiones mediante la cuenta de servicio del entorno flexible de App Engine. La cuenta de servicio tiene el formato PROJECT_ID@appspot.gserviceaccount.com.

Si la cuenta de servicio de autorización pertenece a un proyecto distinto de la instancia de Cloud SQL, se deberán añadir los permisos de la API Cloud SQL Admin y de gestión de identidades y accesos a ambos proyectos.

  • Actualiza el archivo app.yaml de tu proyecto con la opción que mejor te venga. Puede usar una lista de instancias separada por comas para especificar varias opciones a la vez.

    Habilitar un puerto TCP

    Para habilitar un puerto TCP local, añade una de las siguientes opciones al archivo app.yaml de tu proyecto, en función de si te conectas a una o varias instancias:
    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 privada

    Para conectarte a tu instancia de Cloud SQL a través de una IP privada, tu implementación del entorno flexible de App Engine debe estar en la misma red de VPC que tu instancia de Cloud SQL. Consulta la documentación de configuración de Configuración de red para obtener instrucciones sobre cómo especificar una red de VPC para tu implementación.

    Una vez implementada, tu aplicación podrá conectarse directamente mediante la dirección IP privada y el puerto 1433 de tu instancia.

    Conectar a Cloud SQL

    Una vez que hayas configurado el entorno flexible de App Engine, podrás conectarte a tu instancia de Cloud SQL.

    IP pública (predeterminada)

    En las rutas de IP pública, el entorno flexible de App Engine proporciona cifrado y se conecta mediante el proxy de autenticación de Cloud SQL de dos formas:

    IP privada

    Conectarse con TCP

    Conéctate mediante la dirección IP privada de tu instancia de Cloud SQL como host y el puerto 1433.

    Python

    Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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 en el contexto de una aplicación web, consulta el archivo README en 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 en el contexto de una aplicación web, consulta el archivo README en 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 ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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 en el contexto de una aplicación web, consulta el archivo README en 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 en el contexto de una aplicación web, consulta el archivo README en 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 en el contexto de una aplicación web, consulta el archivo README en 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ácticas recomendadas y otra información

    Puedes usar el proxy de autenticación de Cloud SQL cuando pruebes tu aplicación de forma local. Consulta la guía de inicio rápido para usar el proxy de autenticación de Cloud SQL para obtener instrucciones detalladas.

    Grupos de conexión

    Es posible que se interrumpan las conexiones con las bases de datos subyacentes, ya sea por el propio servidor de la base de datos o por la infraestructura subyacente. Para mitigar este problema, te recomendamos que utilices una biblioteca de cliente que admita grupos de conexiones y la reconexión automática.

    Límites de conexiones

    Cada instancia de App Engine que se ejecute en entornos estándar no puede tener más de 100 conexiones simultáneas a una instancia. El límite en el caso de las aplicaciones escritas en PHP 5.5 es de 60 conexiones simultáneas. Este límite se aplica a cada instancia de la aplicación. Esto significa que cada instancia de la aplicación de App Engine puede tener ese número de conexiones a la base de datos. De esta forma, al aumentar el número de instancias, aumenta el número total de conexiones de cada despliegue. Para obtener más información, consulta Escalar elementos.

    Puedes limitar el número máximo de conexiones que se usan por instancia mediante un grupo de conexiones. Para ver ejemplos más detallados sobre cómo limitar el número de conexiones, consulta la página Gestión de conexiones de bases de datos.

    Las aplicaciones de App Engine están sujetas a límites de tiempo de solicitud que varían en función del uso y del entorno. Para obtener más información, consulta cómo se gestionan las instancias en entornos estándar y flexibles de App Engine.

    Límites de cuota de la API

    App Engine proporciona un mecanismo que se conecta mediante el proxy de autenticación de Cloud SQL, que usa la API Admin de Cloud SQL. Se aplican límites de cuota de API al proxy de autenticación de Cloud SQL. Cuando se inicia la API Admin de Cloud SQL, utiliza una cuota de dos y, después, una media de dos por hora. La cuota predeterminada es de 180 por minuto y usuario. Las aplicaciones de App Engine también están sujetas a otros límites y cuotas, tal como se explica en la página Cuotas de App Engine.