Se connecter depuis Cloud Build

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

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 Build est un service qui exécute vos compilations 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 MySQL. 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 Build

La procédure à suivre pour configurer Cloud Build dépend du type d'adresse IP que vous avez attribué à votre instance Cloud SQL.

Adresse IP publique (par défaut)

Assurez-vous que votre compte de service Cloud Build dispose des rôles et autorisations IAM requis pour se connecter à l'instance Cloud SQL. Le compte de service Cloud Build apparaît sur la page IAM de la console Google Cloud en tant que compte principal [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com.

Pour afficher ce compte de service dans la console Google Cloud, cochez la case Inclure les attributions de rôles fournies par Google.

Votre compte de service Cloud Build doit disposer de l'un des rôles IAM suivants :

  • Cloud SQL Client (rôle à privilégier)
  • Cloud SQL Admin
Vous pouvez également attribuer manuellement les autorisations IAM suivantes :
  • cloudsql.instances.connect
  • cloudsql.instances.get

Si le compte de service Cloud Build appartient à un autre projet que celui de l'instance Cloud SQL, vous devez ajouter l'API Cloud SQL Admin et les autorisations IAM aux deux projets.

Adresse IP privée

Pour vous connecter à votre instance Cloud SQL via une adresse IP privée, Cloud Build doit se trouver sur le même réseau VPC que votre instance Cloud SQL. La procédure de configuration est la suivante :

  1. Configurez une connexion privée entre le réseau VPC de votre instance Cloud SQL et le réseau de producteurs de services.
  2. Créez un pool privé Cloud Build.

Une fois configurée, votre application pourra se connecter directement à l'aide de l'adresse IP privée et du port 3306 de votre instance lorsque votre compilation est exécutée dans le pool.

Se connecter à Cloud SQL

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

Adresse IP publique (par défaut)

Pour les chemins d'accès d'adresses IP publiques, Cloud Build prend en charge les sockets Unix et TCP.

Vous pouvez utiliser le proxy d'authentification Cloud SQL dans une étape Cloud Build pour autoriser les connexions à votre base de données. Cette configuration :

  1. crée votre conteneur et le transfère dans Container Registry.
  2. crée un second conteneur, copiant dans le binaire du proxy d'authentification Cloud SQL.
  3. À l'aide du second conteneur, démarre le proxy Cloud SQL Auth et exécute les commandes de migration.
steps:
  - id: "docker-build"
    name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "${_IMAGE_NAME}", "sql-proxy/."]

  - id: "docker-push"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "${_IMAGE_NAME}"]

  - id: "docker-layer"
    name: "gcr.io/cloud-builders/docker"
    entrypoint: /bin/bash
    args:
      - '-c'
      - |
        echo "FROM $_IMAGE_NAME
        COPY --from=gcr.io/cloud-sql-connectors/cloud-sql-proxy /cloud-sql-proxy /cloudsql/cloud-sql-proxy" > Dockerfile-proxy;

        docker build -f Dockerfile-proxy -t ${_IMAGE_NAME}-proxy .

  # For TCP connections
  - id: "migrate-tcp"
    name: "${_IMAGE_NAME}-proxy"
    dir: sql-proxy
    env:
      - "DATABASE_NAME=${_DATABASE_NAME}"
      - "DATABASE_USER=${_DATABASE_USER}"
      - "DATABASE_HOST=127.0.0.1"
      - "DATABASE_PORT=${_DATABASE_PORT}"
      - "DATABASE_TYPE=${_DATABASE_TYPE}"
    secretEnv:
      - DATABASE_PASS
    entrypoint: /bin/bash
    args:
      - '-c'
      - |
        /cloudsql/cloud-sql-proxy --port ${_DATABASE_PORT} ${_INSTANCE_CONNECTION_NAME} & sleep 2;
        python migrate.py # for example

  # For Unix Socket connections
  - id: "migrate-socket"
    name: "${_IMAGE_NAME}-proxy"
    dir: sql-proxy
    env:
      - "DATABASE_NAME=${_DATABASE_NAME}"
      - "DATABASE_USER=${_DATABASE_USER}"
      - "INSTANCE_CONNECTION_NAME=${_INSTANCE_CONNECTION_NAME}"
      - "DATABASE_TYPE=${_DATABASE_TYPE}"
    secretEnv:
      - DATABASE_PASS
    entrypoint: /bin/bash
    args:
      - '-c'
      - |
        /cloudsql/cloud-sql-proxy --unix-socket /cloudsql ${_INSTANCE_CONNECTION_NAME} & sleep 2;
        if [ $_DATABASE_TYPE = 'mssql' ]; then echo "MSSQL doesn't support Unix Sockets. Skippng."; exit 0; fi;
        python migrate.py # for example.

options:
  dynamic_substitutions: true

substitutions:
  _DATABASE_USER: myuser
  _DATABASE_NAME: mydatabase
  _INSTANCE_CONNECTION_NAME: ${PROJECT_ID}:us-central1:myinstance
  _DATABASE_PORT: '5432'
  _DATABASE_TYPE: postgres
  _DATABASE_PASSWORD_KEY: database_password
  _IMAGE_NAME: gcr.io/${PROJECT_ID}/sample-sql-proxy

availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/${_DATABASE_PASSWORD_KEY}/versions/latest
      env: DATABASE_PASS

