如何从Cloud Functions连接Google Cloud SQL?

42

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

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

当我将我的台式机IP地址加入白名单时,我可以从本地计算机使用函数的node.js代码连接到Cloud SQL。但是当我部署后,无法连接,也找不到Firebase函数服务器的HOST IP地址来进行白名单设置。

如何从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);
  }
});
7个回答

36

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

免责声明:这似乎还没有官方公布,所以可能会在之后更改。此外,我只在mysql中测试过。但是根据这个解决方案的性质,我认为在pg模块中应该也可以按照相同的方式工作(它似乎将接受域套接字路径作为主机参数)

编辑(2017/12/7):谷歌似乎提供了官方早期访问权限,并且相同的方法仍然有效。
编辑(2018/07/04):似乎有人只是复制并粘贴我的示例代码并遇到了麻烦。正如谷歌所说的那样,您应该使用连接池来避免SQL连接泄漏。(它会导致ECONNREFUSE),所以我稍微修改了示例代码。 编辑(2019/04/04):在下面的示例中,使用$DBNAME作为spanner实例名称很困惑,因此我修改了示例。

https://issuetracker.google.com/issues/36388165#comment44中,谷歌的工程师说Cloud Function实例可以通过特定路径'/cloudsql/$PROJECT_ID:$REGION:$DBNAME'与Cloud SQL进行通信。

事实上,我可以从以下Cloud Function代码连接和操作Cloud 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
    });
};

我希望这对于那些不能等待谷歌官方公告的人有所帮助。


1
这个有效。套接字路径与实例属性中的“实例连接名称”相同。 - kospol
@rudolph1024 你已经尝试过了吗?我没有第一代云SQL服务器,所以无法尝试。但是如果“$PROJECT_ID:$REGION:$DBNAME”意味着“实例连接名称”,就像kospol所说的那样,它可能有效。如果您能尝试并发布结果,我将不胜感激。 - takehiro iyatomi
1
@rudolph1024 谢谢你的报告,但我很抱歉听到这个消息。也许这就是谷歌还没有宣布的原因。 - takehiro iyatomi
1
@rudolph1024,如果你仍然有兴趣从云函数连接第一代云SQL,请注意,官方文档(https://docs.google.com/document/d/1XZAohlR2Ew_ShZ6wJE7LeMMk6YhhKTEUoEHz_NKt6Mw)指出,可以使用实例连接名称,例如“<ProjectID>:<InstanceID>”。 - takehiro iyatomi
2
如果您需要从另一个Google Cloud Platform项目连接,请将 <YOUR_PROJECT_ID>@appspot.gserviceaccount.com 添加到您的IAM中,并提供Cloud SQL客户端角色。 - Wes Cossick
显示剩余10条评论

14

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

enter image description here

运行以下命令,将数据库密码保存到Firebase环境中:

$ firebase functions:config:set \
    db.user="<username>" \
    db.password="<password>" \
    db.database="<database>"

Then...

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

另请参阅 https://dev59.com/91cQ5IYBdhLWcg3wA_BA#48825037 (通过Babel使用现代JavaScript语法)


14

新答案:

请查看其他回答,现在已经官方支持了。https://cloud.google.com/functions/docs/sql

旧答案:

目前不支持。但是在问题跟踪器#36388165上有一个功能请求:

由于UNIX套接字不存在(导致ENOENT),并且没有定义的IP范围可以列入白名单(导致ETIMEDOUT),因此目前不能从Cloud Functions连接到Cloud SQL。一种可能性是从Cloud SQL实例中将0.0.0.0/0列入白名单,但出于安全原因不建议这样做。

如果这对你很重要,建议你访问问题跟踪器并为该功能请求点赞以帮助其获得人气。


1
目前可以很容易地从云函数连接到云SQL。还有官方指南。请查看其他答案。 - vovahost
请注意,我的回答是2017年的,所以我不认为有必要将其投票降低。我会更新它以反映它已不再相关。 - Niklas B
如果您的云函数是使用Java编写的,并且您正在遵循答案中提供的GCP文档,那么GCP github中的以下存储库也可能会有所帮助:https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory - Shabirmean

10

5

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

1. 创建一个新项目

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

2. 在您的项目上启用计费:https://cloud.google.com/billing/docs/how-to/modify-project

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

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.启用服务网络 API:

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

5.创建2个云 SQL 实例(一个内部 IP,一个公共 IP)- https://cloud.google.com/sql/docs/mysql/create-instance

6.拥有外部 IP 的云 SQL 实例:

#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 内部IP的Cloud Sql实例:

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 实例,我们将使用 Serverless Access 和 TCP 从 Google Cloud Functions 连接到 database-ipinternal,以及使用 Unix 域套接字从 Google Cloud Functions 连接到 database-external。

7. 启用 Cloud SQL 管理 API。

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

注意:默认情况下,Cloud Functions 不支持使用 TCP 连接到 Cloud SQL 实例。除非您已经配置了无服务器 VPC 访问,否则您的代码不应尝试使用 IP 地址(例如 127.0.0.1 或 172.17.0.1)访问该实例。
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. 为您的云函数创建一个服务账户。确保该服务的账户拥有以下IAM角色:Cloud SQL客户端,如果需要从标准App Engine连接到内部IP上的Cloud SQL,我们还需要Compute Network用户角色。

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 domain 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.部署云函数:

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.前往云函数,选择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'

成功!


0

0

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

我已经进行了实验。Google Cloud SQL不使用内部IP地址。因此,您不能使用10.128.0.0/20来允许Google Cloud SQL的内部IP地址。

答案

因此,从控制台进入Google Cloud SQL > 实例 > 授权,您可以添加:

151.101.0.0/17

此将允许您使用 IP 地址范围 151.101.0.0151.101.127.255,其中 Firebase 服务器域目前为151.101.1.195151.101.65.195

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

同时,请确保您的 Cloud SQL 数据库使用 us-central 区域。Firebase 似乎在 us-central 可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接