Connexion depuis Cloud Run

Cette page contient des informations et des exemples de connexion à une instance Cloud SQL à partir d'un service s'exécutant dans Cloud Run.

Pour obtenir des instructions détaillées sur l'exécution d'un exemple d'application Web Cloud Run connectée à Cloud SQL, consultezDémarrage rapide pour la connexion depuis Cloud Run.

Cloud SQL est un service de base de données entièrement géré qui vous aide à configurer, maintenir, gérer et administrer vos bases de données relationnelles dans le cloud.

Cloud Run est une plate-forme de calcul gérée qui vous permet d'exécuter des conteneurs directement sur l'infrastructure Google Cloud .

Configurer une instance Cloud SQL

  1. Activez l'API Cloud SQL Admin dans le projet Google Cloud à partir duquel vous vous connectez, si ce n'est pas déjà fait :

    Enable the API

  2. Créez une instance Cloud SQL pour PostgreSQL. Nous vous recommandons de choisir un emplacement d'instance Cloud SQL qui soit dans la même région que votre service Cloud Run, afin d'améliorer la latence, d'éviter certains coûts de mise en réseau et de réduire les risques de défaillance interrégionale.

    Par défaut, Cloud SQL attribue une adresse IP publique à une nouvelle instance. Vous avez également la possibilité d'attribuer une adresse IP privée. Pour en savoir plus sur les options de connectivité disponibles pour ces deux types d'adresses IP, consultez la page Présentation du processus de connexion.

Configurer Cloud Run

La procédure à suivre pour configurer Cloud Run dépend du type d'adresse IP que vous avez attribué à votre instance Cloud SQL. Si vous acheminez tout le trafic de sortie via la sortie VPC directe ou un connecteur d'accès au VPC sans serveur, utilisez une adresse IP privée. Comparez les deux méthodes de sortie réseau.

  • Assurez-vous que l'instance dispose d'une adresse IP publique. Vous pouvez le vérifier sur la page Présentation de votre instance dans Google Cloud Console. Si vous devez en ajouter une, consultez la page Configurer une adresse IP publique pour obtenir des instructions.
  • Obtenez le INSTANCE_CONNECTION_NAME pour votre instance. Vous trouverez cette valeur sur la page Présentation de votre instance dans la console Google Cloud ou en exécutant la commande gcloud sql instances describe suivante:
    gcloud sql instances describe INSTANCE_NAME
       
    Remplacez INSTANCE_NAME par le nom de votre instance Cloud SQL.
  • Obtenez l'CLOUD_RUN_SERVICE_ACCOUNT_NAME de votre service Cloud Run. Vous pouvez trouver cette valeur sur la page IAM du projet qui héberge le service Cloud Run dans la console Google Cloud ou en exécutant la commande gcloud run services describe suivante dans le projet qui héberge le service Cloud Run:
    gcloud run services describe CLOUD_RUN_SERVICE_NAME
    --region CLOUD_RUN_SERVICE_REGION --format="value(spec.template.spec.serviceAccountName)"
       
    Remplacez les variables suivantes :
    • CLOUD_RUN_SERVICE_NAME: nom de votre service Cloud Run
    • CLOUD_RUN_SERVICE_REGION: région de votre service Cloud Run
  • Configurez le compte de service pour votre service Cloud Run. Pour se connecter à Cloud SQL, assurez-vous que le compte de service dispose du rôle IAM Cloud SQL Client.
  • Si vous ajoutez une connexion Cloud SQL à un nouveau service, vous devez le conteneuriser et l'importer dans Container Registry ou Artifact Registry. Si vous ne disposez pas encore d'une connexion, suivez ces instructions pour en créer et en déployer une.