L'exemple de code Cloud Build ci-dessus montre comment vous pourriez exécuter un script de migration hypothétique après avoir déployé l'exemple d'application ci-dessus pour mettre à jour sa base de données Cloud SQL à l'aide du proxy d'authentification Cloud SQL et de Cloud Build. Pour exécuter cet exemple de code Cloud Build, procédez comme suit :

  1. Créez un dossier nommé sql-proxy
  2. Créez un fichier Dockerfile contenant uniquement la ligne de code suivante dans le dossier sql-proxy :

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Créez un fichier cloudbuild.yaml dans le dossier sql-proxy.
  4. Mettez à jour le fichier cloudbuild.yaml :
    1. Copiez l'exemple de code Cloud Build ci-dessus et collez-le dans le fichier cloudbuild.yaml.
    2. Utilisez une connexion TCP ou une connexion de socket Unix en supprimant le bloc de code pour la méthode de connexion non utilisée.
    3. Remplacez l'exemple de code _DATABASE_TYPE dans le bloc substitutions: par mysql.
    4. Si vous utilisez une connexion TCP, remplacez l'exemple de code _DATABASE_PORT dans le bloc substitutions: par 3306, qui est le port utilisé par MySQL.
    5. Remplacez les valeurs d'espace réservé suivantes par les valeurs utilisées dans votre projet :
      • mydatabase
      • myuser
      • myinstance
  5. Créez un secret nommé database_password dans Secret Manager.
  6. Créez un fichier de script migrate.py dans le dossier sql-proxy.
    • Le script peut référencer les variables d'environnement suivantes et le secret créé dans le fichier cloudbuild.yaml à l'aide des exemples suivants :
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('INSTANCE_CONNECTION_NAME')
    • Pour référencer les mêmes variables à partir d'un script Bash (par exemple, migrate.sh), utilisez les exemples suivants :
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. Exécutez la commande gcloud builds submit suivante pour créer un conteneur avec le proxy d'authentification Cloud SQL, démarrer le proxy d'authentification Cloud SQL et exécuter le script migrate.py :

    gcloud builds submit --config cloudbuild.yaml

Adresse IP privée

Pour les chemins d'accès des adresses IP privées, votre application se connecte directement à votre instance via des pools privés. 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 3306.

Python

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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

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

Remarque :

  • INSTANCE_CONNECTION_NAME doit être représenté au format <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 {

  // 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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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#

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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

Pour afficher cet extrait dans le contexte d'une application Web, consultez le fichier README sur 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;
    }
}

Vous pouvez ensuite créer une étape Cloud Build pour exécuter directement votre code.

steps:
  - id: "docker-build"
    name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "${_IMAGE_NAME}", "sql-private-pool/."]

  - id: "docker-push"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "${_IMAGE_NAME}"]

  - id: "migration"
    name: "${_IMAGE_NAME}"
    dir: sql-private-pool
    env:
      - "DATABASE_NAME=mydatabase"
      - "DATABASE_USER=myuser"
      - "DATABASE_HOST=${_DATABASE_HOST}"
      - "DATABASE_TYPE=${_DATABASE_TYPE}"
    secretEnv:
      - DATABASE_PASS
    entrypoint: python   # for example
    args: ["migrate.py"] # for example

options:
  pool:
    name: projects/$PROJECT_ID/locations/us-central1/workerPools/private-pool
  dynamic_substitutions: true

substitutions:
  _DATABASE_PASSWORD_KEY: database_password
  _DATABASE_TYPE: postgres
  _IMAGE_NAME: gcr.io/${PROJECT_ID}/sample-private-pool

availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/${_DATABASE_PASSWORD_KEY}/versions/latest
      env: DATABASE_PASS

L'exemple de code Cloud Build ci-dessus montre comment vous pourriez exécuter un script de migration hypothétique après avoir déployé l'exemple d'application ci-dessus pour mettre à jour sa base de données Cloud SQL à l'aide de Cloud Build. Pour exécuter cet exemple de code Cloud Build, procédez comme suit :

  1. Créez un dossier nommé sql-private-pool
  2. Créez un fichier Dockerfile contenant uniquement la ligne de code suivante dans le dossier sql-private-pool :

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Créez un fichier cloudbuild.yaml dans le dossier sql-private-pool.
  4. Mettez à jour le fichier cloudbuild.yaml :
    1. Copiez l'exemple de code Cloud Build ci-dessus et collez-le dans le fichier cloudbuild.yaml.
    2. Remplacez les valeurs d'espace réservé suivantes par les valeurs utilisées dans votre projet :
      • mydatabase
      • myuser
      • databasehost, au format host:port.
  5. Créez un secret nommé database_password dans Secret Manager.
  6. Créez un fichier de script migrate.py dans le dossier sql-proxy.
    • Le script peut référencer les variables d'environnement suivantes et le secret créé dans le fichier cloudbuild.yaml à l'aide des exemples suivants :
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('DATABASE_HOST')
    • Pour référencer les mêmes variables à partir d'un script Bash (par exemple, migrate.sh), utilisez les exemples suivants :
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. Exécutez la commande gcloud builds submit suivante pour créer un conteneur avec le proxy d'authentification Cloud SQL, démarrer le proxy d'authentification Cloud SQL et exécuter le script migrate.py :

    gcloud builds submit --config cloudbuild.yaml

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.

Migrations de schéma de base de données

En configurant Cloud Build pour se connecter à Cloud SQL, vous pouvez exécuter des tâches de migration de schéma de base de données dans Cloud Build à l'aide du même code que vous pourriez déployer sur toute autre plate-forme sans serveur.

Utiliser Secret Manager

Vous pouvez utiliser Secret Manager pour inclure des informations sensibles dans vos compilations.