如何从 Cloud Functions 连接 Google Cloud SQL?

How to connect Google Cloud SQL from Cloud Functions?

我正在尝试使用 Cloud Functions for Firebase 构建一个 API 与 Google 云 SQL (PostgreSQL) 实例通信。

我正在使用 HTTP(S) 触发器。

当我将我的桌面 IP 地址列入白名单时,我可以从我的本地计算机使用函数的 node.js 代码连接到云 SQL。但是我部署的时候连不上,也搞不清楚Firebase Function服务器的HOST IP地址,加入白名单。

您如何从 Cloud Functions for Firebase 与 Google Cloud SQL 对话?

谢谢!

// Code Sample, of what's working on Localhost.
var functions = require('firebase-functions');

var pg = require('pg');
var pgConfig = {
  user: functions.config().pg.user,
  database: functions.config().pg.database,
  password: functions.config().pg.password,
  host: functions.config().pg.host
}

exports.helloSql = functions.https.onRequest((request, response) => {
  console.log('connecting...');
  try {
    client.connect(function(err) {
      if (err) throw err;

      console.log('connection success');
      console.log('querying...');

      client.query('SELECT * FROM guestbook;', function(err, result){
        if (err) throw err;

        console.log('querying success.');
        console.log('Results: ', result);
        console.log('Ending...');

        client.end(function(err){
          if (err) throw err;
          console.log('End success.');
          response.send(result);
        });
      });

    });
  } catch(er) {
    console.error(er.stack)
    response.status(500).send(er);
  }
});

Cloud Functions - Supported Services - 我在此列表中没有看到 Cloud SQL,所以它可能还不受支持。

新答案:

查看其他答案,现已正式支持。 https://cloud.google.com/functions/docs/sql

旧答案:

目前不可能。然而,这是问题跟踪器 #36388165:

上的功能请求

Connecting to Cloud SQL from Cloud Functions is currently not supported, as the UNIX socket does not exist (causing ENOENT) and there is no defined IP range to whitelist (causing ETIMEDOUT). One possibility is to whitelist 0.0.0.0/0 from the Cloud SQL instance but this is not recommended for security reasons.

如果这对您来说是一个重要的功能,我建议您访问 issuetracker 并为功能请求加注星标,以帮助它获得普及。

我在 #36388165 的进一步讨论中找到了答案。

免责声明:这好像不是官方公布的,以后可能会有变化。我也只在 mysql 中测试。但是这个解决方案的性质,我认为应该与 pg 模块中的方法相同(它似乎 accept domain socket path 作为主机参数)

编辑(2017/12/7):google 似乎提供了 official early access,同样的方法仍然有效。
编辑(2018/07/04):似乎有人只是复制并粘贴我的示例代码并遇到麻烦。作为 google says,你应该使用连接池来避免 sql 连接泄漏。 (它会导致 ECONNREFUSE)所以我稍微更改了示例代码。 EDIT(2019/04/04):在下面的示例中,使用 $DBNAME 作为扳手实例名称令人困惑,我修改了示例。

in https://issuetracker.google.com/issues/36388165#comment44 google 家伙说云函数实例可以通过特殊路径中的域套接字与云 sql 通信 '/cloudsql/$PROJECT_ID:$REGION:$DBNAME'。

我实际上可以从以下云功能代码连接和操作云 SQL。

const mysql = require('mysql');
const pool = mysql.createPool({
    connectionLimit : 1,
    socketPath: '/cloudsql/' + '$PROJECT_ID:$REGION:$SPANNER_INSTANCE_NAME',
    user: '$USER',
    password: '$PASS',
    database: '$DATABASE'
});
exports.handler = function handler(req, res) {
    //using pool instead of creating connection with function call
    pool.query(`SELECT * FROM table where id = ?`, 
                                req.body.id, function (e, results) {
        //made reply here
    });
};

我希望这对那些迫不及待 google 正式宣布的人有所帮助。

GCP > SQL > Instances 页面上找到您的数据库区域和实例名称:

通过 运行:

