Conéctate desde el entorno flexible de App Engine

En esta página, se muestran información y ejemplos para 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 completamente administrado que te ayuda a configurar, mantener, controlar y administrar tus bases de datos relacionales en la nube.

App Engine es una plataforma sin servidores completamente administrada para desarrollar y alojar aplicaciones web a gran escala. Puedes elegir entre varios lenguajes, bibliotecas y frameworks populares para desarrollar tu aplicación. Luego, deja que App Engine se encargue del aprovisionamiento de servidores y del escalamiento de las instancias de tu aplicación a pedido.

Configura una instancia de Cloud SQL

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

    Enable the API

  2. Crea una instancia de Cloud SQL para MySQL. Te recomendamos que elijas una ubicación de instancia de Cloud SQL en la misma región que tu servicio de Cloud Run para obtener una mejor latencia, evitar algunos costos de red y reducir los riesgos de fallas 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 Descripción general de conexión.

Configura el entorno flexible de App Engine

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

IP pública (predeterminada)

Sigue estos pasos si quieres configurar el entorno flexible de App Engine para habilitar conexiones a una instancia de Cloud SQL:

  1. Asegúrate de que la instancia creada anteriormente tenga una dirección IP pública. Puedes verificarlo en la página Descripción general de tu instancia en la consola de Google Cloud. Si necesitas agregar una, consulta la sección sobre cómo configurar la conectividad de IP pública para obtener instrucciones.
  2. Obtén el INSTANCE_CONNECTION_NAME de tu instancia. Puedes encontrarlo en la página Descripción general de la instancia en la consola de Google Cloud o mediante la ejecución del siguiente comando: gcloud sql instances describe [INSTANCE_NAME].
  3. Asegúrate de que la cuenta de servicio que usa tu app para autenticar llamadas a Cloud SQL tenga los roles y permisos de Cloud SQL adecuados.
    • La cuenta de servicio para tu servicio necesita una de las siguientes funciones de IAM:
      • Cloud SQL Client (recomendado)
      • Cloud SQL Editor
      • Cloud SQL Admin
      O bien puedes asignar manualmente los siguientes permisos de IAM:
      • cloudsql.instances.connect
      • cloudsql.instances.get
      Para obtener instrucciones detalladas sobre cómo agregar funciones de IAM a una cuenta de servicio, consulta la sección sobre cómo asignar funciones a cuentas de servicio.

    De forma predeterminada, tu app autorizará tus conexiones con 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 al de la instancia de Cloud SQL, se deberán agregar los permisos de IAM y la API de Cloud SQL Admin para ambos proyectos.

  4. Actualiza el archivo app.yaml del proyecto con la opción más adecuada. Puedes usar una lista de instancias separadas por comas para especificar varias opciones a la vez.

    Habilita un socket de dominio Unix

    Para habilitar un socket de dominio Unix, agrega una de las siguientes opciones al archivo app.yaml del proyecto, según desees conectarte a una o varias instancias:

    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME
    

    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME,INSTANCE_CONNECTION_NAME_2,...
    

    Habilita un puerto TCP

    Para habilitar un puerto TCP local, agrega una de las siguientes opciones al archivo app.yaml del proyecto, según desees conectarte 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 de 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 en Configuración de red si deseas obtener instrucciones a fin de especificar una red de VPC para tu implementación.

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

Conectar a Cloud SQL

Después de configurar el entorno flexible de App Engine, puedes conectarte a tu instancia de Cloud SQL.

IP pública (predeterminada)

En el caso de las rutas de IP públicas, el entorno flexible de App Engine proporciona encriptación y se conecta mediante el proxy de autenticación de Cloud SQL de tres maneras:

IP privada

Conéctate con TCP

