Menghubungkan dari Cloud Build

Halaman ini berisi informasi dan contoh untuk terhubung ke instance Cloud SQL dari layanan yang berjalan di Cloud Build.

Cloud SQL adalah layanan database yang terkelola sepenuhnya untuk membantu Anda menyiapkan, memelihara, mengelola, dan mengatur database relasional Anda di cloud.

Cloud Build adalah layanan yang mengeksekusi build Anda di infrastruktur Google Cloud.

Siapkan instance Cloud SQL

  1. Aktifkan Cloud SQL Admin API di project Google Cloud yang menjadi sumber koneksi, jika Anda belum melakukannya:

    Mengaktifkan API

  2. Buat instance Cloud SQL untuk MySQL. Sebaiknya pilih lokasi instance Cloud SQL di region yang sama dengan layanan Cloud Run Anda untuk mendapatkan latensi yang lebih baik, menghindari biaya jaringan, dan mengurangi risiko kegagalan lintas region.

    Secara default, Cloud SQL menetapkan alamat IP publik ke instance baru. Anda juga memiliki opsi untuk menetapkan alamat IP pribadi. Untuk mengetahui informasi selengkapnya tentang opsi konektivitas untuk keduanya, lihat halaman Menghubungkan Ringkasan.

Konfigurasi Cloud Build

Langkah-langkah untuk mengonfigurasi Cloud Build bergantung pada jenis alamat IP yang Anda tetapkan ke instance Cloud SQL.

IP Publik (default)

Pastikan bahwa akun layanan Cloud Build Anda memiliki peran dan izin IAM yang diperlukan untuk terhubung ke instance Cloud SQL. Akun layanan Cloud Build tercantum di halaman IAM di konsol Google Cloud sebagai Akun utama [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com.

Untuk melihat akun layanan ini di Konsol Google Cloud, centang kotak Include Google-available role Grants.

Akun layanan Cloud Build Anda memerlukan salah satu peran IAM berikut:

  • Cloud SQL Client (Pilihan)
  • Cloud SQL Admin
Atau, Anda dapat menetapkan izin IAM berikut secara manual:
  • cloudsql.instances.connect
  • cloudsql.instances.get

Jika akun layanan Cloud Build termasuk dalam project yang berbeda dengan instance Cloud SQL, izin Cloud SQL Admin API dan IAM perlu ditambahkan untuk kedua project.

IP Pribadi

Untuk terhubung ke instance Cloud SQL Anda melalui IP pribadi, Cloud Build harus berada dalam jaringan VPC yang sama dengan instance Cloud SQL Anda. Untuk mengonfigurasinya:

  1. Siapkan koneksi pribadi antara jaringan VPC instance Cloud SQL Anda dan jaringan produsen layanan.
  2. Buat kumpulan pribadi Cloud Build.

Setelah dikonfigurasi, aplikasi Anda akan dapat terhubung langsung menggunakan alamat IP pribadi instance dan port 3306 Anda saat build Anda dijalankan di kumpulan tersebut.

Menghubungkan ke Cloud SQL

Setelah Anda mengonfigurasi Cloud Build, Anda dapat terhubung ke instance Cloud SQL Anda.

IP Publik (default)

Untuk jalur IP publik, Cloud Build mendukung soket Unix dan TCP.

Anda dapat menggunakan Proxy Auth Cloud SQL dalam langkah Cloud Build untuk mengizinkan koneksi ke database Anda. Konfigurasi ini:

  1. Mem-build container Anda dan mengirimkannya ke Container Registry.
  2. Mem-build container kedua, dengan menyalin di biner Proxy Auth Cloud SQL.
  3. Dengan menggunakan container kedua, memulai Proxy Auth Cloud SQL dan menjalankan perintah migrasi apa pun.
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

Contoh kode Cloud Build di atas menunjukkan cara Anda menjalankan skrip migrate hipotetis setelah men-deploy aplikasi contoh di atas untuk memperbarui database Cloud SQL-nya menggunakan Proxy Auth Cloud SQL dan Cloud Build. Untuk menjalankan contoh kode Cloud Build ini langkah-langkah penyiapan yang diperlukan adalah:

  1. Buat nama folder sql-proxy
  2. Buat file Dockerfile di folder sql-proxy dengan satu baris kode berikut untuk konten filenya:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Buat file cloudbuild.yaml di folder sql-proxy.
  4. Update file cloudbuild.yaml:
    1. Salin contoh kode Cloud Build di atas dan tempelkan ke file cloudbuild.yaml.
    2. Gunakan koneksi TCP atau koneksi soket Unix dengan menghapus blok kode untuk metode koneksi yang tidak digunakan.
    3. Update kode contoh _DATABASE_TYPE dalam blok substitutions: menjadi mysql.
    4. Jika Anda menggunakan koneksi TCP, perbarui kode contoh _DATABASE_PORT dalam blok substitutions: menjadi 3306, yang merupakan port yang digunakan oleh MySQL.
    5. Ganti nilai placeholder berikut dengan nilai yang digunakan dalam project Anda:
      • mydatabase
      • myuser
      • myinstance
  5. Buat secret bernama database_password di Secret Manager.
  6. Buat file skrip migrate.py di folder sql-proxy.
    • Skrip ini dapat merujuk ke variabel lingkungan berikut dan secret yang dibuat di file cloudbuild.yaml menggunakan contoh berikut:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('INSTANCE_CONNECTION_NAME')
    • Untuk merujuk variabel yang sama dari skrip Bash (misalnya: migrate.sh) gunakan contoh berikut:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. Jalankan perintah gcloud builds submit berikut untuk mem-build container dengan Proxy Auth Cloud SQL, memulai Proxy Auth Cloud SQL, dan menjalankan skrip migrate.py:

    gcloud builds submit --config cloudbuild.yaml

IP Pribadi

Untuk jalur IP pribadi, aplikasi Anda terhubung langsung ke instance melalui kumpulan pribadi. Metode ini menggunakan TCP untuk terhubung langsung ke instance Cloud SQL tanpa menggunakan Proxy Auth Cloud SQL.

Terhubung dengan TCP

Hubungkan menggunakan alamat IP pribadi instance Cloud SQL Anda sebagai host dan port 3306.

Python

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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

Untuk melihat cuplikan ini dalam konteks aplikasi web, lihat README di GitHub.

Catatan:

  • INSTANCE_CONNECTION_NAME harus direpresentasikan sebagai <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME>
  • Menggunakan argumen ipTypes=PRIVATE akan memaksa SocketFactory untuk terhubung dengan IP pribadi yang terkait dengan sebuah instance
  • Lihat persyaratan versi factory soket JDBC untuk file pom.xml di sini .


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

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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#

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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

Untuk menemukan cuplikan ini dalam konteks aplikasi web, lihat README pada 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;
    }
}

