As the title says, I have a Flask app, running on cloud run that I am attempting to connect to Cloud SQL where I have a postgres instance.
After much debugging I stripped down the app to the bare minimum and I receive the following error in the Cloud Run logs:
Cloud SQL instance "<instance-name>" is not reachable. Deploy a new revision adding the Cloud SQL connection. See documentation: https://cloud.google.com/sql/docs/mysql/connect-run
But I do have a cloud SQL connection configured.
This is then followed by a long python stacktrace as we'd expect if there is no db connection, which I don't think is useful so I haven't included it here.
TL;DR: I have a feeling the issue is that I'm using the incorrect DB_HOST
IP address in order to make sure SQL Auth proxy is used, what should I be using in the DB_URI
string?
I followed the steps from this post which is essentially the same as the official documentation.
A summary of what I did:
- I have a PostgreSQL instance with a public IP
- I made a new table
- I deployed my code using
gcloud run deploy
- I added the
Cloud SQL Client
role to the service account used for the cloud run service. - I made a revision in which I added the Cloud SQL connection to the cloud run service as detailed in the docs above
- I also added
DB_USER
,DB_HOST
,DB_PASS
etc as environment variables in a revision.
I tested this locally using the SQL Auth Proxy and it works as expected. In the terminal running the auth proxy I can see a line New connection for "<instance-connection-name>"
so it works locally. I used 127.0.0.1
as the DB_HOST
when running locally. For context my DB URI is different on my local machine because I'm running Windows and I can't use unix sockets.
In cloud run I have tried setting DB_HOST
to both 127.0.0.1
and the DB public IP address, both produce the same error.
my ./run.py
file that I use to run the app.
from sampleapp import app
if __name__ == "__main__":
app.run(debug=True, host='127.0.0.1', port=int(os.environ.get("PORT", 8000)))
my ./sampleapp/__init__.py
which contains app instantiation.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os
LOCAL_TESTING = bool(os.environ.get("LOCAL_TESTING", False)) # Set to True if running locally
DB_USER = os.getenv('DB_USER')
DB_PASS = os.getenv('DB_PASS')
DB_NAME = os.getenv('DB_NAME')
DB_HOST = os.getenv('DB_HOST')
DB_PORT = os.getenv('DB_PORT')
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['DEBUG'] = os.getenv('DEBUG', False)
if LOCAL_TESTING == True:
# This is what runs on my local machine
print("Using tcp connection to db")
DB_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
else:
# This run on cloud run
print("Using unix_sock connection to db")
db_socket_dir = os.environ.get("DB_SOCKET_DIR", "/cloudsql")
DB_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@/{DB_NAME}?unix_sock={db_socket_dir}/{DB_INSTANCE_NAME}/.s.PGSQL.5432'
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
db = SQLAlchemy(app)
from sampleapp import routes
The DB URIs above are taken from the code snippets in the docs but instead of using sqlalchemy.create_engine()
I'm using flask-sqlalchemy and instead of sqlalchemy.engine.url.URL.create()
I'm just making my own URI string.
The excecution line from my ./Dockerfile
:
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 run:app
From my cloud run logs I can see that my routes work fine up until the point I try to access the DB.
From reading this answer and the docs on the Cloud SQL Python connector I should be using the connector code from google but I don't know how to use this in the context of flask_sqlalchemy
because google always uses standard sqlalchemy. I couldn't work out how to assign the engine
object in google's code snippets to the flask-sqlalchemy SQLAlchemy
object that I am using for db
.
Apologies for the long question I have been trying to debug this for a long time to no avail. Any suggestions are greatly appreciated.