将您的数据库密码保存到 Firebase environment
$ firebase functions:config:set \
    db.user="<username>" \
    db.password="<password>" \
    db.database="<database>"

然后...

db.js

const { Pool } = require('pg');
const { config } = require('firebase-functions');

const project = process.env.GCP_PROJECT;
const region = 'europe-west1';
const instance = 'db';

module.exports = new Pool({
  max: 1,
  host: `/cloudsql/${project}:${region}:${instance}`,
  ...config().db
});

someFunction.js

const { https } = require('firebase-functions');
const db = require('./db');

module.exports = https.onRequest((req, res) =>
  db
    .query('SELECT version()')
    .then(({ rows: [{ version }]) => {
      res.send(version);
    }));

另见 (通过 Babel 使用现代 JavaScript 语法)

您还可以授权 Firebase IP 地址范围,因为我们真的不知道 Firebase 在外部使用哪个 IP 地址。

我已经试验过了。 Google 云 SQL 不使用内部 IP 地址。因此,您不能使用 10.128.0.0/20 允许您的 Google 云 SQL.

使用内部 IP 地址

回答

因此,从控制台转到 Google Cloud SQL > Instance > Authorization,您可以添加:

151.101.0.0/17

这将允许您 151.101.0.0151.101.127.255 IP 地址范围,其中 Firebase 服务器域目前是 151.101.1.195151.101.65.195.

我不确定这个 IP 地址是否会改变。

此外,请确保您的云 SQL 数据库正在使用 us-central 区域。 Firebase 似乎在 us-central.

中可用

现在有官方文档,尽管截至 2018 年 7 月仍处于测试阶段

https://cloud.google.com/functions/docs/sql

从 GOOGLE 云功能连接到云 SQL 使用 TCP 和 UNIX 域套接字 2020

1.Create一个新项目

gcloud projects create gcf-to-sql
gcloud config set project gcf-to-sql
gcloud projects describe gcf-to-sql

2.Enable 您项目的账单:https://cloud.google.com/billing/docs/how-to/modify-project

3.Set 计算项目信息元数据:

gcloud compute project-info describe --project gcf-to-sql
#Enable the Api, and you can check that default-region,google-compute-default-zone are not set. Set the metadata.
gcloud compute project-info add-metadata --metadata google-compute-default-region=europe-west2,google-compute-default-zone=europe-west2-b

4.Enable 服务网络 Api:

gcloud services list --available
gcloud services enable servicenetworking.googleapis.com

5.Create 2 个云 sql 实例,(一个具有内部 ip,一个具有 public ip)- https://cloud.google.com/sql/docs/mysql/create-instance:

6.a Cloud Sql 外网ip实例:

#Create the sql instance in the 
gcloud --project=con-ae-to-sql beta sql instances create database-external --region=europe-west2
#Set the password for the "root@%" MySQL user:
gcloud sql users set-password root --host=% --instance database-external --password root 
#Create a user
gcloud sql users create user_name --host=% --instance=database-external  --password=user_password
#Create a database
gcloud sql databases create user_database --instance=database-external
gcloud sql databases list --instance=database-external

6.b 云 Sql 内部 ip 实例:

i.#Create a private connection to Google so that the VM instances in the default VPC network can use private services access to reach Google services that support it.

gcloud compute addresses create google-managed-services-my-network     --global  --purpose=VPC_PEERING --prefix-length=16  --description="peering range for Google"  --network=default --project=con-ae-to-sql
gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --ranges=google-managed-services-my-network  --network=default  --project=con-ae-to-sql
#Check whether the operation was successful.
gcloud services vpc-peerings operations describe     --name=operations/pssn.dacc3510-ebc6-40bd-a07b-8c79c1f4fa9a
#Listing private connections
gcloud services vpc-peerings list --network=default --project=con-ae-to-sql
 
ii.Create the instance:

gcloud --project=con-ae-to-sql beta sql instances create database-ipinternal --network=default --no-assign-ip  --region=europe-west2
#Set the password for the "root@%" MySQL user:
gcloud sql users set-password root --host=% --instance database-ipinternal --password root
#Create a user
gcloud sql users create user_name --host=% --instance=database-ipinternal  --password=user_password
#Create a database
gcloud sql databases create user_database --instance=database-ipinternal
gcloud sql databases list --instance=database-ipinternal 


gcloud sql instances list
gcloud sql instances describe database-external
gcloud sql instances describe database-ipinternal
#Remember the instances connectionName

好的,所以我们有两个 mysql 实例,我们将使用无服务器访问和 TCP 从 Google Cloud Functions 连接到 database-ipinternal,然后从 Google Cloud Functions 连接到数据库-外部使用 unix 域套接字。

7.Enable 云 SQL 管理员 API

gcloud services list --available
gcloud services enable sqladmin.googleapis.com

注意:默认情况下,Cloud Functions 不支持使用 TCP 连接到云 SQL 实例。您的代码不应尝试使用 IP 地址(例如 127.0.0.1 或 172.17.0.1)访问实例,除非您已配置无服务器 VPC 访问。

8.a 确保为您的项目启用无服务器 VPC 访问 API:

gcloud services enable vpcaccess.googleapis.com

8.b 创建连接器:

gcloud compute networks vpc-access connectors create serverless-connector --network default --region europe-west2 --range 10.10.0.0/28
#Verify that your connector is in the READY state before using it
gcloud compute networks vpc-access connectors describe serverless-connector --region europe-west2

9.Create 您的云功能的服务帐户。确保您的服务的服务帐户具有以下 IAM 角色:云 SQL 客户端,并且为了在内部 ip 上从 App Engine 标准连接到云 Sql,我们还需要角色计算网络用户。

gcloud iam service-accounts create cloud-function-to-sql
gcloud projects add-iam-policy-binding gcf-to-sql --member serviceAccount:cloud-function-to-sql@gcf-to-sql.iam.gserviceaccount.com   --role roles/cloudsql.client
gcloud projects add-iam-policy-binding gcf-to-sql --member serviceAccount:cloud-function-to-sql@gcf-to-sql.iam.gserviceaccount.com  --role roles/compute.networkUser

现在我配置了设置

1.使用 Tcp 和 unix domanin socket

从 Google Cloud Functions 连接到 Cloud Sql
cd app-engine-standard/
ls
#main.py requirements.txt

cat requirements.txt
sqlalchemy
pymysql
      
cat main.py 
import pymysql
from sqlalchemy import create_engine


 def gcf_to_sql(request):

    engine_tcp = create_engine('mysql+pymysql://user_name:user_password@10.36.0.3:3306')
    existing_databases_tcp = engine_tcp.execute("SHOW DATABASES;")
    con_tcp = "Connecting from Google Cloud Functions to Cloud SQL using TCP: databases => " + str([d[0] for d in existing_databases_tcp]).strip('[]') + "\n"
    engine_unix_socket = create_engine('mysql+pymysql://user_name:user_password@/user_database?unix_socket=/cloudsql/gcf-to-sql:europe-west2:database-external')
    existing_databases_unix_socket = engine_unix_socket.execute("SHOW DATABASES;")
    con_unix_socket = "Connecting from Google Cloud Function  to Cloud SQL using Unix Sockets: tables in sys database:  => " + str([d[0] for d in existing_databases_unix_socket]).strip('[]') + "\n"
    return con_tcp + con_unix_socket
     

2.Deploy云函数:

gcloud beta functions deploy gcf_to_sql --runtime python37 --region europe-west2 --vpc-connector projects/gcf-to-sql/locations/europe-west2/connectors/serverless-connector  --trigger-http
 

3.Go 到 Cloud Function,选择 gcf-to-sql,测试,测试功能:

#Connecting from Google Cloud Functions to Cloud SQL using TCP: databases => 'information_schema', 'mysql', 'performance_schema', 'sys', 'user_database'
#Connecting from Google Cloud Function  to Cloud SQL using Unix Sockets: tables in sys database:  => 'information_schema', 'mysql', 'performance_schema', 'sys', 'user_database'

成功!