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

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

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

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

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

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

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

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

    להפעלת ה-API

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

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

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

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

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

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

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

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

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

כברירת מחדל, האפליקציה תאשר את החיבורים באמצעות חשבון השירות של סביבה גמישה ב-App Engine. חשבון השירות הוא בפורמט PROJECT_ID@appspot.s3ns-system.iam.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 מספקת הצפנה ומתחברת באמצעות Cloud SQL Auth Proxy בשלוש דרכים:

    כתובת 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);
    };

    המשך

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

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

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

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

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

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

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

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

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

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

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