连接到 Cloud SQL 时,Cloud Functions “连接被拒绝”

Posted

技术标签:

【中文标题】连接到 Cloud SQL 时,Cloud Functions “连接被拒绝”【英文标题】:Cloud Functions "Connection Refused" when connecting to Cloud SQL 【发布时间】:2019-04-18 22:30:41 【问题描述】:

我正在尝试跟随 example provided by Google 使用 Python 和 mysql 实例将 Cloud Functions 脚本连接到 Cloud SQL。

我在项目中创建了一个 Cloud SQL 实例,然后创建了一个 Cloud Function,在其中我将整个脚本从链接粘贴到内联编辑器中。我将环境变量设置为连接到 Cloud SQL 所需的环境变量,但出现错误。

为了完成这项工作,我是否缺少一些明显的东西?

错误:函数崩溃。细节: (2003 年,“无法连接到 'localhost' 上的 MySQL 服务器([Errno 111] 连接被拒绝)”)

Traceback(最近一次调用最后):文件“/env/local/lib/python3.7/site-packages/pymysql/connections.py”,第 582 行,在连接 **kwargs)文件“/opt/python3 .7/lib/python3.7/socket.py”,第 727 行,在 create_connection 中引发错误文件“/opt/python3.7/lib/python3.7/socket.py”,第 716 行,在 create_connection sock.connect( sa) ConnectionRefusedError: [Errno 111] Connection refused 在处理上述异常期间,发生了另一个异常: Traceback (last recent call last): File "/user_code/main.py", line 49, in mysql_demo mysql_conn = pymysql.connect( **mysql_config) 文件“/env/local/lib/python3.7/site-packages/pymysql/init.py”,第 94 行,在 Connect 返回 Connection(*args, **kwargs)文件“/env/local/lib/python3.7/site-packages/pymysql/connections.py”,第 327 行,在 init self.connect() 文件“/env/local/lib/ python3.7/site-packages/pymysql/connections.py”,第 629 行,在连接中引发 exc pymysql.err.OperationalError:(2003,“无法连接到 MySQL 服务器 o n 'localhost' ([Errno 111] Connection denied)") 在处理上述异常的过程中,又出现了一个异常: Traceback (last recent call last): File "/env/local/lib/python3.7/site-packages/ pymysql/connections.py", line 570, in connect sock.connect(self.unix_socket) ConnectionRefusedError: [Errno 111] Connection refused 在处理上述异常的过程中,又发生了一个异常: Traceback (last last call last): File "/ env/local/lib/python3.7/site-packages/google/cloud/functions_v1beta2/worker.py”,第 297 行,在 run_http_function 结果 = _function_handler.invoke_user_function(flask.request) 文件“/env/local/lib/python3 .7/site-packages/google/cloud/functions_v1beta2/worker.py”,第 199 行,invoke_user_function return call_user_function(request_or_event) 文件“/env/local/lib/python3.7/site-packages/google/cloud/functions_v1beta2 /worker.py”,第 192 行,在 call_user_function 返回 self._user_function(request_or_event) 文件“/user_code/main.py”,第 53 行,在 mysql_demo mysql _conn = pymysql.connect(**mysql_config) 文件“/env/local/lib/python3.7/site-packages/pymysql/init.py”,第 94 行,在 Connect 返回 Connection(* args, **kwargs) 文件“/env/local/lib/python3.7/site-packages/pymysql/connections.py”,第 327 行,在 init self.connect() 文件中“/ env/local/lib/python3.7/site-packages/pymysql/connections.py”,第 629 行,在连接中引发 exc pymysql.err.OperationalError:(2003,“无法连接到 'localhost' 上的 MySQL 服务器( [Errno 111] 连接被拒绝)")

【问题讨论】:

它无法连接并默认使用本地主机进行开发测试,显然 CF 没有运行 sql 实例,因此失败。您是否尝试通过实例的私有 ip 进行连接?我会仔细检查你的连接字符串。 我使用的是 Google 提供的示例中的确切代码。我的理解是它不是私有IP,因为CF可以直接连接到Cloud SQL,所以我可以使用“连接名称”作为服务器,格式为f'/cloudsql/project.region.instance' INSTANCE_CONNECTION_NAME 的格式应为 <project_id>:<region>:<instance_id>。您可以从“实例连接名称”下的“实例概述”页面复制它。您可以尝试一下并告诉我们它是否有效吗? @LoxBagel 您是否有时间尝试以 :: 形式使用 INSTANCE_CONNECTION_NAME 的代码?如果是这样,请回答问题以造福社区。​​span> 我确实使用了这种格式,但我仍然得到同样的错误。看来只复制示例代码并运行是行不通的。 【参考方案1】:

非常重要的信息:

在开始之前,您需要转到 GCP IAM 将云 SQL 管理员角色添加到云功能服务帐户。服务账户信息可以在云功能的常规选项卡中找到。完成此操作后,您应该一切顺利。如果没有,请尝试向您的云功能服务帐户添加更多角色,例如项目编辑器。

代码参考,(仅部分改动)

# TODO(developer): specify SQL connection details  
CONNECTION_NAME = getenv(
'INSTANCE_CONNECTION_NAME',
'proj-chatbot-og:us-central1:your connection name')
# Please don't change the name on the left like MYSQL_USER
DB_USER = getenv('MYSQL_USER', 'your user name')
DB_PASSWORD = getenv('MYSQL_PASSWORD', 'your password')
DB_NAME = getenv('MYSQL_DATABASE', 'your sql database')


mysql_config = 
'user': DB_USER,
'password': DB_PASSWORD,
'db': DB_NAME,
'charset': 'utf8mb4',
'cursorclass': pymysql.cursors.DictCursor,
'autocommit': True

完整的文档可以在这里找到https://cloud.google.com/functions/docs/sql

如果您发现解决方案解决了您的问题,请为解决方案投票。

【讨论】:

我将角色添加到与该功能绑定的服务帐户,但仍然收到连接拒绝。有什么我可以尝试的吗? 我不得不将云功能高级设置中的默认服务帐户从App Engine default service account更改为Compute Engine default service account。我认为我的默认应用程序引擎用户已损坏,因为它具有 apppot 传统。为了弄清楚这一点,我拔了四个小时的头发。【参考方案2】:

我遇到了同样的问题,在与 Google 反复多次后,这终于为我解决了。

示例显示以下 try 子句:

if not mysql_conn:
    try:
        mysql_conn = pymysql.connect(**mysql_config)
    except OperationalError:
        # If production settings fail, use local development ones
        mysql_config['unix_socket'] = f'/cloudsql/CONNECTION_NAME'
        mysql_conn = pymysql.connect(**mysql_config)

我改成:

if not mysql_conn:
    mysql_config['unix_socket'] = f'/cloudsql/CONNECTION_NAME'
    mysql_conn = pymysql.connect(**mysql_config)

并使用(data, context)作为函数参数而不是(request)

这就是我最终作为测试函数的结果:

from os import getenv

import pymysql
from pymysql.err import OperationalError

CONNECTION_NAME = getenv(
  'INSTANCE_CONNECTION_NAME',
  'connection_name')
DB_USER = getenv('MYSQL_USER', 'username')
DB_PASSWORD = getenv('MYSQL_PASSWORD', 'password')
DB_NAME = getenv('MYSQL_DATABASE', 'database')

mysql_config = 
  'user': DB_USER,
  'password': DB_PASSWORD,
  'db': DB_NAME,
  'charset': 'utf8mb4',
  'cursorclass': pymysql.cursors.DictCursor,
  'autocommit': True


if getenv('NODE_ENV', '') == 'production':
    mysql_config['unix_socket'] = f'/cloudsql/CONNECTION_NAME'

# Create SQL connection globally to enable reuse
# PyMySQL does not include support for connection pooling
mysql_conn = None


def __get_cursor():
    """
    Helper function to get a cursor
    PyMySQL does NOT automatically reconnect,
    so we must reconnect explicitly using ping()
    """
    try:
        return mysql_conn.cursor()
    except OperationalError:
        mysql_conn.ping(reconnect=True)
        return mysql_conn.cursor()


def mysql_demo(data, context):
    global mysql_conn

    # Initialize connections lazily, in case SQL access isn't needed for this
    # GCF instance. Doing so minimizes the number of active SQL connections,
    # which helps keep your GCF instances under SQL connection limits.
    if not mysql_conn:
        mysql_conn = pymysql.connect(**mysql_config)

    # Remember to close SQL resources declared while running this function.
    # Keep any declared in global scope (e.g. mysql_conn) for later reuse.
    with __get_cursor() as cursor:
        cursor.execute('SELECT NOW() as now')
        results = cursor.fetchone()
        return str(results['now'])

【讨论】:

【参考方案3】:

同样的问题,但是

    App Engine StandardPostgreSQL 上 Cloud SQL 在不同项目B中运行,而不是在项目A中运行的应用代码。

根本问题看起来非常相同

从the tutorial 到点(同样,这是针对 Postgres,而不是 MySQL),因此使用来自 here 的示例代码,该解决方案几乎可以立即工作(没有发现配置延迟):

在项目 AIAM & Admin 部分中查找主服务帐户的电子邮件地址;它的格式应为 @appspot.gserviceaccount.com 在项目 B 中,在 IAM & Admin 下添加一行,此电子邮件地址为 principal 和角色 Cloud SQL Client

教程讲的

如果授权服务帐号与 Cloud SQL 实例属于不同的项目,则需要为这两个项目添加 Cloud SQL Admin API 和 IAM 权限。

我确实将 Cloud SQL API 和 Cloud SQL Admin API 添加到两个项目,但没有为项目 A 中的服务帐户提供任何与 Cloud SQL 相关的权限 --如果这就是教程所说的。

以上暗示如果 Cloud SQL 和应用代码都在在同一个项目中运行,那么将 Cloud SQL Client 角色添加到项目的服务帐户应该就是这样需要。 (@Zou Dino 建议的角色 Cloud SQL Admin 不是必需的,但也应该可以工作。)

【讨论】:

以上是关于连接到 Cloud SQL 时,Cloud Functions “连接被拒绝”的主要内容,如果未能解决你的问题,请参考以下文章

Spring GCP 服务未连接到 Cloud SQL 数据库

Cloud Run 完全托管连接到 Cloud SQL:这是不是支持 SQL Server?

通过启用 IAM 登录的 cloud-sql-proxy 从 Cloud Run 连接到 Cloud SQL

如何从 Cloud Run 安全地连接到 Cloud SQL?

Spring Boot 应用在部署到 Google App Engine 时无法连接到 Google Cloud SQL (PostgreSQL)

将 Django 连接到 Google Cloud SQL