Conectar-se pelo Cloud Build

Nesta página, você confere informações e exemplos de como se conectar a uma instância do Cloud SQL usando um serviço em execução no Cloud Build.

O Cloud SQL é um serviço de banco de dados totalmente gerenciado que ajuda a configurar, manter, gerenciar e administrar seus bancos de dados relacionais na nuvem.

O Cloud Build é um serviço que executa seus builds na infraestrutura do Google Cloud.

Configurar uma instância do Cloud SQL

  1. Ative a API Cloud SQL Admin no projeto do Google Cloud do qual você está se conectando, caso ainda não tenha feito isso:

    Enable the API

  2. Crie uma instância do Cloud SQL para MySQL. Recomendamos que você escolha um local da instância do Cloud SQL na mesma região do serviço do Cloud Run para melhorar a latência, evitar alguns custos de rede e reduzir os riscos de falha entre regiões.

    Por padrão, o Cloud SQL atribui um endereço IP público a uma nova instância. Você também tem a opção de atribuir um endereço IP privado. Para mais informações sobre as opções de conectividade de ambos, consulte a página Visão geral da conexão.

Configure o Cloud Build

As etapas para configurar o Cloud Build dependem do tipo de endereço IP atribuído à instância do Cloud SQL.

IP público (padrão)

Verifique se sua conta de serviço do Cloud Build tem os papéis e permissões do IAM necessários para se conectar à instância do Cloud SQL. A conta de serviço do Cloud Build está listada na página IAM do Console do Google Cloud como Principal.[YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com

Para mostrar essa conta de serviço no console do Google Cloud, marque a caixa de seleção Incluir concessões de papéis fornecidos pelo Google.

A conta de serviço do Cloud Build precisa de um dos seguintes papéis do IAM:

  • Cloud SQL Client (recomendável)
  • Cloud SQL Admin
Também é possível atribuir manualmente as seguintes permissões do IAM:
  • cloudsql.instances.connect
  • cloudsql.instances.get

Se a conta de serviço do Cloud Build pertencer a um projeto diferente da instância do Cloud SQL, as permissões da API Cloud SQL Admin e do IAM precisarão ser adicionadas aos dois projetos.

IP particular

Para se conectar à instância do Cloud SQL por um IP privado, o Cloud Build precisa estar na mesma rede VPC da instância do Cloud SQL. Para configurar isso:

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

Depois de configurado, o aplicativo poderá se conectar diretamente usando o endereço IP privado e a porta 3306 da instância quando a versão for executada no pool.

Conecte-se ao Cloud SQL

Depois de configurar o Cloud Build, conecte-se à instância do Cloud SQL.

IP público (padrão)

Para caminhos de IP público, o Cloud Build é compatível com soquetes Unix e TCP.

É possível usar o proxy de autenticação do Cloud SQL em uma etapa do Cloud Build para permitir conexões com o banco de dados. Essa configuração:

  1. Cria o contêiner e o envia para o Container Registry.
  2. Cria um segundo contêiner, copiando o binário do proxy de autenticação do Cloud SQL.
  3. Usando o segundo contêiner, inicia o proxy de autenticação do Cloud SQL e executa todos os comandos de migração.
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

O exemplo de código do Cloud Build acima mostra como executar um script hipotético de migração após a implantação do aplicativo de exemplo acima para atualizar o banco de dados do Cloud SQL usando o proxy de autenticação do Cloud SQL e o Cloud Build. Para executar este exemplo de código do Cloud Build, siga as etapas necessárias:

  1. Crie um nome de pasta sql-proxy.
  2. Crie um arquivo Dockerfile na pasta sql-proxy com a única linha de código a seguir no conteúdo do arquivo:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Crie um arquivo cloudbuild.yaml na pasta sql-proxy.
  4. Atualize o cloudbuild.yaml arquivo:
    1. Copie o código de amostra do Cloud Build acima e cole-o no arquivo cloudbuild.yaml.
    2. Use uma conexão TCP ou uma conexão de soquete Unix removendo o bloco de código do método que não está sendo usado.
    3. Atualize o código de exemplo _DATABASE_TYPE no bloco substitutions: para que ele seja mysql.
    4. Se você estiver usando uma conexão TCP, atualize o código de exemplo _DATABASE_PORT no bloco substitutions: para que seja 3306, que é a porta usada pelo MySQL.
    5. Substitua os valores de marcador a seguir pelos valores usados no projeto:
      • mydatabase
      • myuser
      • myinstance
  5. Crie um secret chamado database_password no Secret Manager.
  6. Crie um arquivo de script migrate.py na pasta sql-proxy.
    • O script pode fazer referência às seguintes variáveis de ambiente e ao secret criado no arquivo cloudbuild.yaml usando os 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 de um script de Bash (por exemplo: migrate.sh), use os exemplos a seguir.
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. Execute o seguinte comando gcloud builds submit para criar um contêiner com o proxy de autenticação do Cloud SQL, inicie esse proxy e execute o script migrate.py:

    gcloud builds submit --config cloudbuild.yaml

IP particular

Para caminhos de IP particulares, o aplicativo se conecta diretamente à instância via pools particulares. Esse método usa TCP para a conexão direta com a instância do Cloud SQL sem usar o proxy de autenticação do Cloud SQL.

Conectar com TCP

Conecte-se usando o endereço IP privado da sua instância do Cloud SQL como o host e a porta 3306.

Python

Para conferir esse snippet no contexto de um aplicativo da Web, consulte o 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 esse snippet no contexto de um aplicativo da Web, consulte o README no GitHub (em inglês).

Observação:

  • INSTANCE_CONNECTION_NAME deve ser representado como <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • O uso do argumento ipTypes=PRIVATE forçará o SocketFactory a se conectar ao IP privado associado de uma instância
  • Consulte os requisitos de versão de fábrica do soquete JDBC para o arquivo pom.xml aqui.


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 conferir esse snippet no contexto de um aplicativo da Web, consulte o 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);
};

