התחברות מפונקציות Cloud Run

בדף הזה מוסבר איך להתחבר למכונה של Cloud SQL משירות שפועל בפונקציות Cloud Run, ומוצגות דוגמאות.

להוראות מפורטות להרצת אפליקציית אינטרנט לדוגמה של פונקציות Cloud Run שמחוברת ל-Cloud SQL, קראו את המדריך למתחילים להתחברות מפונקציות Cloud Run.

‫Cloud SQL הוא שירות מנוהל של מסד נתונים, שבעזרתו אפשר ליצור, לתחזק ולנהל מסדי נתונים רלציוניים בענן.

‫פונקציות Cloud Run הן פתרון מחשוב קל משקל למפתחים, שמאפשר ליצור פונקציות עצמאיות למטרה יחידה שמגיבות לאירועים ב-Cloud, בלי צורך לנהל שרת או סביבת זמן ריצה.

הגדרת מכונה של Cloud SQL

  1. מפעילים את Cloud SQL Admin API בפרויקט ב- Google Cloud שממנו מתחברים, אם עדיין לא עשיתם את זה:

    התפקידים שנדרשים כדי להפעיל ממשקי API

    כדי להפעיל ממשקי API, צריך את ההרשאה serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד "בעלים" (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד "אדמין בממשק 'שימוש בשירות'" (roles/serviceusage.serviceUsageAdmin). איך מקצים תפקידים

    להפעלת ה-API

  2. יוצרים מכונה של Cloud SQL ל-MySQL. כדי לשפר את זמן האחזור, להימנע מעלויות מסוימות של רשתות ולהפחית את הסיכונים לכשלים בין אזורים, מומלץ לבחור מיקום למכונה של Cloud SQL באותו אזור שבו נמצא השירות של פונקציות Cloud Run.

    כברירת מחדל, Cloud SQL מקצה כתובת IP ציבורית למכונה חדשה. אפשר גם להקצות כתובת IP פרטית. למידע נוסף על אפשרויות הקישוריות של שניהם, קראו את הדף סקירה כללית על התחברות.

  3. כשיוצרים את המכונה, אפשר לבחור את ההיררכיה של אישור השרת (CA) של המכונה, ואז להגדיר את ההיררכיה בתור serverCaMode של המכונה. כדי להתחבר למכונות מאפליקציות אינטרנט, צריך לבחור באפשרות של רשות אישורים לכל מכונה (GOOGLE_MANAGED_INTERNAL_CA) כמצב של רשות האישורים בשרת.

הגדרת פונקציות Cloud Run

השלבים להגדרת פונקציות Cloud Run משתנים בהתאם לסוג כתובת ה-IP שהקציתם למכונה של Cloud SQL.

כתובת IP ציבורית (ברירת מחדל)

כדי להגדיר פונקציות Cloud Run שיאפשרו חיבורים למכונה של Cloud SQL:

  • מוודאים שלמכונה שנוצרה יש כתובת IP ציבורית. אפשר לוודא את זה בדף Overview של המכונה במסוףGoogle Cloud . אם אתם צריכים להוסיף כתובת IP ציבורית, קראו את המאמר הגדרת כתובת IP ציבורית.
  • משיגים את ה-INSTANCE_CONNECTION_NAME של המכונה. הערך הזה זמין:
    • בדף Overview של המכונה במסוףGoogle Cloud , או
    • מריצים את הפקודה הבאה: gcloud sql instances describe [INSTANCE_NAME]
  • מגדירים את חשבון השירות לפונקציה. אם חשבון השירות שנותן הרשאה שייך לפרויקט אחר מזה של המכונה של Cloud SQL, צריך להפעיל את Cloud SQL Admin API ולהוסיף את תפקיד ה-IAM Cloud SQL Client לשני הפרויקטים.
  • מוודאים שלחשבון השירות יש את התפקיד הזה, כדי שהחשבון יוכל להתחבר ל-Cloud SQL.
  • אם אתם משתמשים ב פונקציות Cloud Run ולא בפונקציות Cloud Run (דור ראשון), אתם צריכים את ההרשאות הבאות (ראו גם הגדרת Cloud Run):
    1. פורסים את הפונקציה בפעם הראשונה.
      כשמתחילים ליצור פונקציית Cloud Run במסוף Google Cloud , שירות Cloud Run הבסיסי עדיין לא נוצר. אי אפשר להגדיר חיבור ל-Cloud SQL עד שהשירות הזה נוצר (על ידי פריסת פונקציית Cloud Run).
    2. במסוף Google Cloud , בפינה הימנית העליונה של הדף Function details, בקטע Powered by Cloud Run, לוחצים על הקישור כדי לגשת לשירות Cloud Run הבסיסי.
    3. בדף Service details של Cloud Run, בוחרים בכרטיסייה Edit and deploy new revision.
    4. כדי להגדיר חיבור חדש ל-Cloud SQL, פועלים לפי השלבים הרגילים (כמו בכל שינוי בהגדרות).
      הפעולה הזו יוצרת גרסה חדשה של Cloud Run, והגרסאות הבאות מקבלות את החיבור הזה ל-Cloud SQL באופן אוטומטי, אלא אם משנים אותו באופן מפורש.

כתובת IP פרטית

אם חשבון השירות המאשר שייך לפרויקט אחר מזה שמכיל את המכונה של Cloud SQL, מבצעים את הפעולות הבאות:

  • מפעילים את Cloud SQL Admin API בשני הפרויקטים.
  • מוסיפים את הרשאות ה-IAM לחשבון השירות בפרויקט שמכיל את המכונה של Cloud SQL.
המחבר של חיבור לרשת (VPC) מאפליקציית serverless משתמש בכתובות IP פרטיות כדי לטפל בתקשורת עם רשת ה-VPC. כדי להתחבר ישירות לכתובות IP פרטיות, צריך לבצע את הפעולות הבאות:
  1. מוודאים שלמכונה של Cloud SQL שיצרתם קודם יש כתובת IP פרטית. אם אתם צריכים להוסיף כתובת IP פרטית, תוכלו להיעזר בהוראות שבמאמר הגדרת כתובת IP פרטית.
  2. יוצרים מחבר של גישה ל-VPC ללא שרת (Serverless) באותה רשת VPC שבה נמצאת המכונה של Cloud SQL. שימו לב לתנאים הבאים:
    • אם אתם לא משתמשים ב- VPC משותף, המחבר צריך להיות באותו פרויקט ובאותו אזור כמו המשאב שמשתמש בו, אבל הוא יכול לשלוח תנועה למשאבים באזורים שונים.
    • חיבור לרשת (VPC) מאפליקציית serverless תומך בתקשורת עם רשתות VPC שמחוברות באמצעות Cloud VPN וקישור בין רשתות VPC שכנות (peering).
    • אי אפשר להשתמש בחיבור לרשת (VPC) מאפליקציית serverless ברשתות מדור קודם.
  3. מגדירים את פונקציות Cloud Run כך שישתמשו במחבר.
  4. מתחברים באמצעות כתובת ה-IP הפרטית של המכונה ויציאה 3306.

התחברות ל-Cloud SQL

אחרי שמגדירים את פונקציות Cloud Run, אפשר להתחבר למכונה של Cloud SQL.

כתובת IP ציבורית (ברירת מחדל)

בנתיבים של כתובות IP ציבוריות, אפשר להגדיר את פונקציות Cloud Run כך שישתמשו ב-Cloud SQL Auth Proxy להצפנה בשתי דרכים:

כתובת IP פרטית

בנתיבים של כתובות IP פרטיות, האפליקציה מתחברת ישירות למכונה דרך רשת VPC. בשיטה הזו משתמשים ב-TCP כדי להתחבר ישירות למכונה של Cloud SQL בלי להשתמש ב-Cloud SQL Auth Proxy.

התחברות באמצעות TCP

מתחברים באמצעות כתובת ה-IP הפרטית של מכונת Cloud SQL כמארח וליציאה 3306.

Python

כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין ב-README ב-GitHub.

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

כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, עיינו ב-README ב-GitHub.

הערה:


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

כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין ב-README ב-GitHub.

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

המשך

כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין ב-README ב-GitHub.

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

כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין ב-README ב-GitHub.

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.

מאגרי חיבורים

יכול להיות שהחיבורים למסדי הנתונים הבסיסיים ינותקו, על ידי שרת מסד הנתונים עצמו או על ידי התשתית שעליה מבוססות פונקציות Cloud Run. מומלץ להשתמש בספריית לקוח שתומכת במאגרי חיבורים, שמחברים מחדש באופן אוטומטי חיבורי לקוח מנותקים. בנוסף, מומלץ להשתמש במאגר חיבורים ברמת ההיקף הגלובלי כדי להגדיל את הסבירות שהפונקציה תעשה שימוש חוזר באותו חיבור להפעלות הבאות של הפונקציה, ותסגור את החיבור באופן טבעי כשהמכונה תפונה (התאמה אוטומטית לעומס (automatic scaling) כלפי מטה). דוגמאות מפורטות יותר לשימוש במאגרי חיבורים מופיעות במאמר ניהול חיבורים למסדי נתונים.

מגבלות על החיבורים

ב-Cloud SQL יש מגבלה על מספר החיבורים בו-זמנית, והמגבלות האלה עשויות להשתנות בהתאם למנוע של מסד הנתונים שנבחר (ראו מכסות ומגבלות ב-Cloud SQL).

מומלץ להשתמש במאגר חיבורים עם פונקציות Cloud Run, אבל חשוב להגדיר את מספר החיבורים המקסימלי ל-1.

במידת האפשר, כדאי להקפיד להפעיל מאגר חיבורים רק לפונקציות שצריכות גישה למסד הנתונים. חלק ממאגרי החיבורים יוצרים חיבורים באופן יזום, מה שעלול לצרוך משאבים מיותרים ולהיכלל במגבלות החיבורים. לכן מומלץ להשתמש ב אתחול מושהה כדי לדחות את היצירה של מאגר החיבורים עד שיהיה בו צורך, ולכלול את מאגר החיבורים רק בפונקציות שבהן משתמשים בו.

במאמר ניהול החיבורים למסד הנתונים תוכלו לקרוא דוגמאות מפורטות יותר שמסבירות איך להגביל את מספר החיבורים.

מגבלות המכסות של ה-API

פונקציות Cloud Run מספקות מנגנון שמתחבר באמצעות Cloud SQL Auth Proxy, שמשתמש ב-Cloud SQL Admin API. מגבלות המכסות של ה-API חלות על Cloud SQL Auth Proxy. המכסה של Cloud SQL Admin API שנעשה בה שימוש היא בערך פי שניים ממספר המכונות של Cloud SQL שהוגדרו, כפול המספר הכולל של הפונקציות שנפרסו. כדי לשנות את מכסת ה-API הצפויה, אתם יכולים להגדיר את המספר המקסימלי של קריאות בו-זמנית. בפונקציות Cloud Run יש גם מגבלות קצב על מספר הקריאות ל-API שמותרות לכל 100 שניות.

המאמרים הבאים