5

I am trying to follow along with the example provided by Google to connect a Cloud Functions script to Cloud SQL with Python and a MySQL instance.

I have created a Cloud SQL instance in the project and then created a Cloud Function in which I pasted the entire script from the link into the inline editor. I set the environment variables equal to what they need to be in order to connect to Cloud SQL but I am getting an error.

Is there something obvious I am missing in order to make this work?

Error: function crashed. Details: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")

Traceback (most recent call last): File "/env/local/lib/python3.7/site-packages/pymysql/connections.py", line 582, in connect **kwargs) File "/opt/python3.7/lib/python3.7/socket.py", line 727, in create_connection raise err File "/opt/python3.7/lib/python3.7/socket.py", line 716, in create_connection sock.connect(sa) ConnectionRefusedError: [Errno 111] Connection refused During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/user_code/main.py", line 49, in mysql_demo mysql_conn = pymysql.connect(**mysql_config) File "/env/local/lib/python3.7/site-packages/pymysql/init.py", line 94, in Connect return Connection(*args, **kwargs) File "/env/local/lib/python3.7/site-packages/pymysql/connections.py", line 327, in init self.connect() File "/env/local/lib/python3.7/site-packages/pymysql/connections.py", line 629, in connect raise exc pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)") During handling of the above exception, another exception occurred: Traceback (most 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 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/env/local/lib/python3.7/site-packages/google/cloud/functions_v1beta2/worker.py", line 297, in run_http_function result = _function_handler.invoke_user_function(flask.request) File "/env/local/lib/python3.7/site-packages/google/cloud/functions_v1beta2/worker.py", line 199, in invoke_user_function return call_user_function(request_or_event) File "/env/local/lib/python3.7/site-packages/google/cloud/functions_v1beta2/worker.py", line 192, in call_user_function return self._user_function(request_or_event) File "/user_code/main.py", line 53, in mysql_demo mysql_conn = pymysql.connect(**mysql_config) File "/env/local/lib/python3.7/site-packages/pymysql/init.py", line 94, in Connect return Connection(*args, **kwargs) File "/env/local/lib/python3.7/site-packages/pymysql/connections.py", line 327, in init self.connect() File "/env/local/lib/python3.7/site-packages/pymysql/connections.py", line 629, in connect raise exc pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")

8
  • It's failing to connect and defaulting to the localhost for development testing, and obvious the CF isn't running a sql instance so that fails. Are you attempting to connect through the private ip of the instance? I'd double check your connection string. Commented Nov 15, 2018 at 20:24
  • I'm using the exact code in the example Google provided. My understanding is that it isnt a private ip since CF can connect to Cloud SQL directly so i can use the "connection name" as the server in the format of f'/cloudsql/project.region.instance' Commented Nov 16, 2018 at 15:28
  • The INSTANCE_CONNECTION_NAME should be of the form <project_id>:<region>:<instance_id>. You can copy it from the Instance Overview page under "Instance connection name". Can you try that and let us know if it works? Commented Nov 16, 2018 at 22:02
  • @LoxBagel did you have time to try the code with the INSTANCE_CONNECTION_NAME in form <project_id>:<region>:<instance_id>? If so, please, answer the question to the benefit of the community. Commented Nov 22, 2018 at 14:46
  • I did use that format, and I still get the same error. It appears that just copying the example code and running does not work. Commented Nov 23, 2018 at 15:31

3 Answers 3

4

Very important information:

Before you can get started, you need to go the GCP IAM to add the cloud SQL admin role to the cloud function service account. The service account information can be found in the general tab of the cloud function. After you complete this action, you should be good to go. If not, try to add more roles to your cloud function service account like project editor.

For code reference,( only part to change)

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

The full documentation can be found in here https://cloud.google.com/functions/docs/sql

Please vote the solution if you found the solution solved your issue.

Sign up to request clarification or add additional context in comments.

2 Comments

I added role to service account that bind with the function but still getting connection refuse. Are there anything that I can try?
I had to change my default service account in the advanced settings of the cloud function from App Engine default service account to Compute Engine default service account. I think my default app engine user is broken because it has appspot heritage. I was pulling out hairs for four hours trying to figure this one out.
0

I was having the same issue and after much back and forth with Google, this finally solved it for me.

The sample shows the following try clause:

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) 

I changed it to:

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

And used (data, context) as function arguments rather than (request)

This is what I ultimately ended up with as a test function:

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']) 

Comments

0

Same problem, but

  1. on App Engine Standard and with PostgreSQL
  2. Cloud SQL running within a different project B than the app code that is running in project A.

The underlying issue looks to be very much the same.

Following the tutorial to the dot (again, this is for Postgres, not MySQL), and thus using sample code from here, the solution that made things work almost immediately (no configuration delay noticed):

  • Look up the main service account's email address in project A's IAM & Admin section; it should have the format <Your project ID>@appspot.gserviceaccount.com
  • In project B, add a line under IAM & Admin with this email address as principal and role Cloud SQL Client

The tutorial talks about

If the authorizing service account belongs to a different project than the Cloud SQL instance, the Cloud SQL Admin API and IAM permissions will need to be added for both projects.

I did add Cloud SQL API and Cloud SQL Admin API to both projects, but did NOT give the service account in project A any Cloud SQL-related permissions -- if that's what the tutorial is talking about.

The above implies that if both Cloud SQL and the app code run within the same project, adding the Cloud SQL Client role to the project's service account should be all that's needed. (Role Cloud SQL Admin, as @Zou Dino suggests isn't required, but should work, too.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.