GCP Cloud Run ile Konteyner Dağıtımı: Adım Adım Rehber
Konteyner teknolojisi hayatımıza girdiğinden beri “bunu nereye koşturacağız?” sorusu her zaman gündemde oldu. VM kurmak, Kubernetes cluster yönetmek, load balancer ayarlamak… Bunların hepsi zaman ve enerji isteyen işler. Google Cloud Run tam da bu noktada devreye giriyor: Konteyneri yükle, çalıştır, unut. Altyapıyı Google halleder. Bu yazıda Cloud Run’ı gerçek bir prodüksiyon senaryosu üzerinden, sysadmin gözüyle ele alacağız.
Cloud Run Nedir, Ne Değildir?
Cloud Run, Google’ın fully managed konteyner çalıştırma platformudur. Serverless kategorisinde yer alır ama klasik serverless’tan (Cloud Functions gibi) farkı, standart bir Docker imajı çalıştırabilmeniz. Yani uygulamanızı özel bir runtime’a uyarlamanız gerekmiyor.
Ne zaman Cloud Run tercih edilmeli?
- HTTP/gRPC tabanlı stateless uygulamalar
- Trafik tahmin edilemeyen, ani spike’lar olan servisler
- CI/CD pipeline’ına hızlı entegrasyon istenen projeler
- Kubernetes’in karmaşıklığını taşımak istemediğiniz mikro servisler
- Cron job veya event-driven workload’lar
Ne zaman Cloud Run yeterli olmaz?
- Stateful uygulamalar (veritabanları, persistent storage gerektiren servisler)
- WebSocket bağlantıları uzun süre açık kalacaksa (destekliyor ama limitli)
- GPU gerektiren ML workload’ları için (Cloud Run GPU desteği hâlâ sınırlı)
- Konteyner başlatma süresi kritikse (cold start sorunu var)
Ortam Hazırlığı
Başlamadan önce gcloud CLI kurulu ve authenticate edilmiş olmalı. Eğer yoksa:
# gcloud CLI kurulumu (Linux)
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
gcloud init
# Mevcut konfigürasyonu kontrol et
gcloud config list
# Proje ayarla
gcloud config set project YOUR_PROJECT_ID
# Gerekli API'leri aktifleştir
gcloud services enable run.googleapis.com
gcloud services enable containerregistry.googleapis.com
gcloud services enable artifactregistry.googleapis.com
Artifact Registry, eski Container Registry’nin yerini aldı. Yeni projelerde Artifact Registry kullanmanızı öneririm.
# Artifact Registry repository oluştur
gcloud artifacts repositories create my-app-repo
--repository-format=docker
--location=europe-west1
--description="Uygulama konteyner deposu"
# Docker'ı Artifact Registry ile authenticate et
gcloud auth configure-docker europe-west1-docker.pkg.dev
Örnek Uygulama ve Dockerfile
Gerçek bir senaryo üzerinden gidelim. Bir Python Flask API’si dağıtacağız. Basit ama prodüksiyon prensiplerine uygun bir yapı kuracağız.
# Proje yapısı
mkdir cloudrun-demo && cd cloudrun-demo
touch app.py requirements.txt Dockerfile .dockerignore
app.py içeriği:
cat > app.py << 'EOF'
import os
from flask import Flask, jsonify
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
@app.route("/")
def index():
return jsonify({"status": "ok", "version": os.getenv("APP_VERSION", "1.0.0")})
@app.route("/health")
def health():
return jsonify({"healthy": True}), 200
if __name__ == "__main__":
port = int(os.getenv("PORT", 8080))
app.run(host="0.0.0.0", port=port, debug=False)
EOF
Cloud Run, container’a PORT environment variable’ı otomatik enjekte eder. Uygulamanızın bu porta dinlemesi zorunlu. Bunu atlayan çok sayıda geliştirici gördüm, deployment başarılı görünür ama servis ayağa kalkmaz.
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
# Güvenlik: root olmayan kullanıcı oluştur
RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
# Dosya sahipliğini ayarla
RUN chown -R appuser:appuser /app
USER appuser
# Cloud Run PORT env variable kullanır
ENV PORT=8080
EXPOSE 8080
CMD ["python", "app.py"]
EOF
cat > .dockerignore << 'EOF'
__pycache__/
*.pyc
*.pyo
.env
.git
.gitignore
README.md
EOF
İmaj Build ve Push
# Değişkenleri tanımla
PROJECT_ID=$(gcloud config get-value project)
REGION="europe-west1"
REPO="my-app-repo"
IMAGE_NAME="flask-api"
TAG="v1.0.0"
IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${IMAGE_NAME}:${TAG}"
# Build et
docker build -t ${IMAGE_PATH} .
# Yerel test
docker run -p 8080:8080 -e PORT=8080 ${IMAGE_PATH}
curl http://localhost:8080/health
# Artifact Registry'ye push
docker push ${IMAGE_PATH}
Cloud Build kullanmak isteyenler için (local Docker daemon gerektirmez, CI/CD için ideal):
# Cloud Build ile direkt build ve push
gcloud builds submit
--tag ${IMAGE_PATH}
--region=${REGION}
.
Cloud Run’a Deploy
Temel bir deployment:
gcloud run deploy flask-api
--image ${IMAGE_PATH}
--region ${REGION}
--platform managed
--allow-unauthenticated
--port 8080
--memory 512Mi
--cpu 1
--min-instances 0
--max-instances 10
--timeout 30
Bu komuttaki parametreleri açıklayalım:
- –allow-unauthenticated: Herkese açık endpoint. Internal servis için bu flag’i kaldırın
- –min-instances 0: Cold start yaşarsınız ama para ödemezsiniz. Prod için 1 veya daha fazla düşünün
- –max-instances 10: Ani trafik spike’larında sonsuz scale-out’u engeller, bütçenizi korur
- –timeout 30: Request timeout saniye cinsinden. Default 300, ihtiyaca göre ayarlayın
- –memory 512Mi: Konteyner başına bellek limiti
- –cpu 1: Konteyner başına CPU birimi (1 = 1 vCPU)
Deploy sonrası servis URL’ini alın:
SERVICE_URL=$(gcloud run services describe flask-api
--region=${REGION}
--format='value(status.url)')
echo "Servis URL: ${SERVICE_URL}"
curl ${SERVICE_URL}/health
Environment Variables ve Secret Yönetimi
Prodüksiyonda environment variable’ları düzgün yönetmek kritik. Database şifrelerini, API anahtarlarını asla imaj içine gömmeyin.
# Düz environment variable (hassas olmayan değerler için)
gcloud run services update flask-api
--region ${REGION}
--set-env-vars APP_VERSION=1.0.0,LOG_LEVEL=INFO
# Secret Manager ile hassas değerleri yönet
# Önce secret oluştur
echo -n "super-secret-db-password" |
gcloud secrets create db-password
--data-file=-
--replication-policy=automatic
# Cloud Run servisine secret'ı environment variable olarak bağla
gcloud run services update flask-api
--region ${REGION}
--set-secrets DB_PASSWORD=db-password:latest
Secret Manager entegrasyonu için Cloud Run servis hesabına izin vermeniz gerekir:
# Servis hesabını öğren
SERVICE_ACCOUNT=$(gcloud run services describe flask-api
--region=${REGION}
--format='value(spec.template.spec.serviceAccountName)')
# Secret'a erişim izni ver
gcloud secrets add-iam-policy-binding db-password
--member="serviceAccount:${SERVICE_ACCOUNT}"
--role="roles/secretmanager.secretAccessor"
Traffic Splitting ve Canary Deployment
Cloud Run’ın en güçlü özelliklerinden biri revision tabanlı trafik yönetimi. Yeni bir versiyon dağıtırken riskleri minimize etmek için canary deployment yapabilirsiniz.
# Yeni versiyon deploy et, ama trafik gönderme
gcloud run deploy flask-api
--image ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${IMAGE_NAME}:v1.1.0
--region ${REGION}
--no-traffic
--tag canary
# Mevcut revision'ları listele
gcloud run revisions list
--service flask-api
--region ${REGION}
# Trafiği böl: %90 stable, %10 canary
gcloud run services update-traffic flask-api
--region ${REGION}
--to-revisions REVISION_NAME_STABLE=90,REVISION_NAME_CANARY=10
Canary test başarılıysa tam geçiş:
# Tüm trafiği yeni versiyona aktar
gcloud run services update-traffic flask-api
--region ${REGION}
--to-latest
Bu özellik özellikle staging’den prod’a geçişlerde hayat kurtarıyor. Birkaç ay önce bir e-ticaret projesinde %5 canary ile başlayıp 30 dakika gözlemledikten sonra %100’e geçtik. Eski yöntemde tüm deploy’u geri almak zorunda kalırdık.
Concurrency ve Scaling Ayarları
Cloud Run’da her konteyner instance’ı eş zamanlı birden fazla request işleyebilir. Bu Fargate veya Lambda’dan farklı bir yaklaşım.
# Concurrency ve scaling ayarları
gcloud run services update flask-api
--region ${REGION}
--concurrency 80
--min-instances 1
--max-instances 50
--cpu-throttling
- –concurrency 80: Bir instance aynı anda 80 request işler. Thread-safe olmayan uygulamalarda düşürün
- –min-instances 1: Cold start’ı önlemek için en az 1 instance her zaman ayakta
- –cpu-throttling: Request yokken CPU’yu kısıtla, para tasarrufu sağlar
CPU her zaman aktif olsun istiyorsanız:
gcloud run services update flask-api
--region ${REGION}
--no-cpu-throttling
--min-instances 1
Background işlemler yapan, cron job benzeri uygulamalar için CPU throttling’i kapatın, aksi hâlde process durabilir.
VPC Connector ile Private Network Erişimi
Cloud Run servisleri default olarak internette çalışır. Cloud SQL, Redis, iç ağdaki kaynaklara erişmek için VPC connector gerekir.
# VPC Access Connector oluştur
gcloud compute networks vpc-access connectors create my-vpc-connector
--region ${REGION}
--network default
--range 10.8.0.0/28
--min-throughput 200
--max-throughput 1000
# Cloud Run servisini VPC'ye bağla
gcloud run services update flask-api
--region ${REGION}
--vpc-connector my-vpc-connector
--vpc-egress all-traffic
–vpc-egress seçenekleri:
- all-traffic: Tüm çıkış trafiği VPC üzerinden geçer
- private-ranges-only: Sadece RFC 1918 adresleri VPC’den geçer, geri kalanı internete direkt gider
Cloud SQL için Private IP kullanıyorsanız all-traffic tercih edin. Hem daha güvenli hem de daha hızlı bağlantı sağlar.
Cloud SQL Entegrasyonu
Gerçek dünya senaryolarının büyük çoğunluğunda bir veritabanı bağlantısı var. Cloud Run ile Cloud SQL’i bağlamanın en temiz yolu Cloud SQL Auth Proxy kullanmak:
# Cloud SQL bağlantısı için servis hesabına izin ver
gcloud projects add-iam-policy-binding ${PROJECT_ID}
--member="serviceAccount:${SERVICE_ACCOUNT}"
--role="roles/cloudsql.client"
# Cloud Run deploy sırasında Cloud SQL bağlantısını tanımla
gcloud run services update flask-api
--region ${REGION}
--add-cloudsql-instances ${PROJECT_ID}:${REGION}:my-database
--set-env-vars DB_HOST=/cloudsql/${PROJECT_ID}:${REGION}:my-database
Cloud Run, Cloud SQL Auth Proxy’yi otomatik olarak sidecar olarak çalıştırır. Unix socket üzerinden bağlantı kurulur, şifreli ve IAM tabanlıdır.
Monitoring ve Logging
Cloud Run, Google Cloud Logging ve Monitoring ile entegre gelir. Ama birkaç şeyi manuel yapılandırmak gerekebilir.
# Servis loglarını takip et (canlı)
gcloud logging read
"resource.type=cloud_run_revision AND resource.labels.service_name=flask-api"
--limit 50
--format "table(timestamp, textPayload)"
--freshness 1h
# Revision bazlı metrikler
gcloud monitoring metrics list
--filter="metric.type:run.googleapis.com"
Uptime check oluşturmak için:
# Basit HTTP uptime check
gcloud monitoring uptime create
--display-name="Flask API Health Check"
--resource-type=cloud-run-revision
--service=flask-api
--region=${REGION}
--path=/health
--period=60
Alert policy için Console üzerinden yapmanızı öneririm, gcloud CLI ile alert oluşturmak verbose. Özellikle şu metriklere alert koyun:
run.googleapis.com/request_latencies– P99 latency spike’larırun.googleapis.com/request_count– Hata oranı (5xx response’lar)run.googleapis.com/container/instance_count– Maksimum instance sayısına yaklaşım
CI/CD Pipeline Entegrasyonu
Manuel deployment production’da kabul edilemez. GitHub Actions ile basit bir pipeline kuralım:
# .github/workflows/deploy.yml içeriği
cat > deploy-workflow-example.sh << 'EOF'
# Bu bir referans komut dizisi, GitHub Actions YAML formatında düzenleyin
# 1. Google Cloud kimlik doğrulama
# Workload Identity Federation kullanın, service account key JSON KULLANMAYIN
# 2. İmaj build
gcloud builds submit
--tag ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${IMAGE_NAME}:${GITHUB_SHA}
--region=${REGION}
# 3. Deploy
gcloud run deploy flask-api
--image ${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${IMAGE_NAME}:${GITHUB_SHA}
--region ${REGION}
--platform managed
# 4. Smoke test
SERVICE_URL=$(gcloud run services describe flask-api
--region=${REGION}
--format='value(status.url)')
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" ${SERVICE_URL}/health)
if [ "${RESPONSE}" != "200" ]; then
echo "Health check failed! Rolling back..."
gcloud run services update-traffic flask-api
--region ${REGION}
--to-revisions PREVIOUS_REVISION=100
exit 1
fi
echo "Deployment successful!"
EOF
Workload Identity Federation kullanmak için service account JSON key’ini GitHub Secrets’a koymayın. Bu eski ve güvensiz bir yöntem. Bunun yerine OIDC tabanlı Workload Identity Federation kurun, çok daha güvenli.
Maliyet Optimizasyonu
Cloud Run’ın fiyatlandırması kullandığın kadar öde modelinde. Ama dikkat edilmesi gereken noktalar var:
# Mevcut servislerin kaynak kullanımını görüntüle
gcloud run services list
--platform managed
--region ${REGION}
--format="table(metadata.name,spec.template.spec.containers[0].resources.limits.memory,spec.template.spec.containers[0].resources.limits.cpu)"
# Kullanılmayan servisleri listele
gcloud run services list
--platform managed
--region ${REGION}
--filter="status.conditions[0].status=True"
Maliyet düşürme için pratik öneriler:
- –min-instances 0 kullanın: Dev ve staging ortamlarda kesinlikle. Prod’da cold start kabul edilebilirse yine 0 yapın
- CPU ve memory’yi gerçek kullanıma göre ayarlayın: Her servis 2 CPU + 2GB almasın
- –cpu-throttling aktif tutun: Background process yoksa CPU’yu request sırasında kullanmak yeterli
- –max-instances sınırı koyun: Sonsuz scale-out bütçenizi patlatır
- Revision temizliği yapın: Eski revision’lar log ve metadata tutmaya devam eder
# 30 günden eski revision'ları temizle (dikkatli kullan!)
CUTOFF_DATE=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%S)
gcloud run revisions list
--service flask-api
--region ${REGION}
--filter="metadata.creationTimestamp < ${CUTOFF_DATE}"
--format="value(metadata.name)" |
xargs -I {} gcloud run revisions delete {}
--region ${REGION}
--quiet
Yaygın Sorunlar ve Çözümleri
Cold Start Sorunu: Min instance 0 olduğunda ilk istek yavaş gelir. Çözüm olarak --min-instances 1 ayarlayın veya startup CPU boost aktifleştirin:
gcloud run services update flask-api
--region ${REGION}
--cpu-boost
Container başlamıyor: En sık yapılan hata PORT variable’ı dinlememek. Logları kontrol edin:
gcloud logging read
"resource.type=cloud_run_revision
AND resource.labels.service_name=flask-api
AND severity>=ERROR"
--limit 20
--format "table(timestamp,textPayload)"
Memory limit aşımı: OOM kill loglarına bakın. Belleği artırın veya uygulamayı optimize edin. Konteyner başına 8GB’a kadar çıkabilirsiniz.
Timeout hataları: Default 300 saniye. Uzun süren işlemler için Cloud Tasks veya Pub/Sub ile async mimari kurun, Cloud Run’ı sync request handler olarak kullanın.
Sonuç
Cloud Run, doğru use case için mükemmel bir araç. Stateless HTTP servisler, API gateway’ler, webhook handler’lar, event-driven işlemler için ideal. Kubernetes’in karmaşıklığına katlanmadan konteyner ekosisteminin avantajlarını kullanmanızı sağlıyor.
Bu yazıda temel deployment’tan başlayıp trafik bölme, VPC entegrasyonu, secret yönetimi ve CI/CD pipeline’a kadar bir prodüksiyon kurulumunun temel taşlarını ele aldık. Her projenin gereksinimleri farklı, ama bu altyapı üzerine inşa edilen servisler genellikle güvenilir çalışıyor.
En kritik nokta olarak şunu vurgulayayım: min-instances, max-instances ve timeout değerlerini asla default bırakmayın. Bu üç parametre hem performansınızı hem de bütçenizi doğrudan etkiliyor. Prodüksiyona atmadan önce load test yapın, metrikleri gözlemleyin, sonra değerleri gerçek kullanıma göre fine-tune edin.
Sorularınız veya eklemek istedikleriniz varsa yorumlarda buluşalım.
