Ligue a partir do Cloud Build

Esta página contém informações e exemplos para estabelecer ligação a uma instância do Cloud SQL a partir de um serviço em execução no Cloud Build.

O Cloud SQL é um serviço de base de dados totalmente gerido que ajuda na configuração, manutenção, gestão e administração das suas bases de dados relacionais na nuvem.

O Cloud Build é um serviço que executa as suas compilações na Google Cloud infraestrutura.

Configure uma instância do Cloud SQL

  1. Ative a API Cloud SQL Admin no Google Cloud projeto a partir do qual está a estabelecer ligação, se ainda não o tiver feito:

    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. Crie uma instância do Cloud SQL para MySQL. Recomendamos que escolha uma localização da instância do Cloud SQL na mesma região que o seu serviço do Cloud Run para ter uma melhor latência, evitar alguns custos de rede e reduzir os riscos de falhas entre regiões.

    Por predefinição, o Cloud SQL atribui um endereço IP público a uma nova instância. Também tem a opção de atribuir um endereço IP privado. Para mais informações sobre as opções de conetividade para ambos, consulte a página Vista geral da ligação.

  3. Quando cria a instância, pode escolher a hierarquia do certificado do servidor (AC) para a instância e, em seguida, configurar a hierarquia como a serverCaMode para a instância. Tem de selecionar a opção de AC por instância (GOOGLE_MANAGED_INTERNAL_CA) como o modo de AC do servidor para instâncias às quais quer estabelecer ligação a partir de aplicações Web.

Configure um repositório do Artifact Registry

  1. Se ainda não o fez, ative a API Artifact Registry no Google Cloud projeto a partir do qual está a estabelecer ligação:

    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. Crie um Docker Artifact Registry. Para melhorar a latência, reduzir o risco de falha entre regiões e evitar custos de rede adicionais, recomendamos que escolha uma localização do Artifact Registry na mesma região que o seu serviço do Cloud Run.

Configure o Cloud Build

Os passos para configurar o Cloud Build dependem do tipo de endereço IP que atribuiu à sua instância do Cloud SQL.

IP público (predefinição)

Certifique-se de que a conta de serviço do Cloud Build tem as funções e autorizações do IAM necessárias para estabelecer ligação à instância do Cloud SQL. A conta de serviço do Cloud Build é apresentada na página Google Cloud console IAM como o principal [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com.

Para ver esta conta de serviço na Google Cloud consola, selecione a caixa de verificação Incluir concessões de funções fornecidas pela Google.

A sua conta de serviço do Cloud Build precisa da Cloud SQL Client função do IAM.

Se a conta de serviço do Cloud Build pertencer a um projeto diferente da instância do Cloud SQL, tem de adicionar a API Admin do Cloud SQL e a função para ambos os projetos.

IP privado

Para se ligar à sua instância do Cloud SQL através de um IP privado, o Cloud Build tem de estar na mesma rede de VPC que a sua instância do Cloud SQL. Para configurar esta opção:

  1. Configure uma ligação privada entre a rede VPC da sua instância do Cloud SQL e a rede do produtor de serviços.
  2. Crie um grupo privado do Cloud Build.

Depois de configurada, a sua aplicação vai poder estabelecer ligação diretamente através do endereço IP privado e da porta 3306 da instância quando a compilação for executada no conjunto.

Estabeleça ligação ao Cloud SQL

Depois de configurar o Cloud Build, pode estabelecer ligação à sua instância do Cloud SQL.

IP público (predefinição)

Para caminhos de IP público, o Cloud Build suporta sockets Unix e TCP.

Pode usar o proxy Auth do Cloud SQL num passo do Cloud Build para permitir ligações à sua base de dados. Esta configuração:

  1. Cria o contentor e envia-o para o Artifact Registry.
  2. Cria um segundo contentor, copiando o ficheiro binário do proxy Auth do Cloud SQL.
  3. A utilização do segundo contentor inicia o proxy Auth do Cloud SQL e executa quaisquer comandos de migração.
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: '3306'
    _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"

O exemplo de código do Cloud Build mostra como pode executar um script migrate.py hipotético após implementar a app de exemplo anterior para atualizar a respetiva base de dados do Cloud SQL através do proxy Auth do Cloud SQL e do Cloud Build. Para executar este exemplo de código do Cloud Build, os passos de configuração necessários são:

  1. Crie um nome de pasta sql-proxy
  2. Crie um ficheiro Dockerfile na pasta sql-proxy com a seguinte linha de código única para o conteúdo do ficheiro:
        FROM gcr.io/gcp-runtimes/ubuntu_20_0_4
        
  3. Crie um ficheiro cloudbuild.yaml na pasta sql-proxy.
  4. Atualize o ficheiro cloudbuild.yaml:
    1. Copie o código do Cloud Build do exemplo anterior e cole-o no ficheiro cloudbuild.yaml.
    2. Se estiver a usar uma ligação TCP, atualize o código de exemplo _DATABASE_PORT no bloco substitutions: para 3306, que é a porta usada pelo MySQL.
    3. Substitua os seguintes valores de marcadores de posição pelos valores usados no seu projeto:
      • mydatabase
      • myuser
      • myinstance
  5. Crie um segredo com o nome database_password no Secret Manager.
  6. Crie um ficheiro de script migrate.py na pasta sql-proxy.
    • O script pode referenciar as seguintes variáveis de ambiente e o segredo criado no ficheiro cloudbuild.yaml através dos seguintes exemplos:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('INSTANCE_CONNECTION_NAME')
    • Para fazer referência às mesmas variáveis a partir de um script Bash (por exemplo: migrate.sh), use os seguintes exemplos:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. Execute o seguinte comando gcloud builds submit para criar um contentor com o proxy Auth do Cloud SQL, iniciar o proxy Auth do Cloud SQL e executar o script migrate.py:
        gcloud builds submit --config cloudbuild.yaml
        

IP privado

Para caminhos de IP privado, a sua aplicação liga-se diretamente à instância através de pools privadas. Este método usa o TCP para se ligar diretamente à instância do Cloud SQL sem usar o proxy Auth do Cloud SQL.

Estabeleça ligação com TCP

Estabeleça ligação através do endereço IP privado da sua instância do Cloud SQL como anfitrião e porta 3306.

Python

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no GitHub.

import os

import sqlalchemy


def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
    """Initializes a TCP connection pool for a Cloud SQL instance of MySQL."""
    # Note: Saving credentials in environment variables is convenient, but not
    # secure - consider a more secure solution such as
    # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
    # keep secrets safe.
    db_host = os.environ[
        "INSTANCE_HOST"
    ]  # e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
    db_user = os.environ["DB_USER"]  # e.g. 'my-db-user'
    db_pass = os.environ["DB_PASS"]  # e.g. 'my-db-password'
    db_name = os.environ["DB_NAME"]  # e.g. 'my-database'
    db_port = os.environ["DB_PORT"]  # e.g. 3306

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

Java

Para ver este fragmento no contexto de uma app Web, consulte o LEIA-ME no GitHub.

Nota:


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

public class TcpConnectionPoolFactory extends ConnectionPoolFactory {

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

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


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

    // The following URL is equivalent to setting the config options below:
    // jdbc:mysql://<INSTANCE_HOST>:<DB_PORT>/<DB_NAME>?user=<DB_USER>&password=<DB_PASS>
    // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory
    // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url

    // Configure which instance and what database user to connect with.
    config.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "mysql"
    config.setPassword(DB_PASS); // e.g. "my-password"


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

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

Node.js

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no 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);
};

