Cloud Run 함수에서 연결

이 페이지에는 Cloud Run 함수에서 실행되는 서비스에서 Cloud SQL 인스턴스에 연결하는 방법에 대한 정보와 예시가 포함되어 있습니다.

Cloud SQL에 연결된 Cloud Run 함수 샘플 웹 애플리케이션을 실행하는 방법에 대한 단계별 안내빠른 시작: Cloud Run 함수에서 연결을 참조하세요.

Cloud SQL은 클라우드에서 관계형 데이터베이스를 설정, 유지, 관리할 수 있는 완전 관리형 데이터베이스 서비스입니다.

Cloud Run 함수는 개발자가 서버나 런타임 환경을 관리하지 않고도 Cloud 이벤트에 응답하는 단일 용도의 독립 실행형 함수를 만들 수 있는 경량형 컴퓨팅 솔루션입니다.

Cloud SQL 인스턴스 설정

  1. 아직 사용 설정하지 않은 경우에는 연결 중인 Google Cloud 프로젝트에서 Cloud SQL Admin API를 사용 설정하세요.

    Enable the API

  2. MySQL용 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 콘솔에서 인스턴스의 개요 페이지에서 이를 확인할 수 있습니다. 공개 IP 주소를 추가해야 하는 경우 공개 IP 구성을 참조하세요.
  • 인스턴스의 INSTANCE_CONNECTION_NAME을 가져옵니다. 이 값은 다음을 통해 제공됩니다.
    • 인스턴스의 개요 페이지, Google Cloud 콘솔 또는
    • gcloud sql instances describe [INSTANCE_NAME] 명령어 실행
  • 함수의 서비스 계정을 구성합니다. 승인 서비스 계정이 Cloud SQL 인스턴스와 다른 프로젝트에 속하는 경우 Cloud SQL Admin API를 사용 설정하고 아래에 나열된 IAM 권한을 두 프로젝트에 추가합니다. 서비스 계정에 Cloud SQL에 연결하기 위한 적절한 Cloud SQL 역할 및 권한이 있는지 확인합니다.
    • Cloud SQL에 연결하려면 서비스 계정에 다음 IAM 역할 중 하나가 필요합니다.
      • Cloud SQL Client(권장)
      • Cloud SQL Editor
      • Cloud SQL Admin
      또는 다음 IAM 권한을 수동으로 할당할 수도 있습니다.
      • cloudsql.instances.connect
      • cloudsql.instances.get
  • Cloud Run 함수(1세대)가 아닌 Cloud Run 함수를 사용하는 경우 다음이 필요합니다(Cloud Run 구성 참조).
    1. 처음에 함수를 배포합니다.
      Google Cloud 콘솔에서 Cloud Run 함수를 처음 만들 때는 기본 Cloud Run 서비스가 아직 생성되지 않습니다. Cloud run 함수를 배포하여 서비스가 생성될 때까지는 Cloud SQL 연결을 구성할 수 없습니다.
    2. Google Cloud 콘솔의 함수 세부정보 페이지 오른쪽 위에 있는 Cloud Run 제공 아래에서 링크를 클릭하여 기본 Cloud Run 서비스에 액세스합니다.
    3. Cloud Run 서비스 세부정보 페이지에서 새 버전 수정 및 배포 탭을 선택합니다.
    4. Cloud SQL 연결의 새 구성 설정을 위한 표준 단계를 따릅니다(구성 변경 대비).
      그러면 새 Cloud Run 버전이 생성되고 이를 명시적으로 변경하지 않는 한 이후 버전에 이 Cloud SQL 연결이 자동으로 수신됩니다.

비공개 IP

승인 서비스 계정이 Cloud SQL 인스턴스를 포함하는 것과 다른 프로젝트에 속하는 경우 다음을 수행합니다.

  • 두 프로젝트 모두에서 Cloud SQL Admin API를 사용 설정합니다.
  • Cloud SQL 인스턴스가 포함된 프로젝트의 서비스 계정에 IAM 권한을 추가합니다.