Conéctate con la dirección IP privada de tu instancia de Cloud SQL como host y puerto 3306.

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 MySQL."""
    # 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. 3306

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # mysql+pymysql://<db_user>:<db_pass>@<db_host>:<db_port>/<db_name>
        sqlalchemy.engine.url.URL.create(
            drivername="mysql+pymysql",
            username=db_user,
            password=db_pass,
            host=db_host,
            port=db_port,
            database=db_name,
        ),
        # ...
    )
    return pool

Java

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en GitHub.

Nota:

  • INSTANCE_CONNECTION_NAME se debe representar como <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • Con el argumento ipTypes=PRIVATE, se forzará a SocketFactory a la conexión con la IP privada asociada de una instancia
  • Consulta los requisitos de la versión de fábrica de los sockets de JDBC para el archivo pom.xml aquí.


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

public class TcpConnectionPoolFactory extends ConnectionPoolFactory {

  // Saving credentials in environment variables is convenient, but not secure - consider a more
  // secure solution such as 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();

    // The following URL is equivalent to setting the config options below:
    // jdbc:mysql://<INSTANCE_HOST>:<DB_PORT>/<DB_NAME>?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:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "mysql"
    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 mysql = require('promise-mysql');
const fs = require('fs');

// createTcpPool initializes a TCP connection pool for a Cloud SQL
// instance of MySQL.
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 = {
    host: process.env.INSTANCE_HOST, // e.g. '127.0.0.1'
    port: process.env.DB_PORT, // e.g. '3306'
    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'
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return mysql.createPool(dbConfig);
};

Go

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en GitHub.

package cloudsql

import (
	"crypto/tls"
	"crypto/x509"
	"database/sql"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/go-sql-driver/mysql"
)

// connectTCPSocket initializes a TCP connection pool for a Cloud SQL
// instance of MySQL.
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.", 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'
		dbPort    = mustGetenv("DB_PORT")       // e.g. '3306'
		dbTCPHost = mustGetenv("INSTANCE_HOST") // e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
	)

	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: %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 MySql.Data.MySqlClient;
using System;

namespace CloudSql
{
    public class MySqlTcp
    {
        public static MySqlConnectionStringBuilder NewMysqlTCPConnectionString()
        {
            // Equivalent connection string:
            // "Uid=<DB_USER>;Pwd=<DB_PASS>;Host=<INSTANCE_HOST>;Database=<DB_NAME>;"
            var connectionString = new MySqlConnectionStringBuilder()
            {
                // 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.
                Server = 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'
                Database = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'

                // The Cloud SQL proxy provides encryption between the proxy and instance.
                SslMode = MySqlSslMode.Disabled,
            };
            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: mysql2
  # 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") { 3306 }%>

PHP

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en GitHub.

namespace Google\Cloud\Samples\CloudSQL\MySQL;

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('mysql:dbname=%s;host=%s', $dbName, $instanceHost);

            // 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/mysql/connect-external-app',
                    $e->getMessage()
                ),
                $e->getCode(),
                $e
            );
        }

        return $conn;
    }
}

Prácticas recomendadas y más información

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

Grupos de conexiones

Es posible que las conexiones a bases de datos subyacentes se interrumpan debido al mismo servidor de la base de datos o a la infraestructura subyacente. Para mitigar este problema, te recomendamos que uses una biblioteca cliente que admita grupos de conexiones y reconexiones automáticas.

Límites de conexión

Cada instancia de App Engine que se ejecuta en un entorno estándar no puede tener más de 100 conexiones simultáneas en una instancia. En el caso de las apps en PHP 5.5, el límite es de 60 conexiones simultáneas. Este límite se aplica por instancia de aplicación. Esto significa que cada instancia de la aplicación de App Engine puede tener esa cantidad de conexiones a la base de datos. A medida que aumenta la escala, puede aumentar el total de conexiones por implementación. Si quieres obtener más información, consulta la sección Elementos de escalamiento.

Puedes limitar la cantidad máxima de conexiones de cada instancia mediante un grupo de conexiones. Para obtener ejemplos más detallados sobre cómo limitar la cantidad de conexiones, consulta la página Administra conexiones de bases de datos.

Las aplicaciones de App Engine están sujetas a los límites de tiempo de las solicitudes según el uso y el entorno. Para obtener más información, consulta cómo se administran las instancias en entornos estándar y flexibles del entorno estándar 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 de Cloud SQL Admin. Los límites de cuota de la API se aplican al proxy de Cloud SQL Auth. Cuando se inicia la API de Cloud SQL Admin, usa una cuota de dos y un promedio de dos por hora después. La cuota predeterminada es de 180 por minuto, por usuario. Las aplicaciones de App Engine también están sujetas a las cuotas y los límites adicionales que se describen en la página Cuotas de App Engine.