Conectarse desde Cloud Build

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

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

Cloud Build es un servicio que ejecuta tus compilaciones en la infraestructura de Google Cloud .

Configurar una instancia de Cloud SQL

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

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  2. Crea una instancia de Cloud SQL para PostgreSQL. Te recomendamos que elijas una ubicación de instancia de Cloud SQL en la misma región que tu servicio de Cloud Run para mejorar la latencia, evitar algunos costes de redes y reducir los riesgos de fallos entre regiones.

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

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

Configurar un repositorio de Artifact Registry

  1. Si aún no lo has hecho, habilita la API de Artifact Registry en el Google Cloud proyecto desde el que te vas a conectar:

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  2. Crea un registro de artefactos de Docker. Para mejorar la latencia, reducir el riesgo de fallos entre regiones y evitar costes de red adicionales, te recomendamos que elijas una ubicación de Artifact Registry en la misma región que tu servicio de Cloud Run.

Configurar Cloud Build

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

IP pública (predeterminada)

Asegúrate de que tu cuenta de servicio de Cloud Build tenga los roles y permisos de IAM necesarios para conectarse a la instancia de Cloud SQL. La cuenta de servicio de Cloud Build aparece en la página Gestión de identidades y accesos de la consola Google Cloud como Principal [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com.

Para ver esta cuenta de servicio en la Google Cloud consola, selecciona la casilla Incluir asignaciones de roles proporcionadas por Google.

Tu cuenta de servicio de Cloud Build necesita el Cloud SQL Client rol de gestión de identidades y accesos.

Si la cuenta de servicio de Cloud Build pertenece a un proyecto diferente al de la instancia de Cloud SQL, se deben añadir la API Cloud SQL Admin y el rol a ambos proyectos.

IP privada

Para conectarte a tu instancia de Cloud SQL a través de una IP privada, Cloud Build debe estar en la misma red de VPC que tu instancia de Cloud SQL. Para configurarlo, sigue estos pasos:

  1. Configura una conexión privada entre la red de VPC de tu instancia de Cloud SQL y la red del productor de servicios.
  2. Crea un grupo privado de Cloud Build.

Una vez configurada, tu aplicación podrá conectarse directamente mediante la dirección IP privada y el puerto 5432 de tu instancia cuando se ejecute la compilación en el grupo.

Conectar a Cloud SQL

Después de configurar Cloud Build, puedes conectarte a tu instancia de Cloud SQL.

IP pública (predeterminada)

En el caso de las rutas de IP pública, Cloud Build admite sockets Unix y TCP.

Puedes usar el proxy de autenticación de Cloud SQL en un paso de Cloud Build para permitir conexiones a tu base de datos. Esta configuración:

  1. Compila el contenedor y lo envía a Artifact Registry.
  2. Crea un segundo contenedor y copia el archivo binario del proxy de autenticación de Cloud SQL.
  3. Con el segundo contenedor, se inicia el proxy de autenticación de Cloud SQL y se ejecutan los comandos de migración.
steps:
  - id: install-proxy
    name: gcr.io/cloud-builders/wget
    entrypoint: sh
    args:
      - -c
      - |
        wget -O /workspace/cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/2.18.2
        chmod +x /workspace/cloud-sql-proxy

 - id: migrate
    waitFor: ['install-proxy']
    name: YOUR_CONTAINER_IMAGE_NAME
    entrypoint: sh
    env:
      - "DATABASE_NAME=${_DATABASE_NAME}"
      - "DATABASE_USER=${_DATABASE_USER}"
      - "DATABASE_PORT=${_DATABASE_PORT}"
      - "INSTANCE_CONNECTION_NAME=${_INSTANCE_CONNECTION_NAME}"
    secretEnv:
      - DATABASE_PASS
    args:
      - "-c"
      - |
        /workspace/cloud-sql-proxy ${_INSTANCE_CONNECTION_NAME} --port ${_DATABASE_PORT} & sleep 2;
        # Cloud SQL Proxy is now up and running, add your own logic below to connect
        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_PASSWORD_KEY: database_password
    _AR_REPO_REGION: us-central1
    _AR_REPO_NAME: my-docker-repo
    _IMAGE_NAME: ${_AR_REPO_REGION}-docker.pkg.dev/${PROJECT_ID}/${_AR_REPO_NAME}/sample-sql-proxy

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

El ejemplo de código de Cloud Build muestra cómo se puede ejecutar una secuencia de comandos migrate.py hipotética después de desplegar la aplicación de ejemplo anterior para actualizar su base de datos de Cloud SQL mediante el proxy de autenticación de Cloud SQL y Cloud Build. Para ejecutar este código de ejemplo de Cloud Build, debes seguir estos pasos:

  1. Crea un nombre de carpeta sql-proxy
  2. Crea un archivo Dockerfile en la carpeta sql-proxy con la siguiente línea de código como contenido:
        FROM gcr.io/gcp-runtimes/ubuntu_20_0_4
        
  3. Crea un archivo cloudbuild.yaml en la carpeta sql-proxy.
  4. Actualiza el archivo cloudbuild.yaml:
    1. Copia el código de ejemplo de Cloud Build anterior y pégalo en el archivo cloudbuild.yaml.
    2. Sustituye los siguientes valores de marcador de posición por los valores que se usan en tu proyecto:
      • mydatabase
      • myuser
      • myinstance
  5. Crea un secreto llamado database_password en Secret Manager.
  6. Crea un archivo de script migrate.py en la carpeta sql-proxy.
    • La secuencia de comandos puede hacer referencia a las siguientes variables de entorno y al secreto creado en el archivo cloudbuild.yaml mediante los siguientes ejemplos:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('INSTANCE_CONNECTION_NAME')
    • Para hacer referencia a las mismas variables desde una secuencia de comandos de Bash (por ejemplo, migrate.sh), usa los siguientes ejemplos:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. Ejecuta el siguiente comando gcloud builds submit para crear un contenedor con el proxy de autenticación de Cloud SQL, iniciar el proxy de autenticación de Cloud SQL y ejecutar la secuencia de comandos migrate.py:
        gcloud builds submit --config cloudbuild.yaml
        

IP privada

En las rutas de IP privada, tu aplicación se conecta directamente a tu instancia a través de grupos privados. Este método usa TCP para conectarse directamente a la instancia de Cloud SQL sin usar el proxy de autenticación de Cloud SQL.

Conectarse con TCP

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

Python

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

Java

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

Nota:


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

public class TcpConnectionPoolFactory extends ConnectionPoolFactory {

  // Note: Saving credentials in environment variables is convenient, but not
  // secure - consider a more secure solution such as
  // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
  // keep secrets safe.
  private static final String DB_USER = System.getenv("DB_USER");
  private static final String DB_PASS = System.getenv("DB_PASS");
  private static final String DB_NAME = System.getenv("DB_NAME");

  private static final String INSTANCE_HOST = System.getenv("INSTANCE_HOST");
  private static final String DB_PORT = System.getenv("DB_PORT");


  public static DataSource createConnectionPool() {
    // The configuration object specifies behaviors for the connection pool.
    HikariConfig config = new HikariConfig();

    // 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);
  }
}

Node.js

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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);
};