Comme pour toute modification de configuration, la définition d'une nouvelle configuration pour la connexion Cloud SQL entraîne la création d'une révision Cloud Run. Les révisions ultérieures hériteront automatiquement de cette connexion Cloud SQL, à moins que vous ne la mettiez explicitement à jour.

  1. Accédez à Cloud Run

  2. Commencez à configurer le service. Pour ajouter des connexions Cloud SQL à un service existant, procédez comme suit:

    1. Dans la liste Services, cliquez sur le nom du service souhaité.
    2. Cliquez sur MODIFIER ET DÉPLOYER LA NOUVELLE RÉVISION.
  3. Activez la connexion à une instance Cloud SQL  
    1. Cliquez sur Conteneurs, puis sur Paramètres.
    2. Faites défiler la page jusqu'à Connexions Cloud SQL.
    3. Cliquez sur Ajouter une connexion.
    4. Cliquez sur le bouton Activer Cloud SQL Admin si vous n'avez pas encore activé l'API Cloud SQL Admin.

    Ajouter une connexion Cloud SQL

    • Si vous ajoutez une connexion à une instance Cloud SQL dans votre projet, sélectionnez l'instance Cloud SQL souhaitée dans le menu.
    • Si vous utilisez une instance Cloud SQL issue d'un autre projet, sélectionnez custom connection string (chaîne de connexion personnalisée) dans le menu, puis saisissez le nom de connexion complet de l'instance au format PROJECT-ID:REGION:INSTANCE-ID.
    • Pour supprimer une connexion, maintenez le curseur à droite de la connexion pour afficher l'icône Supprimer, puis cliquez dessus.
  4. Cliquez sur Créer ou Déployer.

Avant d'utiliser l'une des commandes suivantes, effectuez les remplacements suivants:

  • IMAGE par l'image que vous déployez.
  • SERVICE_NAME par le nom de votre service Cloud Run
  • INSTANCE_CONNECTION_NAME par le nom de connexion de l'instance Cloud SQL ou par une liste de noms de connexion séparés par une virgule

    Si vous déployez un nouveau conteneur, exécutez la commande suivante:

    gcloud run deploy \
      --image=IMAGE \
      --add-cloudsql-instances=INSTANCE_CONNECTION_NAME
    Si vous mettez à jour un service existant, exécutez la commande suivante:
    gcloud run services update SERVICE_NAME \
      --add-cloudsql-instances=INSTANCE_CONNECTION_NAME

Le code suivant crée un conteneur Cloud Run de base avec une instance Cloud SQL connectée.

resource "google_cloud_run_v2_service" "default" {
  name     = "cloudrun-service"
  location = "us-central1"

  deletion_protection = false # set to "true" in production

  template {
    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello:latest" # Image to deploy

      volume_mounts {
        name       = "cloudsql"
        mount_path = "/cloudsql"
      }
    }
    volumes {
      name = "cloudsql"
      cloud_sql_instance {
        instances = [google_sql_database_instance.default.connection_name]
      }
    }
  }
  client     = "terraform"
  depends_on = [google_project_service.secretmanager_api, google_project_service.cloudrun_api, google_project_service.sqladmin_api]
}

  1. Appliquez les modifications en saisissant terraform apply.
  2. Vérifiez les modifications en consultant le service Cloud Run, en cliquant sur l'onglet Révisions, puis sur l'onglet Connexions.

Si le compte de service d'autorisation appartient à un projet différent de celui contenant l'instance Cloud SQL, procédez comme suit :

  • Dans les deux projets, activez l'API Cloud SQL Admin.
  • Pour le compte de service du projet contenant l'instance Cloud SQL, ajoutez les autorisations IAM.
La sortie et les connecteurs VPC directs utilisent des adresses IP privées pour gérer la communication avec votre réseau VPC. Pour vous connecter directement à des adresses IP privées à l'aide de l'une de ces méthodes de sortie, procédez comme suit :
  1. Assurez-vous que l'instance Cloud SQL créée précédemment possède une adresse IP privée. Pour ajouter une adresse IP interne, consultez la page Configurer une adresse IP privée.
  2. Configurez votre méthode de sortie pour vous connecter au même réseau VPC que votre instance Cloud SQL. Veuillez noter les conditions suivantes :
    • La sortie VPC directe et l'accès au VPC sans serveur acceptent la communication avec les réseaux VPC connectés à l'aide de Cloud VPN et de l'appairage de réseaux VPC.
    • La sortie VPC directe et l'accès au VPC sans serveur ne sont pas compatibles avec les anciens réseaux.
    • À moins que vous n'utilisiez le VPC partagé, un connecteur doit se trouver dans le même projet et la même région que la ressource qui l'utilise, mais il peut envoyer du trafic à des ressources se trouvant dans d'autres régions.
  3. Connectez-vous avec l'adresse IP privée et le port 5432 de votre instance.