Ir

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no GitHub.

package cloudsql

import (
	"crypto/tls"
	"crypto/x509"
	"database/sql"
	"errors"
	"fmt"
	"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#

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no 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

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no 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

Para ver este fragmento no contexto de uma app Web, consulte o ficheiro README no 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;
    }
}

Em seguida, pode criar um passo do Cloud Build para executar o seu código diretamente.

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

O exemplo de código do Cloud Build acima mostra como pode executar um script migrate hipotético após implementar a app de exemplo acima para atualizar a respetiva base de dados do Cloud SQL através do Cloud Build. Para executar este exemplo de código do Cloud Build, os passos de configuração necessários são:

  1. Crie um nome de pasta sql-private-pool
  2. Crie um ficheiro Dockerfile na pasta sql-private-pool com a seguinte linha de código única para o conteúdo do ficheiro:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Crie um ficheiro cloudbuild.yaml na pasta sql-private-pool.
  4. Atualize o ficheiro cloudbuild.yaml:
    1. Copie o exemplo de código do Cloud Build acima e cole-o no ficheiro cloudbuild.yaml.
    2. Substitua os seguintes valores de marcadores de posição pelos valores usados no seu projeto:
      • mydatabase
      • myuser
      • databasehost, no formato host:port.
  5. Crie um segredo com o nome database_password no Secret Manager.
  6. Crie um ficheiro de script migrate.py na pasta sql-proxy.
    • O script pode referenciar as seguintes variáveis de ambiente e o segredo criado no ficheiro cloudbuild.yaml através dos seguintes exemplos:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('DATABASE_HOST')
    • Para fazer referência às mesmas variáveis a partir de um script Bash (por exemplo: migrate.sh), use os seguintes exemplos:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. Execute o seguinte comando gcloud builds submit para criar um contentor com o proxy Auth do Cloud SQL, iniciar o proxy Auth do Cloud SQL e executar o script migrate.py:

    gcloud builds submit --config cloudbuild.yaml

Práticas recomendadas e outras informações

Pode usar o proxy Auth do Cloud SQL quando testar a sua aplicação localmente. Consulte o início rápido para usar o proxy Auth do Cloud SQL para ver instruções detalhadas.

Também pode testar através do proxy do Cloud SQL através de um contentor Docker.

Migrações de esquemas de bases de dados

Ao configurar o Cloud Build para estabelecer ligação ao Cloud SQL, pode executar tarefas de migração do esquema da base de dados no Cloud Build com o mesmo código que implementaria em qualquer outra plataforma sem servidor.

Usar o Secret Manager

Pode usar o Secret Manager para incluir informações confidenciais nas suas compilações.