Conéctate desde Cloud Build

En esta página, se muestran información y ejemplos para conectar a una instancia de Cloud SQL desde un servicio que se ejecuta en Cloud Build.

Cloud SQL es un servicio de base de datos completamente administrado que te ayuda a configurar, mantener, controlar y administrar tus bases de datos relacionales en la nube.

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

Configura una instancia de Cloud SQL

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

    Enable the API

  2. Crea una instancia de Cloud SQL para SQL Server. Te recomendamos que elijas una ubicación de instancia de Cloud SQL en la misma región que tu servicio de Cloud Run para obtener una mejor latencia, evitar algunos costos de red y reducir los riesgos de fallas entre regiones.

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

Configura Cloud Build

Los pasos para configurar Cloud Run dependen del tipo de dirección IP que hayas asignado a la 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 IAM de la consola de Google Cloud como la Principal [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com.

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

Tu cuenta de servicio de Cloud Build necesita uno de los siguientes roles de IAM:

  • Cloud SQL Client (recomendado)
  • Cloud SQL Admin
O bien, puedes asignar los siguientes permisos de IAM de forma manual:
  • cloudsql.instances.connect
  • cloudsql.instances.get

Si la cuenta de servicio de Cloud Build pertenece a un proyecto distinto del de la instancia de Cloud SQL, se deberán agregar los permisos de IAM y la API de Cloud SQL Admin para 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 configurar esto, haz lo siguiente:

  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, la aplicación podrá conectarse directamente mediante la dirección IP privada y el puerto 1433 de la instancia cuando la compilación se ejecute en el grupo.

Conectar a Cloud SQL

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

IP pública (predeterminada)

Para rutas de IP públicas, Cloud Build es compatible con sockets 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 tiene las siguientes características:

  1. Compila el contenedor y lo envía a Container Registry.
  2. Compila un segundo contenedor y copia el objeto binario del proxy de autenticación de Cloud SQL.
  3. Mediante el segundo contenedor, inicia el proxy de Cloud SQL Auth y ejecuta cualquier comando de migración.
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

La muestra de código de Cloud Build anterior muestra cómo puedes ejecutar una secuencia de comandos hipotética de migración después de implementar la app de ejemplo anterior para actualizar su base de datos de Cloud SQL con el proxy de Cloud SQL Auth y Cloud Build. Para ejecutar esta muestra de código de Cloud Build, los pasos de configuración necesarios son los siguientes:

  1. Crea un nombre de carpeta llamada sql-proxy
  2. Crea un archivo Dockerfile en la carpeta sql-proxy con la siguiente línea de código única para el contenido del archivo:

    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 muestra de Cloud Build anterior y pégalo en el archivo cloudbuild.yaml.
    2. Actualiza el código de ejemplo _DATABASE_TYPE dentro del bloque substitutions: para que sea mssql.
    3. Actualiza el código de ejemplo _DATABASE_PORT en el bloque substitutions: para que sea 1433, que es el puerto que usa SQL Server.
    4. Reemplaza los siguientes valores de marcador de posición por los valores que se usan en tu proyecto:
      • mydatabase
      • myuser
      • myinstance
  5. Crea un Secret llamado database_password en Secret Manager.
  6. Crea un archivo de secuencia de comandos migrate.py en la carpeta sql-proxy.
    • La secuencia de comandos puede hacer referencia a las siguientes variables de entorno y el Secret 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 compilar un contenedor con el proxy de autenticación de Cloud SQL, inicia el proxy de Cloud SQL Auth y ejecuta la secuencia de comandos migrate.py:

    gcloud builds submit --config cloudbuild.yaml

IP privada

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

Conéctate con TCP

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

Python

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

import os

import sqlalchemy


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

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

    return pool

Java

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

Nota:

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


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

public class TcpConnectionPoolFactory extends ConnectionPoolFactory {

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

    // Configure which instance and what database user to connect with.
    config.setJdbcUrl(
        String.format("jdbc:sqlserver://%s:%s;databaseName=%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "sqlserver"
    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 mssql = require('mssql');

// createTcpPool initializes a TCP connection pool for a Cloud SQL
// instance of SQL Server.
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 = {
    server: process.env.INSTANCE_HOST, // e.g. '127.0.0.1'
    port: parseInt(process.env.DB_PORT), // e.g. 1433
    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'
    options: {
      trustServerCertificate: true,
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return mssql.connect(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"
	"strings"

	_ "github.com/denisenkom/go-mssqldb"
)

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

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


	// dbPool is the pool of database connections.
	dbPool, err := sql.Open("sqlserver", 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 Microsoft.Data.SqlClient;
using System;

namespace CloudSql
{
    public class SqlServerTcp
    {
        public static SqlConnectionStringBuilder NewSqlServerTCPConnectionString()
        {
            // Equivalent connection string:
            // "User Id=<DB_USER>;Password=<DB_PASS>;Server=<INSTANCE_HOST>;Database=<DB_NAME>;"
            var connectionString = new SqlConnectionStringBuilder()
            {
                // 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.
                DataSource = 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'
                InitialCatalog = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'

                // The Cloud SQL proxy provides encryption between the proxy and instance
                Encrypt = false,
            };
            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: sqlserver
  # 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") { 1433 }%> 

PHP

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

namespace Google\Cloud\Samples\CloudSQL\SQLServer;

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(
                'sqlsrv:server=%s;Database=%s',
                $instanceHost,
                $dbName
            );

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

        return $conn;
    }
}

Luego, puedes crear un paso de Cloud Build para ejecutar tu 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
  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

La muestra de código de Cloud Build anterior muestra cómo puedes ejecutar una secuencia de comandos hipotética de migración después de implementar la app de ejemplo anterior para actualizar su base de datos de Cloud SQL con Cloud Build. Para ejecutar esta muestra de código de Cloud Build, los pasos de configuración necesarios son los siguientes:

  1. Crea un nombre de carpeta llamada sql-private-pool
  2. Crea un archivo Dockerfile en la carpeta sql-private-pool con la siguiente línea de código única para el contenido del archivo:

    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 muestra de Cloud Build anterior y pégalo en el archivo cloudbuild.yaml.
    2. Reemplaza los siguientes valores de marcador de posición por los valores que se usan en tu proyecto:
      • mydatabase
      • myuser
      • databasehost, en el formato host:port.
  5. Crea un Secret llamado database_password en Secret Manager.
  6. Crea un archivo de secuencia de comandos migrate.py en la carpeta sql-proxy.
    • La secuencia de comandos puede hacer referencia a las siguientes variables de entorno y el Secret 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 compilar un contenedor con el proxy de autenticación de Cloud SQL, inicia el proxy de Cloud SQL Auth y ejecuta la secuencia de comandos migrate.py:

    gcloud builds submit --config cloudbuild.yaml

Prácticas recomendadas y más información

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

También puedes realizar pruebas mediante 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 conectarte a Cloud SQL, puedes ejecutar tareas de migración del esquema de la base de datos en Cloud Build mediante el mismo código que implementarías en cualquier otra plataforma sin servidores.

Usa Secret Manager

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