從 App Engine 標準環境連線

本頁面提供相關資訊和範例,說明如何從 App Engine 標準環境中執行的服務,連線至 Cloud SQL 執行個體。

Cloud SQL 是全代管資料庫服務,可協助您在雲端設定、維護及管理關聯式資料庫。

App Engine 是全代管的無伺服器平台,適用於大規模開發和託管網頁應用程式。您可以選擇多種熱門語言、程式庫和框架來開發應用程式,然後讓 App Engine 負責佈建伺服器,並視需求調整應用程式執行個體資源。

設定 Cloud SQL 執行個體

  1. 在連線來源的 Google Cloud 專案中啟用 Cloud SQL Admin API (如果尚未啟用):

    啟用 API 所需的角色

    如要啟用 API,您必須具備 serviceusage.services.enable 權限。如果您建立了專案,可能已透過擁有者角色 (roles/owner) 取得這項權限。否則,您可以透過服務使用情形管理員角色 (roles/serviceusage.serviceUsageAdmin) 取得這項權限。瞭解如何授予角色。

    啟用 API

  2. 建立 MySQL 適用的 Cloud SQL 執行個體。建議您選擇與 App Engine 標準環境服務位於相同區域的 Cloud SQL 執行個體位置,以縮短延遲時間、避免產生部分網路費用,並降低跨區域故障風險。

    根據預設,Cloud SQL 會為新執行個體指派公開 IP 位址。您也可以選擇指派私人 IP 位址。如要進一步瞭解兩者的連線選項,請參閱「 連線總覽」頁面。

  3. 建立執行個體時,您可以選擇執行個體的 伺服器憑證 (CA) 階層,然後將階層設為執行個體的 serverCaMode。如要從 App Engine 標準環境網頁應用程式連線至執行個體,必須選取個別執行個體 CA 選項 (GOOGLE_MANAGED_INTERNAL_CA) 做為執行個體的伺服器 CA 模式。

設定 App Engine 標準環境

設定 App Engine 標準環境的步驟,取決於您指派給 Cloud SQL 執行個體的 IP 位址類型。

公開 IP (預設)

如要設定 App Engine 標準環境,啟用使用公開 IP 連至 Cloud SQL 執行個體的功能,請按照下列步驟操作:

  • 確認執行個體具有公開 IP 位址。您可以在Google Cloud 控制台的執行個體「Overview」(總覽) 頁面中確認這項資訊。如需新增,請參閱設定公開 IP 頁面的操作說明。
  • 取得執行個體的 INSTANCE_CONNECTION_NAME。您可以在Google Cloud 控制台中執行下列 gcloud sql instances describe 指令,或在執行個體的「Overview」(總覽) 頁面中找到這個值:
    gcloud sql instances describe INSTANCE_NAME
       
    將 INSTANCE_NAME 替換為 Cloud SQL 執行個體的名稱。
  • 將 INSTANCE_NAME 變數替換為執行個體名稱。
  • 確認應用程式用來驗證 Cloud SQL 呼叫的服務帳戶具備 Cloud SQL Client IAM 角色。

    如需將 IAM 角色新增至服務帳戶的詳細操作說明,請參閱「將角色授予服務帳戶」。

使用 App Engine 時,系統會自動建立預設 App Engine 服務帳戶。服務帳戶身分的格式為 PROJECT_ID@appspot.gserviceaccount.com。

如果授權服務帳戶與 Cloud SQL 執行個體屬於不同專案,則必須為這兩項專案新增 Cloud SQL Admin API 和 IAM 權限。

私人 IP

如果授權服務帳戶和 Cloud SQL 執行個體位於不同專案,請執行下列操作:

  • 在兩項專案中啟用 Cloud SQL Admin API。
  • 在含有 Cloud SQL 執行個體的專案中,為服務帳戶新增 IAM 權限。
無伺服器 VPC 存取連接器會使用私人 IP 位址,處理與虛擬私有雲網路之間的通訊。 如要直接使用私人 IP 位址連線,請務必執行下列操作:
  1. 確認先前建立的 Cloud SQL 執行個體具有私人 IP 位址。如需新增,請參閱設定私人 IP 的操作說明。
  2. 在 Cloud SQL 執行個體所在的虛擬私有雲網路中,建立無伺服器 VPC 存取連接器。請注意下列條件:
    • 除非您使用 Shared VPC,否則連接器必須與使用連接器的資源位於相同專案和區域,但可以將流量傳送至不同區域的資源。
    • 無伺服器 VPC 存取支援與使用 Cloud VPN 和虛擬私有雲網路對接連線的虛擬私有雲網路通訊。
    • 無伺服器 VPC 存取不支援舊版網路。
  3. 設定 App Engine 標準環境,以便使用連接器。
  4. 使用執行個體的私人 IP 位址和通訊埠 3306 進行連線。

連線至 Cloud SQL

設定 App Engine 標準環境後,即可連線至 Cloud SQL 執行個體。

公開 IP (預設)

如果是公開 IP 路徑,您可以透過下列兩種方式,設定 App Engine 標準環境使用 Cloud SQL Auth Proxy 進行加密:

私人 IP

如果是私人 IP 路徑,應用程式會透過虛擬私有雲網路直接連線至執行個體。這個方法會使用 TCP 直接連線至 Cloud SQL 執行個體,不使用 Cloud SQL Auth Proxy。

使用 TCP 連線

使用 Cloud SQL 執行個體的私人 IP 位址做為主機和通訊埠 3306 進行連線。

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

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

注意:


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

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

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

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

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#

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

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;

        }
    }
}

小茹

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

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

如要查看網頁應用程式中的程式碼片段,請參閱 GitHub 上的 README。

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

最佳做法和其他資訊

在本機測試應用程式時,可以使用 Cloud SQL Auth Proxy。如需詳細操作說明,請參閱快速入門導覽課程:使用 Cloud SQL Auth Proxy。

連線集區

基礎資料庫的連線可能會遭到資料庫伺服器本身或基礎架構捨棄。為緩解這個問題,建議您使用支援連線集區和自動重新連線的用戶端程式庫。

連線限制

在標準環境中執行的每個 App Engine 執行個體,與執行個體的並行連線數不得超過 100 個。如果是 PHP 5.5 應用程式,上限為 60 個並行連線。這項限制適用於每個應用程式執行個體。也就是說,App Engine 應用程式的每個執行個體都可以與資料庫建立這麼多連線,而且隨著應用程式擴充,每個部署作業的連線總數也會增加。詳情請參閱「縮放元素」。

您可以使用連線集區,限制每個執行個體使用的連線數上限。如需如何限制連線數量的詳細範例,請參閱「 管理資料庫連線」頁面。

App Engine 應用程式會根據用量和環境,受到要求時間限制。詳情請參閱 App Engine 標準和 彈性環境中的執行個體管理方式。

API 配額限制

App Engine 提供使用 Cloud SQL Auth Proxy 連線的機制,該 Proxy 會使用 Cloud SQL Admin API。Cloud SQL Auth Proxy 適用 API 配額限制。Cloud SQL Admin API 啟動時,會使用兩個配額,之後平均每小時使用兩個配額。預設配額為每位使用者每分鐘 180 個。App Engine 應用程式也須遵守其他配額和限制,詳情請參閱「 App Engine 配額」頁面。