從 Cloud Run 函式連線

本頁面包含從在 Cloud Run 函式中執行的服務連線至 Cloud SQL 執行個體的資訊與範例。

如需有關執行 Cloud Run 函式範例網頁應用程式 (已連結至 Cloud SQL) 的逐步操作說明,請參閱從 Cloud Run 函式連線的快速入門指南

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

Cloud Run 函式是一種輕量運算解決方案,可讓開發人員建立獨立的單一用途函式來回應雲端事件,而不需要管理伺服器或執行階段環境。

設定 Cloud SQL 執行個體

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

    Enable the API

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

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

設定 Cloud Run 函式

設定 Cloud Run 函式的步驟取決於您指派給 Cloud SQL 執行個體的 IP 位址類型。

公開 IP (預設)

如要設定 Cloud Run 函式,以便連線至 Cloud SQL 執行個體,請按照下列步驟操作:

  • 確認上述建立的執行個體是否有公開 IP 位址。您可以在 Google Cloud 主控台的「Instance Overview」頁面上確認這項資訊。如果您需要新增公開 IP 位址,請參閱「設定公開 IP」。
  • 取得例項的 INSTANCE_CONNECTION_NAME。這個值可用於:
    • 在執行個體的「Overview」頁面 (Google Cloud 控制台) 中,
    • 執行下列指令: gcloud sql instances describe [INSTANCE_NAME]
  • 設定函式的服務帳戶。如果授權服務帳戶屬於與 Cloud SQL 執行個體不同的專案,則您必須啟用 Cloud SQL Admin API,並將 Cloud SQL Client IAM 角色新增至兩個專案。
  • 確認服務帳戶具備這個角色,以便帳戶連線至 Cloud SQL。
  • 如果您使用的是 Cloud Run 函式,而非 Cloud Run 函式 (第 1 代),則必須符合下列條件 (另請參閱「 設定 Cloud Run」):
    1. 初始部署函式。
      當您在 Google Cloud 控制台中首次開始建立 Cloud Run 函式時,系統尚未建立基礎 Cloud Run 服務。您必須先建立這項服務 (部署 Cloud Run 函式),才能設定 Cloud SQL 連線。
    2. 在 Google Cloud 控制台的「Function details」頁面右上方的「Powered by Cloud Run」下方,按一下連結即可存取基礎的 Cloud Run 服務。
    3. 在 Cloud Run「Service details」(服務詳細資料) 頁面中,選取「Edit and deploy new revision」(編輯並部署新修訂版本) 分頁。
    4. 如要為 Cloud SQL 連線設定新配置,請按照標準步驟 (如任何設定變更的情況) 操作。
      這會建立新的 Cloud Run 修訂版本,後續修訂版本會自動接收此 Cloud SQL 連線,除非您明確變更。

私人 IP

如果授權服務帳戶屬於與 Cloud SQL 執行個體所在專案不同的專案,請執行下列操作:

  • 在兩個專案中啟用 Cloud SQL Admin API。
  • 針對含有 Cloud SQL 執行個體的專案中服務帳戶,新增 IAM 權限
無伺服器虛擬私人雲端存取連接器會使用私人 IP 位址,處理與虛擬私人雲端網路的通訊。如要直接使用私人 IP 位址連線,您必須執行下列操作:
  1. 請確認先前建立的 Cloud SQL 執行個體具有私人 IP 位址。如需新增私人 IP,請參閱「設定私人 IP」一文瞭解操作方式。
  2. 建立無伺服器虛擬私有雲存取連接器,並與 Cloud SQL 執行個體位於相同的虛擬私有雲網路。請注意下列條件:
    • 除非您使用 共用虛擬私有雲,否則連接器所在的專案和地區,必須與使用該連接器的資源相同,但連接器可將流量傳送至位於不同地區的資源。
    • 針對透過 Cloud VPN虛擬私人雲端網路對等互連連線的虛擬私人雲端網路,無伺服器虛擬私人雲端存取可支援與其通訊。
    • 無伺服器虛擬私人雲端存取不支援舊版網路
  3. 設定 Cloud Run 函式以使用連接器。
  4. 使用執行個體的私人 IP 位址和通訊埠 5432 連線。

連線至 Cloud SQL

設定 Cloud Run 函式後,您就可以連線至 Cloud SQL 執行個體。

公開 IP (預設)

針對公開 IP 路徑,Cloud Run 函式會提供加密功能,並透過 Cloud SQL Auth Proxy 以兩種方式建立連線:

