App Engine スタンダード環境から Cloud SQL への接続

このページには、App Engine スタンダード環境で実行中のサービスから Cloud SQL インスタンスに接続するための情報と例が記載されています。

Cloud SQL は、クラウド内のリレーショナル データベースの設定、維持、管理に役立つフルマネージド データベース サービスです。

App Engine は、大規模なウェブ アプリケーションを開発およびホスティングするための、フルマネージド型のサーバーレス プラットフォームです。アプリの開発では複数の一般的な言語、ライブラリ、フレームワークからの選択が可能で、開発したアプリのインスタンスのサーバー プロビジョニングとスケーリングは App Engine によってオンデマンドで行われます。

Cloud SQL インスタンスの設定

  1. 接続元のプロジェクトで Cloud SQL Admin API を有効にします(まだ有効にしていない場合)。

    API を有効にする

  2. Cloud SQL for PostgreSQL のインスタンスを作成します

    Cloud SQL は、デフォルトで新しいインスタンスにパブリック IP アドレスを割り当てます。プライベート IP アドレスを割り当てることもできます。両方の接続オプションの詳細については、接続の概要ページをご覧ください。

App Engine の構成

App Engine スタンダード環境を構成する手順は、Cloud SQL インスタンスに割り当てられた IP アドレスのタイプによって異なります。

パブリック IP(デフォルト)

パブリック IP を使用して Cloud SQL インスタンスに接続できるように App Engine スタンダード環境を構成するには:

  • 上記で作成したインスタンスにパブリック IP アドレスがあることを確認します。これは、Google Cloud Console のインスタンスの [概要] ページで確認できます。パブリック IP を追加する必要がある場合は、パブリック IP の構成ページの手順をご覧ください。
  • インスタンスの INSTANCE_CONNECTION_NAME を取得します。これは、Google Cloud Console のインスタンスの [概要] ページで確認できます。また、コマンド gcloud sql instances describe [INSTANCE_NAME] を実行して確認することもできます。
  • アプリが Cloud SQL の呼び出しを認証するために使用しているサービス アカウントに、適切な Cloud SQL のロールと権限があることを確認します。
    • お使いのサービスのサービス アカウントには、次のいずれかの IAM ロールが必要です。
      • Cloud SQL Client(推奨)
      • Cloud SQL Editor
      • Cloud SQL Admin
      または、次の IAM 権限を手動で割り当てることもできます。
      • cloudsql.instances.connect
      • cloudsql.instances.get
      サービス アカウントに IAM ロールを追加する方法の詳細については、サービス アカウントへのロールの付与をご覧ください。

    デフォルトでは、アプリは App Engine サービス アカウントを使用して接続を承認します。サービス アカウント ID の形式は PROJECT_ID@appspot.gserviceaccount.com です。

    サービス アカウントの承認が Cloud SQL インスタンスとは異なるプロジェクトに属している場合、Cloud SQL Admin API と IAM の権限を両方のプロジェクトに追加する必要があります。

プライベート IP

サーバーレス VPC アクセス コネクタは VPC ネットワークとの通信を処理します。 プライベート IP に直接接続するには、次のようにする必要があります。

  1. 上記で作成した Cloud SQL インスタンスにプライベート IP アドレスが割り当てられていることを確認します。プライベート IP を追加する必要がある場合は、プライベート IP の構成ページの手順をご覧ください。
  2. Cloud SQL インスタンスと同じ VPC ネットワークにサーバーレス VPC アクセス コネクタを作成します。
  3. コネクタは、それを使用するリソースと同じプロジェクトとリージョンに配置する必要がありますが、異なるリージョンのリソースにトラフィックを送信できます。

    サーバーレス VPC アクセスでは、Cloud VPNVPC ネットワーク ピアリングを介して接続された VPC ネットワークとの通信がサポートされています。

    サーバーレス VPC アクセスは、レガシー ネットワークまたは共有 VPC をサポートしていません。

  4. コネクタを使用するように App Engine を構成します
  5. インスタンスのプライベート IP とポート 5432 を使用して接続します。

Cloud SQL への接続

App Engine スタンダード環境を構成すると、Cloud SQL インスタンスに接続できます。App Engine には、Cloud SQL Proxy を使用して接続する仕組みが用意されています。