Go

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

package cloudsql

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

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

C#

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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;
        }
    }
}

Ruby

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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 }%>

PHP

Para ver este fragmento en el contexto de una aplicación web, consulta el archivo README en 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;
    }
}

Después, puedes crear un paso de Cloud Build para ejecutar el código directamente.

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
  dynamicSubstitutions: true

substitutions:
  _DATABASE_PASSWORD_KEY: database_password
  _DATABASE_TYPE: postgres
  _AR_REPO_REGION: us-central1
  _AR_REPO_NAME: my-docker-repo
  _IMAGE_NAME: ${_AR_REPO_REGION}-docker.pkg.dev/${PROJECT_ID}/${_AR_REPO_NAME}/sample-private-pool

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

El ejemplo de código de Cloud Build anterior muestra cómo se puede ejecutar una hipotética secuencia de comandos migrate después de desplegar la aplicación de ejemplo anterior para actualizar su base de datos de Cloud SQL mediante Cloud Build. Para ejecutar este código de ejemplo de Cloud Build, debes seguir estos pasos:

  1. Crea un nombre de carpeta sql-private-pool
  2. Crea un archivo Dockerfile en la carpeta sql-private-pool con la siguiente línea de código como contenido:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Crea un archivo cloudbuild.yaml en la carpeta sql-private-pool.
  4. Actualiza el archivo cloudbuild.yaml:
    1. Copia el código de Cloud Build de ejemplo que aparece arriba y pégalo en el archivo cloudbuild.yaml.
    2. Sustituye los siguientes valores de marcador de posición por los valores que se usan en tu proyecto:
      • mydatabase
      • myuser
      • databasehost, con el formato host:port.
  5. Crea un secreto llamado database_password en Secret Manager.
  6. Crea un archivo de script migrate.py en la carpeta sql-proxy.
    • La secuencia de comandos puede hacer referencia a las siguientes variables de entorno y al secreto creado en el archivo cloudbuild.yaml mediante los siguientes ejemplos:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('DATABASE_HOST')
    • Para hacer referencia a las mismas variables desde una secuencia de comandos de Bash (por ejemplo, migrate.sh), usa los siguientes ejemplos:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. Ejecuta el siguiente comando gcloud builds submit para crear un contenedor con el proxy de autenticación de Cloud SQL, iniciar el proxy de autenticación de Cloud SQL y ejecutar la secuencia de comandos migrate.py:

    gcloud builds submit --config cloudbuild.yaml

Prácticas recomendadas y otra información

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

También puedes hacer pruebas con el proxy de Cloud SQL a través de un contenedor de Docker.

Migraciones de esquemas de bases de datos

Si configuras Cloud Build para que se conecte a Cloud SQL, podrás ejecutar tareas de migración de esquemas de bases de datos en Cloud Build con el mismo código que desplegarías en cualquier otra plataforma sin servidor.

Usar Secret Manager

Puedes usar Secret Manager para incluir información sensible en tus compilaciones.