Se connecter à Cloud SQL

Une fois que vous avez configuré Cloud Run, vous pouvez vous connecter à votre instance Cloud SQL.

Pour les chemins d'accès d'adresses IP publiques, Cloud Run assure le chiffrement et se connecte à l'aide du proxy d'authentification Cloud SQL de deux manières :

Se connecter avec des sockets Unix

Une fois votre service correctement configuré, vous pouvez le connecter au socket de domaine Unix de votre instance Cloud SQL accessible via le système de fichiers de l'environnement au chemin d'accès suivant : /cloudsql/INSTANCE_CONNECTION_NAME.

INSTANCE_CONNECTION_NAME utilise le format project:region:instance-id. Pour le trouver, accédez à la page Présentation de votre instance dans la console Google Cloud ou exécutez la commande suivante :

gcloud sql instances describe [INSTANCE_NAME]

Ces connexions sont automatiquement chiffrées sans configuration supplémentaire.

Les exemples de code présentés ci-dessous sont extraits d'exemples plus complets du site GitHub. Cliquez sur View on GitHub (Afficher sur GitHub) pour plus d'informations.

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

import os

import sqlalchemy


def connect_unix_socket() -> sqlalchemy.engine.base.Engine:
    """Initializes a Unix socket connection pool for a Cloud SQL instance of Postgres."""
    # 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_user = os.environ["DB_USER"]  # e.g. 'my-database-user'
    db_pass = os.environ["DB_PASS"]  # e.g. 'my-database-password'
    db_name = os.environ["DB_NAME"]  # e.g. 'my-database'
    unix_socket_path = os.environ[
        "INSTANCE_UNIX_SOCKET"
    ]  # e.g. '/cloudsql/project:region:instance'

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # postgresql+pg8000://<db_user>:<db_pass>@/<db_name>
        #                         ?unix_sock=<INSTANCE_UNIX_SOCKET>/.s.PGSQL.5432
        # Note: Some drivers require the `unix_sock` query parameter to use a different key.
        # For example, 'psycopg2' uses the path set to `host` in order to connect successfully.
        sqlalchemy.engine.url.URL.create(
            drivername="postgresql+pg8000",
            username=db_user,
            password=db_pass,
            database=db_name,
            query={"unix_sock": f"{unix_socket_path}/.s.PGSQL.5432"},
        ),
        # ...
    )
    return pool

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

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 INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET");
  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 URL is equivalent to setting the config options below:
    // jdbc:postgresql:///<DB_NAME>?cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&
    // socketFactory=com.google.cloud.sql.postgres.SocketFactory&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:postgresql:///%s", DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", _postgres"
    config.setPassword(DB_PASS); // e.g. "my-password"

    config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
    config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME);

    // Unix sockets are not natively supported in Java, so it is necessary to use the Cloud SQL
    // Java Connector to connect. When setting INSTANCE_UNIX_SOCKET, the connector will 
    // call an external package that will enable Unix socket connections.
    // Note: For Java users, the Cloud SQL Java Connector can provide authenticated connections
    // which is usually preferable to using the Cloud SQL Proxy with Unix sockets.
    // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details.
    if (INSTANCE_UNIX_SOCKET != null) {
      config.addDataSourceProperty("unixSocketPath", INSTANCE_UNIX_SOCKET);
    }


    // cloudSqlRefreshStrategy set to "lazy" is used to perform a
    // refresh when needed, rather than on a scheduled interval.
    // This is recommended for serverless environments to
    // avoid background refreshes from throttling CPU.
    config.addDataSourceProperty("cloudSqlRefreshStrategy", "lazy");

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

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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

const Knex = require('knex');