パブリック IP(デフォルト)

プライベート IP

TCP で接続

インスタンスのプライベート IP アドレスとポート 5432 を使用して直接接続します。

Python

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

# Remember - storing secrets in plaintext is potentially unsafe. Consider using
# something like https://cloud.google.com/secret-manager/docs/overview to help keep
# secrets secret.
db_user = os.environ["DB_USER"]
db_pass = os.environ["DB_PASS"]
db_name = os.environ["DB_NAME"]
db_host = os.environ["DB_HOST"]

# Extract host and port from db_host
host_args = db_host.split(":")
db_hostname, db_port = host_args[0], int(host_args[1])

pool = sqlalchemy.create_engine(
    # Equivalent URL:
    # postgres+pg8000://<db_user>:<db_pass>@<db_host>:<db_port>/<db_name>
    sqlalchemy.engine.url.URL(
        drivername="postgres+pg8000",
        username=db_user,  # e.g. "my-database-user"
        password=db_pass,  # e.g. "my-database-password"
        host=db_hostname,  # e.g. "127.0.0.1"
        port=db_port,  # e.g. 5432
        database=db_name  # e.g. "my-database-name"
    ),
    # ... Specify additional properties here.
)

Node.js

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

const connectWithTcp = (config) => {
  // Extract host and port from socket address
  const dbSocketAddr = process.env.DB_HOST.split(":") // e.g. '127.0.0.1:5432'

  // Establish a connection to the database
  return Knex({
    client: 'pg',
    connection: {
      user: process.env.DB_USER, // e.g. 'my-user'
      password: process.env.DB_PASS, // e.g. 'my-user-password'
      database: process.env.DB_NAME, // e.g. 'my-database'
      host: dbSocketAddr[0], // e.g. '127.0.0.1'
      port: dbSocketAddr[1], // e.g. '5432'
    },
    // ... Specify additional properties here.
    ...config
  });
}

Go

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

var (
	dbUser    = mustGetenv("DB_USER")     // e.g. 'my-db-user'
	dbPwd     = mustGetenv("DB_PASS")     // e.g. 'my-db-password'
	dbTcpHost = mustGetenv("DB_TCP_HOST") // e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex)
	dbPort    = mustGetenv("DB_PORT")     // e.g. '5432'
	dbName    = mustGetenv("DB_NAME")     // e.g. 'my-database'
)

var dbURI string
dbURI = fmt.Sprintf("host=%s user=%s password=%s port=%s database=%s", dbTcpHost, dbUser, dbPwd, dbPort, dbName)

// dbPool is the pool of database connections.
dbPool, err := sql.Open("pgx", dbURI)
if err != nil {
	return nil, fmt.Errorf("sql.Open: %v", err)
}

// ...

return dbPool, nil

C#

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

            // Equivalent connection string:
            // "Uid=<DB_USER>;Pwd=<DB_PASS>;Host=<DB_HOST>;Database=<DB_NAME>;"
            var connectionString = new NpgsqlConnectionStringBuilder()
            {
                // The Cloud SQL proxy provides encryption between the proxy and instance.
                SslMode = SslMode.Disable,

                // Remember - storing secrets in plaintext is potentially unsafe. Consider using
                // something like https://cloud.google.com/secret-manager/docs/overview to help keep
                // secrets secret.
                Host = Environment.GetEnvironmentVariable("DB_HOST"),     // e.g. '127.0.0.1'
                // Set Host to 'cloudsql' when deploying to App Engine Flexible environment
                Username = 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'
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            // ...
            NpgsqlConnection connection =
                new NpgsqlConnection(connectionString.ConnectionString);

Ruby

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

development:
  adapter: postgresql
  # Configure additional properties here.
  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("DB_HOST") { "127.0.0.1" }%> # '172.17.0.1' if deployed to GAE Flex
  port: <%= ENV.fetch("DB_PORT")  { 5432 }%> 

PHP

このスニペットをウェブ アプリケーションのコンテキストで表示するには、GitHub の README をご覧ください。

        // // $username = 'your_db_user';
        // // $password = 'yoursupersecretpassword';
        // // $db_name = 'your_db_name';
        // // $host = "127.0.0.1";

        // Connect using TCP
        $dsn = sprintf('pgsql:dbname=%s;host=%s', $db_name, $host);

        // Connect to the database
        $conn = new PDO($dsn, $username, $password, $conn_config);
    } catch (TypeError $e) {
        throw new RuntimeException(
            sprintf(
                'Invalid or missing configuration! Make sure you have set ' .
                '$username, $password, $db_name, and $host (for TCP mode) ' .
                'or $cloud_sql_connection_name (for UNIX socket 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/postgres/connect-external-app',
                $e->getMessage()
            ),
            $e->getCode(),
            $e
        );
    }

    return $conn;
}