Go

Para conferir esse snippet no contexto de um aplicativo da Web, consulte o README no 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#

Para conferir esse snippet no contexto de um aplicativo da Web, consulte o 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 conferir esse snippet no contexto de um aplicativo da Web, consulte o 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 conferir esse snippet no contexto de um aplicativo da Web, consulte o 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, crie uma etapa do Cloud Build para executar o 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
  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

A amostra de código do Cloud Build acima mostra como executar um script hipotético de migração após implantar o aplicativo de amostra acima para atualizar o banco de dados do Cloud SQL usando o Cloud Build. Para executar esta amostra de código do Cloud Build, siga as etapas necessárias:

  1. Crie um nome de pasta sql-private-pool.
  2. Crie um arquivo Dockerfile na pasta sql-private-pool com a única linha de código a seguir no conteúdo do arquivo:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Crie um arquivo cloudbuild.yaml na pasta sql-private-pool.
  4. Atualize o cloudbuild.yaml arquivo:
    1. Copie o código de amostra do Cloud Build acima e cole-o no arquivo cloudbuild.yaml.
    2. Substitua os valores de marcador a seguir pelos valores usados no projeto:
      • mydatabase
      • myuser
      • No formulário, databasehost, host:port.
  5. Crie um secret chamado database_password no Secret Manager.
  6. Crie um arquivo de script migrate.py na pasta sql-proxy.
    • O script pode fazer referência às seguintes variáveis de ambiente e ao secret criado no arquivo cloudbuild.yaml usando os 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 de um script de Bash (por exemplo: migrate.sh), use os exemplos a seguir.
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. Execute o seguinte comando gcloud builds submit para criar um contêiner com o proxy de autenticação do Cloud SQL, inicie esse proxy e execute o script migrate.py:

    gcloud builds submit --config cloudbuild.yaml

Práticas recomendadas e outras informações

Use o proxy do Cloud SQL Auth ao testar seu aplicativo localmente. Consulte o guia de início rápido para o uso do proxy de autenticação do Cloud SQL e confira instruções detalhadas.

Se preferir, teste usando o Cloud SQL Proxy por meio de um contêiner do Docker.

Migrações de esquema de banco de dados

Ao configurar o Cloud Build para se conectar ao Cloud SQL, é possível executar tarefas de migração de esquema de banco de dados no Cloud Build usando o mesmo código implantado em qualquer outra plataforma sem servidor.

Como usar o Gerenciador de secrets

Use o Secret Manager para incluir informações confidenciais nos seus builds.