Kemudian Anda dapat membuat langkah Cloud Build untuk menjalankan kode Anda secara langsung.

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

Contoh kode Cloud Build di atas menunjukkan cara Anda menjalankan skrip migrate hipotetis setelah men-deploy aplikasi contoh di atas untuk memperbarui database Cloud SQL-nya menggunakan Cloud Build. Untuk menjalankan contoh kode Cloud Build ini langkah-langkah penyiapan yang diperlukan adalah:

  1. Buat nama folder sql-private-pool
  2. Buat file Dockerfile di folder sql-private-pool dengan satu baris kode berikut untuk konten filenya:

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. Buat file cloudbuild.yaml di folder sql-private-pool.
  4. Update file cloudbuild.yaml:
    1. Salin contoh kode Cloud Build di atas dan tempelkan ke file cloudbuild.yaml.
    2. Ganti nilai placeholder berikut dengan nilai yang digunakan dalam project Anda:
      • mydatabase
      • myuser
      • databasehost, dalam bentuk host:port.
  5. Buat secret bernama database_password di Secret Manager.
  6. Buat file skrip migrate.py di folder sql-proxy.
    • Skrip ini dapat merujuk ke variabel lingkungan berikut dan secret yang dibuat di file cloudbuild.yaml menggunakan contoh berikut:
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('DATABASE_HOST')
    • Untuk merujuk variabel yang sama dari skrip Bash (misalnya: migrate.sh) gunakan contoh berikut:
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. Jalankan perintah gcloud builds submit berikut untuk mem-build container dengan Proxy Auth Cloud SQL, memulai Proxy Auth Cloud SQL, dan menjalankan skrip migrate.py:

    gcloud builds submit --config cloudbuild.yaml

Praktik terbaik dan informasi lainnya

Anda dapat menggunakan Proxy Auth Cloud SQL saat menguji aplikasi Anda secara lokal. Lihat panduan memulai penggunaan Proxy Auth Cloud SQL mendapatkan petunjuk terperinci.

Anda juga dapat menguji menggunakan Proxy Cloud SQL melalui container docker.

Migrasi skema database

Dengan mengonfigurasi Cloud Build agar terhubung ke Cloud SQL, Anda dapat menjalankan tugas migrasi skema database di Cloud Build menggunakan kode yang sama dengan yang akan Anda deploy ke platform serverless lainnya.

Menggunakan Secret Manager

Anda dapat menggunakan Secret Manager untuk menyertakan informasi sensitif dalam build Anda.