/**
 *  @param $conn_config array driver-specific options for PDO
 */
function init_unix_database_connection(array $conn_config)
{
    $username = getenv('DB_USER');
    $password = getenv('DB_PASS');
    $db_name = getenv('DB_NAME');
    $cloud_sql_connection_name = getenv('CLOUD_SQL_CONNECTION_NAME');
    $socket_dir = getenv('DB_SOCKET_DIR') ?: '/cloudsql';

    try {
        // // $username = 'your_db_user';
        // // $password = 'yoursupersecretpassword';
        // // $db_name = 'your_db_name';
        // // $cloud_sql_connection_name = getenv("CLOUD_SQL_CONNECTION_NAME");
        // // $socket_dir = getenv('DB_SOCKET_DIR') ?: '/cloudsql';

        // Connect using UNIX sockets
        $dsn = sprintf(
            'pgsql:dbname=%s;host=%s/%s',
            $db_name,
            $socket_dir,
            $cloud_sql_connection_name
        );

        // Connect to the database.
        $conn = new PDO($dsn, $username, $password, $conn_config);
    } catch (TypeError $e) {
        throw new RuntimeException(
            sprintf(
                'Invalid or missing configuration! Make sure you have set ' .
                '$username, $password, $db_name, and $host (for TCP mode) ' .
                'or $cloud_sql_connection_name (for UNIX socket 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/postgres/connect-external-app',
                $e->getMessage()
            ),
            $e->getCode(),
            $e
        );
    }

    return $conn;
}

// Configure the templating engine.
$container['view'] = function () {
    return Twig::create(__DIR__ . '/../views');
};

// Create the application.
$app = AppFactory::create();

// Add the twig middleware
$app->add(TwigMiddleware::createFromContainer($app));

// Setup error handlinmg
$app->addErrorMiddleware(true, false, false);

return $app;

ベスト プラクティスとその他の情報

アプリケーションをローカルでテストする場合、Cloud SQL プロキシを使用できます。詳細については、ローカルテストにプロキシを使用する場合のクイックスタートを参照してください。

接続プール

データベース サーバー自体、または基盤となるインフラストラクチャによって、基盤となるデータベースへの接続が切断される可能性があります。これを軽減するには、接続プールと自動再接続をサポートするクライアント ライブラリを使用することをおすすめします。

接続プールの使用方法の詳しい例については、データベース接続を管理するをご覧ください。

接続上限

Cloud SQL では、同時接続の上限が設定されています。これらの上限は、選択したデータベース エンジンによって異なります。詳しくは、Cloud SQL の割り当てと上限をご覧ください。

App Engine には負荷の増加に応じて自動的にインスタンスを作成する機能があるため、これらの上限を超える可能性があります。この問題を回避するには、App Engine インスタンスの最大数を制限します。詳しくは、要素のスケーリングをご覧ください。

スタンダード環境で実行される App Engine の各インスタンスでは、インスタンスに対する同時接続数が最大 100 個に制限されます。PHP 5.5 アプリケーションについては、同時接続数が最大 60 個に制限されます。

App Engine アプリケーションでは、使用状況や環境に応じてリクエストに時間制限が設けられる場合があります。詳しくは、App Engine のスタンダード環境とフレキシブル環境におけるインスタンスの管理方法をご覧ください。

さらに App Engine アプリケーションには、App Engine の割り当てページで説明されている App Engine の割り当てと上限も適用されます。

API の割り当て上限

App Engine には、Cloud SQL Admin API を使用する Cloud SQL Proxy を使用して接続する仕組みが用意されています。Cloud SQL Proxy には API 割り当て上限が適用されます。