Verbindung über die App Engine-Standardumgebung herstellen

Diese Seite enthält Informationen und Beispiele zum Herstellen einer Verbindung zu einer Cloud SQL-Instanz über einen Dienst, der in der App Engine-Standardumgebung ausgeführt wird.

Cloud SQL ist ein vollständig verwalteter Datenbankdienst, mit dem Sie Ihre relationalen Datenbanken in der Cloud einrichten, warten und verwalten können.

App Engine ist eine vollständig verwaltete, serverlose Plattform für die Entwicklung und das Hosting von Webanwendungen in großem Umfang. Sie können für die Entwicklung Ihrer Anwendungen aus mehreren gängigen Sprachen, Bibliotheken und Frameworks auswählen. App Engine übernimmt dann die Bereitstellung von Servern und die bedarfsgerechte Skalierung Ihrer Anwendungsinstanzen.

Cloud SQL-Instanz einrichten

  1. Aktivieren Sie die Cloud SQL Admin API in dem Google Cloud-Projekt, über das Sie eine Verbindung herstellen, falls noch nicht geschehen:

    Enable the API

  2. Cloud SQL for MySQL-Instanz erstellen Wir empfehlen, einen Cloud SQL-Instanzstandort in derselben Region wie Ihr Cloud Run-Dienst auszuwählen, um die Latenz zu verbessern, einige Netzwerkkosten zu vermeiden und das Risiko von regionenübergreifenden Ausfällen zu reduzieren.

    Standardmäßig weist Cloud SQL einer neuen Instanz eine öffentliche IP-Adresse zu. Sie können aber auch eine private IP-Adresse zuweisen. Weitere Informationen zu den Verbindungsoptionen für beide Möglichkeiten finden Sie auf der Seite Verbindungsübersicht.

App Engine-Standardumgebung konfigurieren

Die Schritte zum Konfigurieren der App Engine-Standardumgebung hängen vom Typ der IP-Adresse ab, die Sie der Cloud SQL-Instanz zugewiesen haben.

Öffentliche IP-Adresse (Standard)

So konfigurieren Sie die App Engine-Standardumgebung, damit Verbindungen zu einer Cloud SQL-Instanz über eine öffentliche IP-Adresse möglich sind:

  • Achten Sie darauf, dass die Instanz eine öffentliche IP-Adresse hat. Sie können dies in der Google Cloud Console auf der Seite Übersicht für Ihre Instanz nachprüfen. Eine Anleitung zum Hinzufügen einer öffentlichen IP-Adresse finden Sie auf der Seite Öffentliche IP-Verbindungen konfigurieren.
  • Rufen Sie den INSTANCE_CONNECTION_NAME für Ihre Instanz ab. Sie finden diesen Wert auf der Seite Übersicht Ihrer Instanz in der Google Cloud Console oder ermitteln ihn über folgenden gcloud sql instances describe-Befehl:
    gcloud sql instances describe INSTANCE_NAME
       
    Ersetzen Sie INSTANCE_NAME durch den Namen Ihrer Cloud SQL-Instanz.
  • Ersetzen Sie die Variable INSTANCE_NAME durch den Namen Ihrer Instanz.
  • Das Dienstkonto, mit dem Ihre Anwendung Aufrufe an Cloud SQL authentifiziert, benötigt die entsprechenden Cloud SQL-Rollen und -Berechtigungen.
    • Das Dienstkonto für Ihren Dienst benötigt eine der folgenden IAM-Rollen:
      • Cloud SQL Client (bevorzugt)
      • Cloud SQL Editor
      • Cloud SQL Admin
      Sie können stattdessen auch manuell die folgenden IAM-Berechtigungen zuweisen:
      • cloudsql.instances.connect
      • cloudsql.instances.get
      Eine ausführliche Anleitung zum Hinzufügen von IAM-Rollen zu einem Dienstkonto finden Sie unter Dienstkonten Rollen zuweisen.

    Standardmäßig autorisiert Ihre Anwendung die Verbindungen mit einem App Engine-Dienstkonto. Die Dienstkonto-ID hat das Format PROJECT_ID@appspot.gserviceaccount.com.

    Wenn das autorisierende Dienstkonto zu einem anderen Projekt gehört als die Cloud SQL-Instanz, müssen die Cloud SQL Admin API und die IAM-Berechtigungen für beide Projekte hinzugefügt werden.

Private IP-Adresse

Wenn das autorisierende Dienstkonto zu einem anderen Projekt gehört als zu dem, das die Cloud SQL-Instanz enthält, gehen Sie so vor:

  • Aktivieren Sie in beiden Projekten die Cloud SQL Admin API.
  • Fügen Sie für das Dienstkonto in dem Projekt, das die Cloud SQL-Instanz enthält, die IAM-Berechtigungen hinzu.
