Cloud Build から接続する

このページには、Cloud Build で実行しているサービスから Cloud SQL インスタンスに接続するための情報と例が含まれています。

Cloud SQL は、クラウド内のリレーショナル データベースの設定、維持、管理に役立つフルマネージド データベース サービスです。

Cloud Build は、Google Cloud インフラストラクチャでビルドを実行するサービスです。

Cloud SQL インスタンスを設定する

  1. 接続元の Google Cloud プロジェクトで Cloud SQL Admin API を有効にします(まだ有効にしていない場合)。

    Enable the API

  2. Cloud SQL for SQL Server インスタンスを作成します。レイテンシを短縮して、ネットワーク費用を回避し、リージョン間の障害リスクを軽減するには、Cloud Run サービスと同じリージョンで Cloud SQL のインスタンス ロケーションを選択することをおすすめします。

    Cloud SQL は、デフォルトで新しいインスタンスにパブリック IP アドレスを割り振ります。 プライベート IP アドレスを割り当てることもできます。両方の接続オプションの詳細については、接続の概要ページをご覧ください。

Cloud Build を構成する

Cloud Build を構成する手順は、Cloud SQL インスタンスに割り当てられた IP アドレスのタイプによって異なります。

パブリック IP(デフォルト)

Cloud Build サービス アカウントCloud SQL インスタンスへの接続に必要な IAM ロールと権限があることを確認します。Cloud Build サービス アカウントは、Google Cloud コンソールの [IAM] ページにプリンシパル [YOUR-PROJECT-NUMBER]@cloudbuild.gserviceaccount.com として表示されます。

Google Cloud コンソールでこのサービス アカウントを表示するには、[Google 提供のロール付与を含める] チェックボックスをオンにします。

Cloud Build サービス アカウントに、次の IAM ロールのいずれかが必要です。

  • Cloud SQL Client(推奨)
  • Cloud SQL Admin
または、次の IAM 権限を手動で割り当てることもできます。
  • cloudsql.instances.connect
  • cloudsql.instances.get

Cloud Build サービス アカウントが Cloud SQL インスタンスとは異なるプロジェクトに属している場合、Cloud SQL Admin API と IAM の権限を両方のプロジェクトに追加する必要があります。

プライベート IP

プライベート IP 経由で Cloud SQL インスタンスに接続するには、Cloud Build が Cloud SQL インスタンスと同じ VPC ネットワークに接続されている必要があります。この構成を行うには:

  1. Cloud SQL インスタンスの VPC ネットワークとサービス プロデューサー ネットワークの間にプライベート接続を設定します。
  2. Cloud Build プライベート プールを作成します。

構成が完了すると、プールでビルドが実行されたときに、アプリケーションがインスタンスのプライベート IP アドレスとポート 1433 を使用して直接接続できるようになります。

Cloud SQL への接続

Cloud Build を構成すると、Cloud SQL インスタンスに接続できます。

パブリック IP(デフォルト)

パブリック IP パスの場合は、Cloud Build が TCP ソケットをサポートします。

データベースへの接続は、Cloud Build の手順で Cloud SQL Auth Proxy を使用して許可できます。この構成では、次の処理を行います。

  1. コンテナをビルドして、Container Registry に push します。
  2. 2 つ目のコンテナをビルドし、このときに Cloud SQL Auth Proxy バイナリをコピーします。
    • Cloud Build によってビルドされたコンテナは、レジストリに push する必要はありません。ビルドの完了時に破棄されます。
  3. 2 つ目のコンテナを使用して、Cloud SQL Auth Proxy を起動して任意の移行コマンドを実行します。
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

この Cloud Build コードサンプルは、前述のサンプルアプリをデプロイした後にその Cloud SQL データベースを更新するための架空の migrate スクリプトを、Cloud SQL Auth Proxy と Cloud Build を使用して実行する方法を示すものです。この Cloud Build コードサンプルを実行するには、次の設定手順が必要です。

  1. sql-proxy という名前のフォルダを作成します
  2. 次のコード行を使用して、sql-proxy フォルダに Dockerfile ファイルを作成します。このファイルの内容は次のとおりです。

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. sql-proxy フォルダに cloudbuild.yaml ファイルを作成します。
  4. cloudbuild.yaml ファイルを更新します。
    1. 上記の Cloud Build サンプルコードをコピーし、cloudbuild.yaml ファイルに貼り付けます。
    2. substitutions: ブロック内のサンプルコード _DATABASE_TYPEmssql に更新します。
    3. substitutions: ブロック内のサンプルコード _DATABASE_PORT1433 に変更します。これは SQL Server で使用されるポートです。
    4. 次のプレースホルダ値を、プロジェクトで使用されている値に置き換えます。
      • mydatabase
      • myuser
      • myinstance
  5. Secret Managerdatabase_password という名前のシークレットを作成します。
  6. sql-proxy フォルダに migrate.py スクリプト ファイルを作成します。
    • このスクリプトでは、以下の例を使用して、cloudbuild.yaml ファイルに作成された次の環境変数とシークレットを参照できます。
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('INSTANCE_CONNECTION_NAME')
    • Bash スクリプトから同じ変数(例: migrate.sh)を参照するには、次の例を使用します。
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $INSTANCE_CONNECTION_NAME
  7. 次の gcloud builds submit コマンドを実行して、Cloud SQL Auth Proxy を含むコンテナをビルドし、Cloud SQL Auth Proxy を起動して、migrate.py スクリプトを実行します。

    gcloud builds submit --config cloudbuild.yaml

