התחברות מהסביבה הגמישה של App Engine

בדף הזה מופיעים מידע ודוגמאות לחיבור למופע Cloud SQL משירות שפועל בסביבה גמישה של App Engine.

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

‫App Engine היא פלטפורמה מנוהלת ללא שרת (serverless) לפיתוח ולאירוח של אפליקציות אינטרנט בהיקף נרחב. אתם יכולים לבחור מבין כמה שפות, ספריות ומסגרות פופולריות כדי לפתח את האפליקציות שלכם, ואז לאפשר ל-App Engine לטפל בהקצאת שרתים ובשינוי גודל המופעים של האפליקציה בהתאם לדרישה.

הגדרת מופע של Cloud SQL

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

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

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

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

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

הגדרת סביבה גמישה של App Engine

השלבים להגדרת הסביבה הגמישה של App Engine תלויים בסוג כתובת ה-IP שהקציתם למופע Cloud SQL.

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

כדי להגדיר את הסביבה הגמישה של App Engine כך שתהיה אפשרות להתחבר למופע Cloud SQL:

  1. מוודאים שלמופע יש כתובת IP ציבורית. אפשר לבדוק את זה בדף Overview של המופע בGoogle Cloud מסוף. אם אתם צריכים להוסיף כתובת IP ציבורית, תוכלו להיעזר בהוראות שבמאמר הגדרת כתובת IP ציבורית.
  2. מאחזרים את INSTANCE_CONNECTION_NAME של המופע. אפשר למצוא את הערך הזה בדף Overview של המופע בGoogle Cloud מסוף או על ידי הפעלת הפקודה הבאה של gcloud sql instances describe:
    gcloud sql instances describe INSTANCE_NAME
       
    מחליפים את INSTANCE_NAME בשם של מופע Cloud SQL.
  3. מוודאים שלחשבון השירות שבו האפליקציה משתמשת כדי לאמת קריאות ל-Cloud SQL יש Cloud SQL Client תפקיד IAM.

    הוראות מפורטות להוספת תפקידי IAM לחשבון שירות זמינות במאמר הקצאת תפקידים לחשבונות שירות.

כברירת מחדל, האפליקציה תאשר את החיבורים באמצעות חשבון השירות של הסביבה הגמישה של App Engine. חשבון השירות הוא בפורמט PROJECT_ID@appspot.gserviceaccount.com.

אם חשבון השירות שנותן את ההרשאה שייך לפרויקט אחר מהמכונה של Cloud SQL, צריך להוסיף את Cloud SQL Admin API ואת הרשאות ה-IAM לשני הפרויקטים.

  • מעדכנים את הקובץ app.yaml של הפרויקט באפשרות שהכי מתאימה. אפשר להשתמש ברשימה מופרדת בפסיקים של מופעים כדי לציין כמה אפשרויות בבת אחת.

    הפעלה של שקע דומיין של Unix

    כדי להפעיל שקע דומיין של Unix, מוסיפים את אחת מהשורות הבאות לקובץ app.yaml של הפרויקט, בהתאם למספר המופעים שאליהם מתחברים:

    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME
    beta_settings:
      cloud_sql_instances: INSTANCE_CONNECTION_NAME,INSTANCE_CONNECTION_NAME_2,...

    הפעלת יציאת 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 פרטית

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

    אחרי הפריסה, האפליקציה תוכל להתחבר ישירות באמצעות כתובת ה-IP הפרטית והיציאה של המופע 3306.

    התחברות ל-Cloud SQL

    אחרי שמגדירים את הסביבה הגמישה של App Engine, אפשר להתחבר למופע Cloud SQL.

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

    בנתיבי IP ציבורי, סביבת App Engine הגמישה מספקת הצפנה ומתחברת באמצעות שרת proxy ל-Cloud SQL Auth בשלוש דרכים:

    כתובת IP פרטית

    התחברות באמצעות 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);
    };

    Go

    כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין בקובץ ה-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
    }
    

    C#‎

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

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

    Ruby

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

    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

    כדי לראות את קטע הקוד הזה בהקשר של אפליקציית אינטרנט, אפשר לעיין בקובץ ה-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;
        }
    }

    שיטות מומלצות ומידע נוסף

    אפשר להשתמש בשרת proxy ל-Cloud SQL Auth כשבודקים את האפליקציה באופן מקומי. הוראות מפורטות מופיעות במדריך לתחילת העבודה עם שרת proxy ל-Cloud SQL Auth.

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

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

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

    בכל מופע של App Engine שפועל בסביבה סטנדרטית, יכולות להיות עד 100 חיבורים בו-זמניים למופע. באפליקציות PHP 5.5, המגבלה היא 60 חיבורים בו-זמנית. המגבלה הזו חלה על כל מופע של אפליקציה. כלומר, לכל מופע של אפליקציית App Engine יכולים להיות כל כך הרבה חיבורים למסד הנתונים, וככל שהאפליקציה גדלה, מספר החיבורים הכולל לכל פריסה יכול לגדול. מידע נוסף זמין במאמר בנושא שינוי גודל של רכיבים.

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

    אפליקציות App Engine כפופות למגבלות זמן לבקשות, בהתאם לשימוש ולסביבה. מידע נוסף על ניהול מכונות זמין במאמרים על סביבות סטנדרטית ו גמישה של App Engine.

    מגבלות מכסת API

    ‫App Engine מספק מנגנון שמתחבר באמצעות שרת proxy ל-Cloud SQL Auth, שמשתמש ב-Cloud SQL Admin API. מגבלות מכסת ה-API חלות על שרת proxy ל-Cloud SQL Auth. כש-Cloud SQL Admin API מתחיל, הוא משתמש במכסה של שניים ובממוצע של שניים לשעה לאחר מכן. מכסת ברירת המחדל היא 180 לדקה לכל משתמש. אפליקציות App Engine כפופות גם למכסות ולמגבלות נוספות, כפי שמתואר בדף מכסות App Engine.