서버리스 VPC 액세스 커넥터는 비공개 IP 주소를 사용하여 VPC 네트워크에 대한 통신을 처리합니다. 비공개 IP 주소로 직접 연결하려면 다음을 수행해야 합니다.
  1. 위에서 만든 Cloud SQL 인스턴스에 비공개 IP 주소가 있는지 확인합니다. 추가해야 하는 경우 비공개 IP 구성의 안내를 참조하세요.
  2. Cloud SQL 인스턴스와 동일한 VPC 네트워크에 서버리스 VPC 액세스 커넥터를 만듭니다. 다음 조건을 참고하세요.
    • 공유 VPC를 사용하지 않는 한 커넥터는 커넥터를 사용하는 리소스와 동일한 프로젝트와 리전에 있어야 하지만 이 커넥터는 서로 다른 리전의 리소스로 트래픽을 전송할 수 있습니다.
    • 서버리스 VPC 액세스는 Cloud VPNVPC 네트워크 피어링을 통해 연결된 VPC 네트워크와의 통신을 지원합니다.
    • 서버리스 VPC 액세스는 기존 네트워크를 지원하지 않습니다.
  3. Cloud Run 함수에서 커넥터를 사용하도록 구성합니다.
  4. 인스턴스의 비공개 IP 주소와 포트 3306를 사용하여 연결합니다.

Cloud SQL에 연결

Cloud Run 함수를 구성한 후에 Cloud SQL 인스턴스에 연결할 수 있습니다.

공개 IP(기본값)

공개 IP 경로의 경우 Cloud Run 함수는 암호화를 제공하고 두 가지 방법으로 Cloud SQL 인증 프록시를 사용하여 연결합니다.

비공개 IP

비공개 IP 경로의 경우 애플리케이션은 VPC 네트워크를 통해 인스턴스에 직접 연결됩니다. 이 메서드는 TCP를 사용하여 Cloud SQL 인증 프록시를 사용하지 않고 Cloud SQL 인스턴스에 직접 연결합니다.

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

자바

웹 애플리케이션의 컨텍스트에서 이 스니펫을 보려면 GitHub의 README를 참조하세요.

참고:

  • INSTANCE_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 {

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

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 인증 프록시를 사용할 수 있습니다. 자세한 내용은 빠른 시작: Cloud SQL 인증 프록시 사용을 참조하세요.

연결 풀

기본 데이터베이스에 대한 연결은 데이터베이스 서버 자체에서 또는 Cloud Run 함수의 기반이 되는 인프라에서 중단될 수 있습니다. 클라이언트 연결이 중단되었을 때 자동으로 다시 연결하는 연결 풀을 지원하는 클라이언트 라이브러리를 사용하는 것이 좋습니다. 또한 함수의 후속 호출에 같은 연결을 다시 사용할 가능성을 높이고 인스턴스가 제거될 때 해당 연결이 자연스럽게 종료(자동 축소)되도록 전역 범위 연결 풀을 사용하는 것이 좋습니다. 연결 풀 사용 방법에 대한 자세한 예시는 데이터베이스 연결 관리를 참조하세요.

연결 한도

Cloud SQL은 동시 연결 수에 최대 한도를 적용하며 이러한 한도는 선택한 데이터베이스 엔진에 따라 달라질 수 있습니다(Cloud SQL 할당량 및 한도 참조). Cloud Run 함수와의 연결을 사용하는 것이 좋지만 최대 연결 수를 1로 설정하는 것이 중요합니다.

가능한 경우 데이터베이스에 액세스해야 하는 함수의 연결 풀만 초기화하도록 주의해야 합니다. 일부 연결 풀은 사전에 연결을 생성하므로 초과 리소스를 소비하고 연결 제한에 도달할 수 있습니다. 이런 이유로 지연된 초기화를 사용하여 필요할 때까지 연결 풀 생성을 지연하고 사용되는 함수에만 연결 풀을 포함하는 것이 좋습니다.

연결 수를 제한하는 방법에 대한 자세한 예시는 데이터베이스 연결 관리를 참조하세요.

API 할당량 한도

Cloud Run 함수는 Cloud SQL Admin API를 사용하는 Cloud SQL 인증 프록시를 사용하여 연결하는 메커니즘을 제공합니다. API 할당량 한도가 Cloud SQL 인증 프록시에 적용됩니다. 사용된 Cloud SQL Admin API 할당량은 대략적으로 구성된 Cloud SQL 인스턴스 수의 2배에 배포된 총 함수 수를 곱한 값입니다. 예상되는 API 할당량을 수정하려면 최대 동시 호출 수를 설정하면 됩니다. 또한 Cloud Run 함수는 100초마다 허용되는 API 호출 수에 비율 제한을 적용합니다.

다음 단계