プライベート IP

プライベート IP パスの場合、アプリケーションはプライベート プールを介してインスタンスに直接接続します。この方法では、Cloud SQL Auth Proxy を使用せずに、TCP を使用して Cloud SQL インスタンスに直接接続します。

TCP による接続

Cloud SQL インスタンスのプライベート IP アドレスをホストとポートの 1433 として使用して接続します。

Python

ウェブ アプリケーションのコンテキストでのこのスニペットの使用については、GitHub の README をご覧ください。

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

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

注:

  • CLOUD_SQL_CONNECTION_NAME は <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME> のように指定する必要があります。
  • 引数 ipTypes=PRIVATE を使用すると、SocketFactory はインスタンスに関連付けられたプライベート IP を使用して接続するようになります。
  • pom.xml ファイルの JDBC ソケット ファクトリ バージョン要件については、こちらをご覧ください。


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

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

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

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

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#

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

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

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

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

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

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

その後、Cloud Build のステップを作成して、コードを直接実行できます。

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

この Cloud Build コードサンプルは、前述のサンプルアプリをデプロイした後にその Cloud SQL データベースを更新するための架空の移行スクリプトを、Cloud Build を使用して実行する方法を示すものです。この Cloud Build コードサンプルを実行するには、次の設定手順が必要です。

  1. sql-private-pool という名前のフォルダを作成します
  2. 次のコード行を使用して、sql-private-pool フォルダに Dockerfile ファイルを作成します。このファイルの内容は次のとおりです。

    FROM gcr.io/gcp-runtimes/ubuntu_20_0_4

  3. sql-private-pool フォルダに cloudbuild.yaml ファイルを作成します。
  4. cloudbuild.yaml ファイルを更新します。
    1. 上記の Cloud Build サンプルコードをコピーし、cloudbuild.yaml ファイルに貼り付けます。
    2. 次のプレースホルダ値を、プロジェクトで使用されている値に置き換えます。
      • mydatabase
      • myuser
      • databasehost。形式は host:port です。
  5. Secret Managerdatabase_password という名前のシークレットを作成します。
  6. sql-proxy フォルダに migrate.py スクリプト ファイルを作成します。
    • このスクリプトでは、以下の例を使用して、cloudbuild.yaml ファイルに作成された次の環境変数とシークレットを参照できます。
      • os.getenv('DATABASE_NAME')
      • os.getenv('DATABASE_USER')
      • os.getenv('DATABASE_PASS')
      • os.getenv('DATABASE_HOST')
    • Bash スクリプトから同じ変数(例: migrate.sh)を参照するには、次の例を使用します。
      • $DATABASE_NAME
      • $DATABASE_USER
      • $DATABASE_PASS
      • $DATABASE_HOST
  7. 次の gcloud builds submit コマンドを実行して、Cloud SQL Auth Proxy を含むコンテナをビルドし、Cloud SQL Auth Proxy を起動して、migrate.py スクリプトを実行します。

    gcloud builds submit --config cloudbuild.yaml

ベスト プラクティスとその他の情報

アプリケーションをローカルでテストする場合は、Cloud SQL Auth Proxy を使用できます。詳細な手順については、Cloud SQL Auth Proxy を使用するためのクイックスタートをご覧ください。

また、Docker コンテナ経由で Cloud SQL Proxy を使用してもテストできます。

データベース スキーマの移行

Cloud SQL に接続するように Cloud Build を構成すると、他のサーバーレス プラットフォームにデプロイするコードと同じコードを使用して、Cloud Build でデータベース スキーマの移行タスクを実行できます。

Secret Manager の使用

Secret Manager を使用すると、ビルドに機密情報を含めることができます。