// createUnixSocketPool initializes a Unix socket connection pool for
// a Cloud SQL instance of Postgres.
const createUnixSocketPool = 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.
  return Knex({
    client: 'pg',
    connection: {
      user: process.env.DB_USER, // e.g. 'my-user'
      password: process.env.DB_PASS, // e.g. 'my-user-password'
      database: process.env.DB_NAME, // e.g. 'my-database'
      host: process.env.INSTANCE_UNIX_SOCKET, // e.g. '/cloudsql/project:region:instance'
    },
    // ... Specify additional properties here.
    ...config,
  });
};

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

using Npgsql;
using System;

namespace CloudSql
{
    public class PostgreSqlUnix
    {
        public static NpgsqlConnectionStringBuilder NewPostgreSqlUnixSocketConnectionString()
        {
            // Equivalent connection string:
            // "Server=<INSTANCE_UNIX_SOCKET>;Uid=<DB_USER>;Pwd=<DB_PASS>;Database=<DB_NAME>"
            var connectionString = new NpgsqlConnectionStringBuilder()
            {
                // The Cloud SQL proxy provides encryption between the proxy and instance.
                SslMode = SslMode.Disable,

                // 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.
                Host = Environment.GetEnvironmentVariable("INSTANCE_UNIX_SOCKET"), // e.g. '/cloudsql/project:region:instance'
                Username = 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'
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            return connectionString;
        }
    }
}

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

package cloudsql

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	// Note: If connecting using the App Engine Flex Go runtime, use
	// "github.com/jackc/pgx/stdlib" instead, since v5 requires
	// Go modules which are not supported by App Engine Flex.
	_ "github.com/jackc/pgx/v5/stdlib"
)

// connectUnixSocket initializes a Unix socket connection pool for
// a Cloud SQL instance of Postgres.
func connectUnixSocket() (*sql.DB, error) {
	mustGetenv := func(k string) string {
		v := os.Getenv(k)
		if v == "" {
			log.Fatalf("Fatal Error in connect_unix.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'
		unixSocketPath = mustGetenv("INSTANCE_UNIX_SOCKET") // e.g. '/cloudsql/project:region:instance'
		dbName         = mustGetenv("DB_NAME")              // e.g. 'my-database'
	)

	dbURI := fmt.Sprintf("user=%s password=%s database=%s host=%s",
		dbUser, dbPwd, dbName, unixSocketPath)

	// dbPool is the pool of database connections.
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}

	// ...

	return dbPool, nil
}

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

unix: &unix
  adapter: postgresql
  # 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" } %>
  # Specify the Unix socket path as host
  host: "<%= ENV["INSTANCE_UNIX_SOCKET"] %>"

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

namespace Google\Cloud\Samples\CloudSQL\Postgres;

use PDO;
use PDOException;
use RuntimeException;
use TypeError;

class DatabaseUnix
{
    public static function initUnixDatabaseConnection(): 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'
            $instanceUnixSocket = getenv('INSTANCE_UNIX_SOCKET'); // e.g. '/cloudsql/project:region:instance'

            // Connect using UNIX sockets
            $dsn = sprintf(
                'pgsql:dbname=%s;host=%s',
                $dbName,
                $instanceUnixSocket
            );

            // 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 $instanceUnixSocket (for UNIX socket mode). ' .
                        'The PHP error was %s',
                    $e->getMessage()
                ),
                (int) $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/postgres/connect-external-app',
                    $e->getMessage()
                ),
                (int) $e->getCode(),
                $e
            );
        }

        return $conn;
    }
}

Se connecter avec des connecteurs Cloud SQL

Les connecteurs Cloud SQL sont des bibliothèques propres aux langages de programmation qui fournissent le chiffrement et l'autorisation basée sur IAM lors de la connexion à une instance Cloud SQL.

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

import os

from google.cloud.sql.connector import Connector, IPTypes
import pg8000

import sqlalchemy