Ein Connector für serverlosen VPC-Zugriff verwendet private IP-Adressen, um die Kommunikation mit Ihrem VPC-Netzwerk zu verarbeiten. So stellen Sie eine direkte Verbindung mithilfe privater IP-Adressen her:
  1. Achten Sie darauf, dass die oben erstellte Cloud SQL-Instanz eine private IP-Adresse hat. Eine Anleitung zum Hinzufügen einer IP-Adresse finden Sie unter Private IP-Adresse konfigurieren.
  2. Erstellen Sie einen Connector für serverlosen VPC-Zugriff im selben VPC-Netzwerk wie Ihre Cloud SQL-Instanz. Beachten Sie folgende Bedingungen:
    • Wenn Sie keine freigegebene VPC verwenden, muss sich ein Connector im selben Projekt und in derselben Region wie die Ressource befinden, von der er verwendet wird. Der Connector kann jedoch Traffic an Ressourcen in anderen Regionen senden.
    • Der serverlose VPC-Zugriff unterstützt die Kommunikation mit VPC-Netzwerken, die über Cloud VPN und VPC-Netzwerk-Peering verbunden sind.
    • Der serverlose VPC-Zugriff unterstützt keine Legacy-Netzwerke.
  3. Konfigurieren Sie die App Engine-Standardumgebung für die Verwendung des Connectors.
  4. Stellen Sie eine Verbindung über die private IP-Adresse der Instanz und den Port 3306 her.

Verbindung mit Cloud SQL herstellen

Nachdem Sie die App Engine-Standardumgebung konfiguriert haben, können Sie eine Verbindung zu Ihrer Cloud SQL-Instanz herstellen.

Öffentliche IP-Adresse (Standard)

Für öffentliche IP-Pfade bietet die App Engine-Standardumgebung Verschlüsselung und stellt über den Cloud SQL Auth-Proxy eine Verbindung auf zwei Arten her:

Private IP-Adresse

Bei privaten IP-Pfaden stellt die Anwendung eine direkte Verbindung zur Instanz über ein VPC-Netzwerk her. Bei dieser Methode wird über TCP eine direkte Verbindung zur Cloud SQL-Instanz hergestellt, ohne den Cloud SQL Auth-Proxy zu verwenden.

Mit TCP verbinden

Stellen Sie eine Verbindung mithilfe der privaten IP-Adresse Ihrer Cloud SQL-Instanz als Host und Port 3306 her.

Python

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

Hinweis:

  • INSTANCE_CONNECTION_NAME sollte als <MY-PROJECT>:<INSTANCE-REGION>:<INSTANCE-NAME> dargestellt werden
  • Die Verwendung des Arguments ipTypes=PRIVATE erzwingt, dass die SocketFactory eine Verbindung zur zugeordneten privaten IP-Adresse einer Instanz herstellen kann.
  • Die Anforderungen für die JDBC Socket Factory-Version der pom.xml-Datei finden Sie hier.


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

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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#

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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

Wenn Sie dieses Snippet im Kontext einer Webanwendung sehen möchten, rufen Sie die README-Datei auf GitHub auf.

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

Best Practices und weitere Informationen

Sie können für den lokalen Test Ihrer Anwendung den Cloud SQL Auth-Proxy verwenden. Eine ausführliche Anleitung dazu finden Sie unter Kurzanleitung zur Verwendung des Cloud SQL Auth-Proxys.

Verbindungspools

Verbindungen zu zugrunde liegenden Datenbanken können unterbrochen werden, entweder vom Datenbankserver selbst oder von der zugrunde liegenden Infrastruktur. Um dies zu vermeiden, empfehlen wir Ihnen die Verwendung einer Clientbibliothek, die Verbindungspools und das automatische Herstellen einer neuen Verbindung unterstützt.

Verbindungseinschränkungen

Eine App Engine-Instanz, die in einer Standardumgebung ausgeführt wird, kann maximal 100 Verbindungen gleichzeitig zu einer Instanz aufbauen. Bei Anwendungen, die in PHP 5.5 geschrieben sind, ist die Anzahl der gleichzeitigen Verbindungen auf 60 beschränkt. Dieses Limit gilt pro Anwendungsinstanz. Das bedeutet, dass jede Instanz der App Engine-Anwendung so viele Verbindungen zur Datenbank herstellen und bei der Skalierung die Gesamtzahl der Verbindungen pro Bereitstellung erhöhen kann. Weitere Informationen finden Sie unter Elemente skalieren.

Sie können die maximale Anzahl der pro Instanz verwendeten Verbindungen mithilfe eines Verbindungspools begrenzen. Ausführlichere Beispiele zum Begrenzen der Anzahl an Verbindungen finden Sie auf der Seite Datenbankverbindungen verwalten.

App Engine-Anwendungen unterliegen je nach Nutzung und Umgebung bestimmten Zeitlimits für Anfragen. Weitere Informationen finden Sie in den Erläuterungen dazu, wie Instanzen in der App Engine-Standardumgebung bzw. der flexiblen App Engine-Umgebung verwaltet werden.

API-Kontingentlimits

App Engine bietet ein Verfahren zum Herstellen einer Verbindung über den Cloud SQL Auth-Proxy, das die Cloud SQL Admin API verwendet. Für den Cloud SQL Auth-Proxy gelten deshalb API-Kontingentlimits. Beim Start der Cloud SQL Admin API wird ein Kontingent von zwei und danach durchschnittlich zwei pro Stunde verwendet. Das Standardkontingent beträgt 180 pro Minute und Nutzer. App Engine-Anwendungen unterliegen außerdem weiteren Kontingenten und Limits. Diese finden Sie auf der Seite mit den Kontingenten für App Engine.