私人 IP

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

連線至 TCP

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

Python

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

import os
import ssl

import sqlalchemy


def connect_tcp_socket() -> sqlalchemy.engine.base.Engine:
    """Initializes a TCP connection pool for a Cloud SQL instance of Postgres."""
    # 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. 5432

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # postgresql+pg8000://<db_user>:<db_pass>@<db_host>:<db_port>/<db_name>
        sqlalchemy.engine.url.URL.create(
            drivername="postgresql+pg8000",
            username=db_user,
            password=db_pass,
            host=db_host,
            port=db_port,
            database=db_name,
        ),
        # ...
    )
    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();

    // The following URL is equivalent to setting the config options below:
    // jdbc:postgresql://<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:postgresql://%s:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME));
    config.setUsername(DB_USER); // e.g. "root", "postgres"
    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 Knex = require('knex');
const fs = require('fs');

// createTcpPool initializes a TCP connection pool for a Cloud SQL
// instance of Postgres.
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 = {
    client: 'pg',
    connection: {
      host: process.env.INSTANCE_HOST, // e.g. '127.0.0.1'
      port: process.env.DB_PORT, // e.g. '5432'
      user: process.env.DB_USER, // e.g. 'my-user'
      password: process.env.DB_PASS, // e.g. 'my-user-password'
      database: process.env.DB_NAME, // e.g. 'my-database'
    },
    // ... Specify additional properties here.
    ...config,
  };
  // Establish a connection to the database.
  return Knex(dbConfig);
};

Go

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

package cloudsql

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	// Note: If connecting using the App Engine Flex Go runtime, use
	// "github.com/jackc/pgx/stdlib" instead, since v5 requires
	// Go modules which are not supported by App Engine Flex.
	_ "github.com/jackc/pgx/v5/stdlib"
)

// connectTCPSocket initializes a TCP connection pool for a Cloud SQL
// instance of Postgres.
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'
		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. '5432'
		dbName    = mustGetenv("DB_NAME")       // e.g. 'my-database'
	)

	dbURI := fmt.Sprintf("host=%s user=%s password=%s port=%s database=%s",
		dbTCPHost, dbUser, dbPwd, dbPort, dbName)


	// dbPool is the pool of database connections.
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}

	// ...

	return dbPool, nil
}

PHP

如要在網頁應用程式內容中查看此程式碼片段,請參閱 GitHub 上的 README 檔案

namespace Google\Cloud\Samples\CloudSQL\Postgres;

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('pgsql: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/postgres/connect-external-app',
                    $e->getMessage()
                ),
                $e->getCode(),
                $e
            );
        }

        return $conn;
    }
}

最佳做法和其他資訊

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

連線集區

底層資料庫的連線可能會遭到中斷,原因可能是資料庫伺服器本身,或是 Cloud Run 函式底層的基礎架構。建議您使用支援連線集區的用戶端程式庫,以便自動重新連線中斷的用戶端連線。此外,建議您使用全域範圍的連線集區,提高函式在後續函式呼叫中重複使用相同連線的可能性,並在執行個體遭到逐出 (自動縮減) 時自然關閉連線。如需進一步瞭解如何使用連線集區的範例,請參閱「管理資料庫連線」。

連線限制

Cloud SQL 會對並行連線設下上限,這些限制可能會因所選資料庫引擎而異 (請參閱「Cloud SQL 配額與限制」)。建議您使用與 Cloud Run 函式連線的連線,但請務必將連線數量上限設為 1。

請盡可能只為需要存取資料庫的函式初始化連線集區。部分連線集區會預先建立連線,這可能會耗用過多資源,並計入連線限制。因此,建議您使用 延遲初始化功能,將建立連線集區的作業延後到需要時再執行,並只在使用連線集區的函式中加入連線集區。

如需限制連線數量的詳細範例,請參閱「管理資料庫連線」。

API 配額限制

Cloud Run 函式提供使用 Cloud SQL 驗證 Proxy 的連線機制,該 Proxy 會使用 Cloud SQL Admin API。 API 配額限制適用於 Cloud SQL 驗證 Proxy。使用的 Cloud SQL Admin API 配額約為所設定 Cloud SQL 執行個體數量乘以部署的函式總數的兩倍。您可以設定並行叫用次數上限,藉此修改預期的 API 配額用量。Cloud Run 函式也會對每 100 秒允許的 API 呼叫次數設定頻率限制

後續步驟