def connect_with_connector() -> sqlalchemy.engine.base.Engine:
    """
    Initializes a connection pool for a Cloud SQL instance of Postgres.

    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["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

    # initialize Cloud SQL Python Connector object
    connector = Connector(refresh_strategy="LAZY")

    def getconn() -> pg8000.dbapi.Connection:
        conn: pg8000.dbapi.Connection = connector.connect(
            instance_connection_name,
            "pg8000",
            user=db_user,
            password=db_pass,
            db=db_name,
            ip_type=ip_type,
        )
        return conn

    # The Cloud SQL Python Connector can be used with SQLAlchemy
    # using the 'creator' argument to 'create_engine'
    pool = sqlalchemy.create_engine(
        "postgresql+pg8000://",
        creator=getconn,
        # ...
    )
    return pool

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

Remarque :

  • CLOUD_SQL_CONNECTION_NAME doit être représenté sous la forme <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>.
  • Pour en savoir plus sur les exigences de version Socket Factory des sockets JDBC pour le fichier pom.xml, cliquez ici.

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 INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET");
  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 URL is equivalent to setting the config options below:
    // jdbc:postgresql:///<DB_NAME>?cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&
    // socketFactory=com.google.cloud.sql.postgres.SocketFactory&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:postgresql:///%s", DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", _postgres"
    config.setPassword(DB_PASS); // e.g. "my-password"

    config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
    config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME);


    // The ipTypes argument can be used to specify a comma delimited list of preferred IP types
    // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the
    // SocketFactory to connect with an instance's associated private IP.
    config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE");

    // cloudSqlRefreshStrategy set to "lazy" is used to perform a
    // refresh when needed, rather than on a scheduled interval.
    // This is recommended for serverless environments to
    // avoid background refreshes from throttling CPU.
    config.addDataSourceProperty("cloudSqlRefreshStrategy", "lazy");

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

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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

package cloudsql

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net"
	"os"

	"cloud.google.com/go/cloudsqlconn"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/stdlib"
)

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 passwords and other 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")
	)

	dsn := fmt.Sprintf("user=%s password=%s database=%s", dbUser, dbPwd, dbName)
	config, err := pgx.ParseConfig(dsn)
	if err != nil {
		return nil, err
	}
	var opts []cloudsqlconn.Option
	if usePrivate != "" {
		opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPrivateIP()))
	}
	// WithLazyRefresh() Option is used to perform refresh
	// when needed, rather than on a scheduled interval.
	// This is recommended for serverless environments to
	// avoid background refreshes from throttling CPU.
	opts = append(opts, cloudsqlconn.WithLazyRefresh())
	d, err := cloudsqlconn.NewDialer(context.Background(), opts...)
	if err != nil {
		return nil, err
	}
	// Use the Cloud SQL connector to handle connecting to the instance.
	// This approach does *NOT* require the Cloud SQL proxy.
	config.DialFunc = func(ctx context.Context, network, instance string) (net.Conn, error) {
		return d.Dial(ctx, instanceConnectionName)
	}
	dbURI := stdlib.RegisterConnConfig(config)
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}
	return dbPool, nil
}

Utiliser Secret Manager

Nous vous recommandons d'utiliser Secret Manager pour stocker les informations sensibles telles que les identifiants SQL. Vous pouvez transmettre des secrets en tant que variables d'environnement ou les installer en tant que volume avec Cloud Run.

Après avoir créé un secret dans Secret Manager, mettez à jour un service existant à l'aide de la commande suivante :

gcloud run services update SERVICE_NAME \
  --add-cloudsql-instances=INSTANCE_CONNECTION_NAME
  --update-env-vars=INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME_SECRET \
  --update-secrets=DB_USER=DB_USER_SECRET:latest \
  --update-secrets=DB_PASS=DB_PASS_SECRET:latest \
  --update-secrets=DB_NAME=DB_NAME_SECRET:latest

La commande suivante crée des ressources de type secret pour conserver en toute sécurité les valeurs d'utilisateur, de mot de passe et de nom de la base de données à l'aide de google_secret_manager_secret et google_secret_manager_secret_version. Notez que vous devez mettre à jour le compte de service Compute du projet pour avoir accès à chaque secret.


# Create dbuser secret
resource "google_secret_manager_secret" "dbuser" {
  secret_id = "dbusersecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbuser secret
resource "google_secret_manager_secret_version" "dbuser_data" {
  secret      = google_secret_manager_secret.dbuser.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbuser secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbuser" {
  secret_id = google_secret_manager_secret.dbuser.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" # Project's compute service account
}


# Create dbpass secret
resource "google_secret_manager_secret" "dbpass" {
  secret_id = "dbpasssecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbpass secret
resource "google_secret_manager_secret_version" "dbpass_data" {
  secret      = google_secret_manager_secret.dbpass.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbpass secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbpass" {
  secret_id = google_secret_manager_secret.dbpass.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" # Project's compute service account
}


# Create dbname secret
resource "google_secret_manager_secret" "dbname" {
  secret_id = "dbnamesecret"
  replication {
    auto {}
  }
  depends_on = [google_project_service.secretmanager_api]
}

# Attaches secret data for dbname secret
resource "google_secret_manager_secret_version" "dbname_data" {
  secret      = google_secret_manager_secret.dbname.id
  secret_data = "secret-data" # Stores secret as a plain txt in state
}

# Update service account for dbname secret
resource "google_secret_manager_secret_iam_member" "secretaccess_compute_dbname" {
  secret_id = google_secret_manager_secret.dbname.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" # Project's compute service account
}

Mettez à jour la ressource Cloud Run principale pour inclure les nouveaux secrets.

resource "google_cloud_run_v2_service" "default" {
  name     = "cloudrun-service"
  location = "us-central1"

  deletion_protection = false # set to "true" in production

  template {
    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello:latest" # Image to deploy

      # Sets a environment variable for instance connection name
      env {
        name  = "INSTANCE_CONNECTION_NAME"
        value = google_sql_database_instance.default.connection_name
      }
      # Sets a secret environment variable for database user secret
      env {
        name = "DB_USER"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbuser.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }
      # Sets a secret environment variable for database password secret
      env {
        name = "DB_PASS"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbpass.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }
      # Sets a secret environment variable for database name secret
      env {
        name = "DB_NAME"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.dbname.secret_id # secret name
            version = "latest"                                      # secret version number or 'latest'
          }
        }
      }

      volume_mounts {
        name       = "cloudsql"
        mount_path = "/cloudsql"
      }
    }
    volumes {
      name = "cloudsql"
      cloud_sql_instance {
        instances = [google_sql_database_instance.default.connection_name]
      }
    }
  }
  client     = "terraform"
  depends_on = [google_project_service.secretmanager_api, google_project_service.cloudrun_api, google_project_service.sqladmin_api]
}

Appliquez les modifications en saisissant terraform apply.

L'exemple de commande utilise la version du secret latest. Google recommande toutefois d'épingler le secret à une version spécifique, SECRET_NAME:v1.

Pour les chemins d'accès des adresses IP privées, votre application se connecte directement à votre instance via un réseau VPC. Cette méthode utilise TCP pour se connecter directement à l'instance Cloud SQL sans utiliser le proxy d'authentification Cloud SQL.

Se connecter avec TCP

Connectez-vous en utilisant l'adresse IP privée de votre instance Cloud SQL en tant qu'hôte et le port 5432.

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

import os
import ssl

import sqlalchemy


def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
    """Initializes a TCP connection pool for a Cloud SQL instance of Postgres."""
    # 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. 5432

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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

Remarque :

  • CLOUD_SQL_CONNECTION_NAME doit être représenté sous la forme <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>.
  • L'utilisation de l'argument ipTypes=PRIVATE force la connexion de SocketFactory à l'adresse IP privée associée à une instance.
  • Pour en savoir plus sur les exigences de version Socket Factory des sockets JDBC pour le fichier pom.xml, cliquez ici.


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();

    // The following URL is equivalent to setting the config options below:
    // jdbc:postgresql://<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:postgresql://%s:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "postgres"
    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);
  }
}

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

const Knex = require('knex');
const fs = require('fs');

// createTcpPool initializes a TCP connection pool for a Cloud SQL
// instance of Postgres.
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 = {
    client: 'pg',
    connection: {
      host: process.env.INSTANCE_HOST, // e.g. '127.0.0.1'
      port: process.env.DB_PORT, // e.g. '5432'
      user: process.env.DB_USER, // e.g. 'my-user'
      password: process.env.DB_PASS, // e.g. 'my-user-password'
      database: process.env.DB_NAME, // e.g. 'my-database'
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return Knex(dbConfig);
};

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

package cloudsql

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	// Note: If connecting using the App Engine Flex Go runtime, use
	// "github.com/jackc/pgx/stdlib" instead, since v5 requires
	// Go modules which are not supported by App Engine Flex.
	_ "github.com/jackc/pgx/v5/stdlib"
)

// connectTCPSocket initializes a TCP connection pool for a Cloud SQL
// instance of Postgres.
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'
		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. '5432'
		dbName    = mustGetenv("DB_NAME")       // e.g. 'my-database'
	)

	dbURI := fmt.Sprintf("host=%s user=%s password=%s port=%s database=%s",
		dbTCPHost, dbUser, dbPwd, dbPort, dbName)


	// dbPool is the pool of database connections.
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}

	// ...

	return dbPool, nil
}

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

using Npgsql;
using System;

namespace CloudSql
{
    public class PostgreSqlTcp
    {
        public static NpgsqlConnectionStringBuilder NewPostgreSqlTCPConnectionString()
        {
            // Equivalent connection string:
            // "Uid=<DB_USER>;Pwd=<DB_PASS>;Host=<INSTANCE_HOST>;Database=<DB_NAME>;"
            var connectionString = new NpgsqlConnectionStringBuilder()
            {
                // 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.
                Host = Environment.GetEnvironmentVariable("INSTANCE_HOST"),     // e.g. '127.0.0.1'
                // Set Host to 'cloudsql' when deploying to App Engine Flexible environment
                Username = 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 = SslMode.Disable,
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            return connectionString;
        }
    }
}

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

tcp: &tcp
  adapter: postgresql
  # 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") { 5432 }%>

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur GitHub.

namespace Google\Cloud\Samples\CloudSQL\Postgres;

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

        return $conn;
    }
}

Bonnes pratiques et autres informations

Vous pouvez utiliser le proxy d'authentification Cloud SQL lorsque vous testez votre application en local. Pour obtenir des instructions détaillées, consultez le guide de démarrage rapide pour l'utilisation du proxy d'authentification Cloud SQL.

Vous pouvez également effectuer des tests à l'aide du proxy Cloud SQL via un conteneur Docker.

Pools de connexions

Les connexions aux bases de données sous-jacentes peuvent être supprimées, soit par le serveur de base de données lui-même, soit par l'infrastructure de la plate-forme. Nous vous recommandons d'utiliser une bibliothèque cliente compatible avec les pools de connexions qui restaurent automatiquement les connexions client interrompues. Pour obtenir des exemples plus détaillés sur l'utilisation des pools de connexions, consultez la page Gérer les connexions à la base de données.

Limites de connexion

Les éditions MySQL et PostgreSQL de Cloud SQL imposent une limite maximale de connexions simultanées, laquelle peut varier en fonction du moteur de base de données choisi (consultez la page Quotas et limites de Cloud SQL).

Les instances de conteneur Cloud Run sont limitées à 100 connexions à une base de données Cloud SQL. Chaque instance d'une tâche ou d'un service Cloud Run peut avoir 100 connexions à la base de données, et le nombre total de connexions par déploiement peut augmenter à mesure que ce service ou cette tâche évolue.

Toutefois, vous avez la possibilité de limiter le nombre maximal de connexions par instance en utilisant un pool de connexions. Pour obtenir des exemples plus détaillés sur les techniques de limitation du nombre de connexions, consultez la page Gérer les connexions à la base de données.

Limites de quota d'API

Cloud Run fournit un mécanisme permettant de se connecter à l'aide du proxy d'authentification Cloud SQL, qui utilise l'API Cloud SQL Admin. Les limites de quota d'API s'appliquent au proxy d'authentification Cloud SQL. Le quota de l'API Cloud SQL Admin est utilisé à hauteur d'environ le double du nombre d'instances Cloud SQL configurées par le nombre d'instances Cloud Run d'un service particulier déployé à un moment donné. Vous pouvez plafonner ou augmenter le nombre d'instances Cloud Run pour modifier le quota d'API que vous prévoyez d'utiliser.

Étapes suivantes