從 App Engine 彈性環境連線

本頁面包含從在 App Engine 彈性環境中執行的服務連線至 Cloud SQL 執行個體的資訊與範例。

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

App Engine 是全代管的無伺服器平台,適用於大規模開發及託管網頁應用程式。您可以選擇多種常見的語言、程式庫和架構來開發應用程式,然後讓 App Engine 負責根據需求佈建伺服器,以及擴充應用程式執行個體。

設定 Cloud SQL 執行個體

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

    Enable the API

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

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

設定 App Engine 彈性環境

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

公開 IP (預設)

如要設定 App Engine 彈性環境,以便連線至 Cloud SQL 執行個體,請按照下列步驟操作:

  1. 請確認執行個體有公開 IP 位址。您可以在 Google Cloud 主控台的執行個體「總覽」頁面中確認這項資訊。如需新增,請參閱「設定公開 IP 頁面」瞭解操作方式。
  2. 取得執行個體的 INSTANCE_CONNECTION_NAME。您可以在 Google Cloud 主控台的「總覽」頁面中找到此值,也可以執行下列 gcloud sql instances describe 指令來取得:
    gcloud sql instances describe INSTANCE_NAME
       
    INSTANCE_NAME 改成 Cloud SQL 執行個體的名稱。
  3. 請確認應用程式用於驗證 Cloud SQL 呼叫的服務帳戶具備 Cloud SQL Client IAM 角色

    如需將身分與存取權管理角色新增至服務帳戶的詳細操作說明,請參閱「為服務帳戶授予角色」一文。

根據預設,應用程式會使用 App Engine 彈性環境服務帳戶授權連線。服務帳戶的格式為 PROJECT_ID@appspot.gserviceaccount.com

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

  • 使用最合適的選項更新專案的 app.yaml 檔案。您可以使用以逗號分隔的例項清單,一次指定多個選項。

    啟用 TCP 通訊埠

    如要啟用本機 TCP 通訊埠,請根據您要連線至一個或多個例項,在專案的 app.yaml 檔案中新增下列其中一項:
    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME=tcp:PORT
    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME_1=tcp:PORT_1,INSTANCE_CONNECTION_NAME_2=tcp:PORT_2,...
  • 私人 IP

    如要透過私人 IP 連線至 Cloud SQL 執行個體,App Engine 彈性環境部署必須與 Cloud SQL 執行個體位於相同的虛擬私有雲網路。如要瞭解如何為部署作業指定 VPC 網路,請參閱「 網路設定」的設定說明文件。

    部署完成後,應用程式就能直接使用執行個體的私人 IP 位址和通訊埠 1433 進行連線。

    連線至 Cloud SQL

    設定 App Engine 彈性環境後,您就可以連線至 Cloud SQL 執行個體。

    公開 IP (預設)

    針對公開 IP 路徑,App Engine 彈性環境會提供加密功能,並透過 Cloud SQL 驗證 Proxy 以兩種方式建立連線:

    私人 IP

    連線至 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
    • 請參閱 這裡 的 JDBC 通訊端處理站版本需求,瞭解 pom.xml 檔案的相關規定。

    
    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 SQL 驗證 Proxy。如需詳細操作說明,請參閱快速入門導覽課程:使用 Cloud SQL 驗證 Proxy

    連線集區

    資料庫伺服器本身或基礎架構可能會中斷與基礎資料庫的連線。為減輕這種情況,建議您使用支援連線集區和自動重新連線的用戶端程式庫。

    連線限制

    以 App Engine 標準環境來說,當中運作的執行個體與其他執行個體之間的並行連線數不得超過 100 個。如果是以 PHP 5.5 編寫而成的應用程式,則上限為 60 個並行連線。這項限制適用於每個應用程式執行個體,也就是說,App Engine 應用程式的每個執行個體與資料庫之間的連線數不得超過這個數量。如果擴充執行個體,每項部署作業的總連線數可隨之增加。詳情請參閱「調整元素的資源配置」。

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

    App Engine 應用程式必須符合要求時間限制,實際情況則視用量和環境而定。詳情請參閱 App Engine 標準環境 標準 彈性環境中的執行個體管理方式。

    API 配額限制

    App Engine 提供一種機制,可使用 Cloud SQL 驗證 Proxy 進行連線,而這項 Proxy 會使用 Cloud SQL Admin API。 API 配額限制適用於 Cloud SQL 驗證 Proxy。Cloud SQL Admin API 啟動時,會使用兩個配額單位,之後每小時平均使用兩個配額單位。預設配額為每位使用者每分鐘 180 次。App Engine 應用程式也必須符合其他配額與限制的規定,詳情請參閱 App Engine 配額頁面。