Cloud Run Servisini Cloud SQL Örneğine Bağlamak
Merhabalar, bu yazımda Cloud Run üstünde koşan bir servisi Cloud SQL örneğine nasıl bağladığımıza bakacağız. Konu kâğıt üstünde basit duruyor ama ilk denediğinizde 'connection refused' veya 'too many connections' hatasıyla karşılaşmamak için bilmeniz gereken birkaç ufak detay var. Lafı çok uzatmadan başlayayım.
Cloud Run, GCP'nin sunucusuz container platformu. Trafik geldiğinde sıfırdan N instance'a ölçekleniyor, gelmeyince yine sıfıra iniyor. Bu güzel ama veritabanı tarafında bir gerçeklik var: Cloud SQL örneğinin sınırlı sayıda bağlantısı var ve siz patlayan instance sayısıyla doğru orantılı bağlantı açarsanız hızla duvara toslarsınız. O yüzden bağlantı şeklini ve havuzu doğru kurmak işin yarısı.
Bağlantı için iki yol var
Pratikte iki ana yaklaşım kullanılıyor:
- Yerleşik Cloud SQL connector + Unix socket: Servisi deploy ederken
--add-cloudsql-instancesparametresini geçiyorsunuz, Cloud Run container içine/cloudsql/<INSTANCE_CONNECTION_NAME>yolunda bir Unix socket bind ediyor. Public IP veya private IP fark etmiyor, akış GCP'nin kendi proxy'si üzerinden geçiyor. - Cloud SQL Connector kütüphaneleri: Python için
cloud-sql-python-connector, Java içinpostgres-socket-factorygibi paketler. IAM Authentication ve TLS'i programatik olarak yönetiyorlar. IAM tabanlı auth kullanmak istiyorsanız genelde tercih edilen yol.
Bence ilk projede socket yaklaşımı yeter. Connector kütüphaneleri IAM auth veya çok özel ihtiyaçlar varsa devreye girer; basit bir CRUD servisinde fazladan bağımlılık.
IAM ve servis hesabı
İlk yapılacak iş Cloud Run servisinin koştuğu service account'a roles/cloudsql.client rolünü vermek. Cloud SQL Admin API'sinin de açık olması lazım; aksi halde proxy bağlantı kuramıyor.
Connection name'i almakla başlayalım:
gcloud sql instances describe my-postgres \
--format='value(connectionName)'
# ornegin: my-project:europe-west1:my-postgres
Bu üçlü project:region:instance ileride deploy ederken sürekli işimize yarayacak. Sonrasında deploy:
gcloud run deploy payment-api \
--image=europe-west1-docker.pkg.dev/my-project/apps/payment-api:1.4.0 \
--region=europe-west1 \
--service-account=payment-api-sa@my-project.iam.gserviceaccount.com \
--add-cloudsql-instances=my-project:europe-west1:my-postgres \
--set-env-vars=DB_HOST=/cloudsql/my-project:europe-west1:my-postgres,DB_USER=payment,DB_NAME=payments \
--set-secrets=DB_PASS=db-password:latest \
--max-instances=20
Burada dikkat etmenizi istediğim iki şey var. Birincisi şifreyi --set-env-vars ile değil --set-secrets ile geçiyoruz; düz metin parola env'e asla yazılmaz, Secret Manager bunun için var. İkincisi --max-instances koymak. Cloud Run kendi haline bırakılırsa yüzlerce instance açabilir, her biri bağlantı tutar, Cloud SQL'in max_connections limiti kısa sürede dolar.
Uygulama tarafı
Python tarafında SQLAlchemy ile bağlanırken host olarak /cloudsql/... socket yolunu veriyoruz. URL'de host yerine query parametresinde belirtmek gerekiyor, çünkü TCP gibi davranmıyor:
import os
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
instance = os.environ['DB_HOST'] # /cloudsql/proj:region:instance
url = URL.create(
drivername='postgresql+psycopg2',
username=os.environ['DB_USER'],
password=os.environ['DB_PASS'],
database=os.environ['DB_NAME'],
query={'host': instance},
)
engine = create_engine(
url,
pool_size=3,
max_overflow=2,
pool_pre_ping=True,
pool_recycle=1800,
)
pool_size=3 ve max_overflow=2 belki size düşük gelebilir. Ama hesabı yapın: 20 instance × 5 bağlantı = 100. Cloud SQL küçük bir tier'daysa default max_connections zaten 100-200 civarında. Pool'u büyütmeden önce instance sayısını sınırlamak daha sağlıklı.
pool_pre_ping=True da kritik. Cloud Run instance'ları cold start sonrası uyuyabiliyor, bağlantı kopuk olabiliyor; pre-ping olmazsa ilk istek hata yiyor.
Private IP isteyenler için kısa not
Eğer Cloud SQL örneği sadece private IP ile erişilebiliyorsa, Cloud Run'ın VPC'ye girmesi lazım. Bunun için Serverless VPC Access connector oluşturuyoruz ve deploy sırasında --vpc-connector ile bağlıyoruz. Bu yazıda detaya girmeyeceğim ama unutmayın: socket yöntemi public IP yokken bile çalışır, çünkü Cloud SQL Auth Proxy GCP içinden konuşuyor. Yani private IP zorunluluğu güvenlik gerekçesiyle değilse, socket genelde yeterli.
Sık karşılaşılan tuzaklar
connection refusedhatası: Çoğunlukla--add-cloudsql-instancesadı yanlış yazılmış veya servis hesabındaroles/cloudsql.clientrolü yok. Bir de Cloud SQL Admin API'nin açık olduğunu kontrol edin.too many connections: Pool size'ı küçültün,--max-instanceskoyun. Gerekirse Cloud SQL tarafındamax_connectionsflag'ini artırın ama bu ikinci adım; önce uygulama tarafını disipline edin.- Connection timeout: Cold start sırasında Cloud SQL Auth Proxy'nin ayağa kalkması bir-iki saniye sürebiliyor. Container'ın
--timeoutdeğerini ve client tarafındaki connect timeout'u makul tutun,pool_pre_pingzaten bayat soketleri eler. - Plaintext parola:
--set-env-vars=DB_PASS=...yazma alışkanlığı çok yaygın. Loglara, revision history'ye sızar. Secret Manager'a 30 saniye veriyorsunuz, ömür boyu rahat ediyorsunuz.
Kapanış
Cloud Run + Cloud SQL ikilisi doğru kurulduğunda çok rahat çalışıyor; iş, ölçeklenmenin veritabanı tarafında yarattığı baskıyı ciddiye almakta. Bana sorarsanız küçük pool, sıkı max-instances ve Secret Manager üçlüsü neredeyse her senaryoya yetiyor; gerisi konfor. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.
