Cómo conectarse desde funciones de Cloud Run

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 funciones de Cloud Run.

Para obtener instrucciones paso a paso sobre cómo ejecutar una aplicación web de muestra de funciones de Cloud Run conectada a Cloud SQL, consulta la Guía de inicio rápido para conectarte desde funciones de Cloud Run.

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.

Funciones de Cloud Run es una solución de procesamiento ligera que permite a los desarrolladores crear funciones individuales y de un solo propósito que respondan a eventos de Cloud sin necesidad de administrar un servidor o entorno de ejecución.

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 funciones de Cloud Run

Los pasos para configurar funciones de Cloud Run dependen del tipo de dirección IP que hayas asignado a la instancia de Cloud SQL.

IP pública (predeterminada)

Para configurar funciones de Cloud Run a fin de habilitar conexiones a una instancia de Cloud SQL, haz lo siguiente:

  • Confirma que la instancia que se creó antes tenga una dirección IP pública. Puedes confirmarlo en la página Descripción general de la instancia en la consola de Google Cloud. Si necesitas agregar una dirección IP pública, consulta Configura IP públicas.
  • Obtén el INSTANCE_CONNECTION_NAME de la instancia. Este valor está disponible:
    • En la página Descripción general de la instancia, en la consola de Google Cloud.
    • Ejecuta el siguiente comando: gcloud sql instances describe [INSTANCE_NAME]
  • Configura la cuenta de servicio de tu función. Si la cuenta de servicio de autorización pertenece a un proyecto distinto al de la instancia de Cloud SQL, habilita la API de Cloud SQL Admin y agrega en ambos proyectos los permisos de IAM mencionados a continuación. Confirma que la cuenta de servicio tenga los roles y los permisos de Cloud SQL adecuados para conectarse a Cloud SQL.
    • Para conectarte a Cloud SQL, la cuenta de servicio necesita uno de los siguientes roles de IAM:
      • Cloud SQL Client (recomendado)
      • Cloud SQL Editor
      • Cloud SQL Admin
      O bien, puedes asignar los siguientes permisos de IAM de forma manual:
      • cloudsql.instances.connect
      • cloudsql.instances.get
  • Si usas funciones de Cloud Run y funciones que no son de Cloud Run (1ª gen.), se requiere lo siguiente (también consulta Configura Cloud Run):
    1. Implementa inicialmente tu función.
      Cuando comienzas a crear una función de Cloud Run en la consola de Google Cloud, aún no se crea el servicio subyacente de Cloud Run. No puedes configurar una conexión de Cloud SQL hasta que se cree ese servicio (mediante la implementación de la función de Cloud Run).
    2. En la consola de Google Cloud, en la parte superior derecha de la página Detalles de la función, en Con tecnología de Cloud Run, haz clic en el vínculo para acceder al servicio de Cloud Run subyacente.
    3. En la página Detalles del servicio de Cloud Run, selecciona la pestaña Editar e implementar nueva revisión.
    4. Sigue los pasos estándar (como en el caso de cualquier cambio de configuración) a fin de establecer una configuración nueva para una conexión de Cloud SQL.
      Crea una revisión de Cloud Run nueva, y las revisiones posteriores reciben esta conexión de Cloud SQL de forma automática, a menos que la cambies de forma explícita.

IP privada

Si la cuenta de servicio de autorización pertenece a un proyecto distinto al que contiene la instancia de Cloud SQL, haz lo siguiente:

  • En ambos proyectos, habilita la API de Cloud SQL Admin.
  • Para la cuenta de servicio del proyecto que contiene la instancia de Cloud SQL, agrega los permisos de IAM.
Un conector de acceso a VPC sin servidores usa direcciones IP privadas para controlar la comunicación con tu red de VPC. Para conectarte directamente con direcciones IP privadas, debes hacer lo siguiente:
  1. Asegúrate de que la instancia de Cloud SQL que creaste antes tenga una dirección IP privada. Si necesitas agregar una, consulta Configura IP privadas para obtener instrucciones.
  2. Crea un conector de Acceso a VPC sin servidores en la misma red de VPC en la que se encuentra tu instancia de Cloud SQL. Ten en cuenta las siguientes condiciones:
    • A menos que uses la VPC compartida, tu conector debe estar en el mismo proyecto y región que el recurso que lo usa, pero puede enviar tráfico a recursos en diferentes regiones.
    • El acceso a VPC sin servidores admite la comunicación con las redes de VPC conectadas con Cloud VPN y el intercambio de tráfico entre redes de VPC.
    • El Acceso a VPC sin servidores no es compatible con las redes heredadas.
  3. Configura funciones de Cloud Run para usar el conector.
  4. Conéctate mediante la dirección IP privada y el puerto 3306 de tu instancia.

Conectar a Cloud SQL

Después de configurar las funciones de Cloud Run, puedes conectarte a la instancia de Cloud SQL.

IP pública (predeterminada)

En las rutas de IP públicas, las funciones de Cloud Run proporcionan encriptación y se conectan mediante el proxy de autenticación de Cloud SQL de las siguientes dos maneras:

IP privada

En el caso de las rutas de IP privadas, tu aplicación se conecta directamente a tu instancia a través de una red de VPC. En este método, se usa TCP para conectarse directamente a la instancia de Cloud SQL sin usar el proxy de autenticación de Cloud SQL.

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
}

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

Las conexiones a bases de datos subyacentes pueden caerse debido al propio servidor de la base de datos o a la infraestructura subyacente a las funciones de Cloud Run. Te recomendamos usar una biblioteca cliente que admita grupos de conexiones que vuelvan a establecer de forma automática las conexiones de clientes que no funcionan. Además, te recomendamos que uses un grupo de conexiones de alcance global, ya que esto aumenta la probabilidad de que la función vuelva a usar la misma conexión para las invocaciones posteriores de la función y cierre la conexión de forma natural cuando se expulse la instancia (reducción del ajuste de escala automático). Para obtener ejemplos más detallados sobre cómo usar los grupos de conexiones, consulta la sección sobre cómo administrar conexiones de bases de datos.

Límites de conexión

Cloud SQL impone un límite máximo en las conexiones simultáneas. Estos límites pueden variar según el motor de base de datos elegido (consulta Cuotas y límites). Se recomienda usar una conexión con funciones de Cloud Eun, pero es importante establecer la cantidad máxima de conexiones en 1.

Siempre que sea posible, asegúrate de solo inicializar un grupo de conexiones para las funciones que necesiten acceder a la base de datos. Algunos grupos de conexiones crearán conexiones de forma interrumpible, que pueden consumir recursos excesivos y se tienen en cuenta para los límites de conexión. Por esta razón, se recomienda usar la inicialización diferida a fin de retrasar la creación de un grupo de conexiones hasta que sea necesaria y solo incluir el grupo de conexiones en las funciones en las que se usa.

Para obtener ejemplos más detallados sobre cómo limitar la cantidad de conexiones, consulta Administra conexiones de bases de datos.

Límites de cuota de la API

Las funciones de Cloud Run proporcionan un mecanismo que se conecta mediante el proxy de Auth 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. La cuota de la API de Cloud SQL Admin que se usa es aproximadamente el doble de la cantidad de instancias de Cloud SQL configuradas por la cantidad total de funciones implementadas. Puedes establecer la cantidad de invocaciones simultáneas máximas para modificar la cuota de API esperada que se consume. Las funciones de Cloud Run también imponen límites de frecuencia en la cantidad de llamadas a la API que se permiten cada 100 segundos.

¿Qué sigue?