GCP Cloud Functions ile HTTP Fonksiyon Oluşturma
Serverless dünyasına adım atmak, altyapı yönetiminden kurtulup sadece kod yazmaya odaklanmak isteyenler için gerçek bir özgürleşme hissi veriyor. Google Cloud Platform’un sunduğu Cloud Functions servisi de tam olarak bu ihtiyacı karşılıyor. HTTP tetikleyicili fonksiyonlar oluşturmak, basit API endpoint’leri yazmaktan karmaşık webhook sistemleri kurgulamaya kadar geniş bir kullanım alanı sunuyor. Bu yazıda, GCP Cloud Functions üzerinde HTTP fonksiyon oluşturmayı, yapılandırmayı ve production ortamına taşımayı adım adım inceleyeceğiz.
Cloud Functions Nedir ve Neden Kullanırız?
Cloud Functions, Google’ın event-driven serverless compute platformudur. Sen sadece fonksiyonu yazarsın, GCP gerisini halleder. Sunucu provisioning yok, işletim sistemi yaması yok, ölçeklendirme endişesi yok. Fonksiyonun çalışmadığı anlarda para ödemezsin, bu da özellikle düşük veya değişken trafikli uygulamalar için büyük bir avantaj.
HTTP Cloud Functions özellikle şu senaryolarda parlıyor:
- Webhook alıcıları: GitHub, Stripe, Slack gibi servislerin webhook’larını karşılamak
- REST API endpoint’leri: Mobil uygulamalar veya frontend’ler için hafif API katmanı
- Form işleyicileri: Statik site form verilerini işlemek
- Veri dönüştürücüler: Bir formattan diğerine anlık dönüşüm servisleri
- Zamanlama tetikleyicileri: Cloud Scheduler ile birlikte periyodik görevler
Ortam Hazırlığı
GCP SDK ve gcloud Kurulumu
Önce yerel geliştirme ortamını hazırlayalım. gcloud CLI olmadan Cloud Functions ile ciddi iş yapılamaz.
# Linux/Debian tabanlı sistemler için gcloud kurulumu
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-456.0.0-linux-x86_64.tar.gz
tar -xf google-cloud-cli-456.0.0-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh
# Kurulum sonrası shell'i yenile
source ~/.bashrc
# gcloud'u başlat ve authenticate et
gcloud init
gcloud auth application-default login
# Proje ayarla ve gerekli API'ları etkinleştir
gcloud config set project PROJE_ADINIZ
# Cloud Functions ve Cloud Build API'larını aç
gcloud services enable cloudfunctions.googleapis.com
gcloud services enable cloudbuild.googleapis.com
gcloud services enable artifactregistry.googleapis.com
# Mevcut yapılandırmayı kontrol et
gcloud config list
Bu aşamada dikkat edilmesi gereken önemli bir nokta var: Cloud Functions 2. nesil (gen2), Cloud Run altyapısı üzerinde çalışıyor. Bazı özellikler sadece gen2’de mevcut, bu yüzden yeni projeler için gen2’yi tercih etmeni öneririm.
İlk HTTP Fonksiyonu: Hello World’den Gerçeğe
Temel Proje Yapısı
# Proje dizinini oluştur
mkdir -p gcf-http-demo && cd gcf-http-demo
# Python sanal ortamı oluştur (Python kullanıyorsak)
python3 -m venv venv
source venv/bin/activate
# Gerekli bağımlılıkları kur
pip install functions-framework flask
main.py dosyamızı oluşturalım:
cat > main.py << 'EOF'
import functions_framework
import json
import os
from datetime import datetime
@functions_framework.http
def http_handler(request):
"""
Ana HTTP handler fonksiyonu.
GET ve POST isteklerini işler.
"""
# CORS header'ları - özellikle frontend entegrasyonlarda şart
if request.method == 'OPTIONS':
headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '3600'
}
return ('', 204, headers)
headers = {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json'
}
if request.method == 'GET':
params = request.args
name = params.get('name', 'Dünya')
response_data = {
'message': f'Merhaba, {name}!',
'timestamp': datetime.utcnow().isoformat(),
'environment': os.environ.get('ENVIRONMENT', 'development')
}
return (json.dumps(response_data, ensure_ascii=False), 200, headers)
elif request.method == 'POST':
try:
request_json = request.get_json(silent=True)
if not request_json:
return (json.dumps({'error': 'Geçersiz JSON body'}), 400, headers)
# İş mantığı burada
result = process_data(request_json)
return (json.dumps(result, ensure_ascii=False), 200, headers)
except Exception as e:
error_response = {
'error': 'İşlem sırasında hata oluştu',
'detail': str(e)
}
return (json.dumps(error_response), 500, headers)
return (json.dumps({'error': 'Method not allowed'}), 405, headers)
def process_data(data):
"""Gelen veriyi işle"""
required_fields = ['name', 'email']
for field in required_fields:
if field not in data:
raise ValueError(f'Zorunlu alan eksik: {field}')
return {
'status': 'success',
'processed': True,
'received': data,
'processed_at': datetime.utcnow().isoformat()
}
EOF
cat > requirements.txt << 'EOF'
functions-framework==3.5.0
flask==3.0.0
google-cloud-secret-manager==2.18.0
google-cloud-firestore==2.13.1
EOF
Lokal Test
Fonksiyonu production’a göndermeden önce mutlaka lokal test yapmalısın:
# Functions framework ile lokal sunucu başlat
functions-framework --target=http_handler --port=8080 --debug
# Başka bir terminal'de test et
# GET isteği
curl "http://localhost:8080?name=Sysadmin"
# POST isteği
curl -X POST http://localhost:8080
-H "Content-Type: application/json"
-d '{"name": "Ahmet", "email": "[email protected]"}'
# CORS preflight test
curl -X OPTIONS http://localhost:8080
-H "Origin: https://siteniz.com"
-H "Access-Control-Request-Method: POST"
-v
Cloud’a Deploy Etmek
gcloud ile Deploy
# Gen2 fonksiyon olarak deploy et
gcloud functions deploy http-demo-fonksiyon
--gen2
--runtime=python311
--region=europe-west1
--source=.
--entry-point=http_handler
--trigger-http
--allow-unauthenticated
--memory=256Mi
--timeout=60s
--min-instances=0
--max-instances=10
--set-env-vars=ENVIRONMENT=production
--service-account=fonksiyon-sa@PROJE_ADINIZ.iam.gserviceaccount.com
Bu komuttaki parametrelerin ne işe yaradığını açıklayalım:
- –gen2: İkinci nesil Cloud Functions kullan (Cloud Run tabanlı)
- –runtime=python311: Python 3.11 runtime’ı kullan
- –region=europe-west1: Avrupa bölgesinde çalıştır (GDPR uyumu için önemli)
- –entry-point: Hangi fonksiyonun HTTP isteğini karşılayacağını belirtir
- –allow-unauthenticated: Public erişime açık (dikkatli kullan!)
- –memory=256Mi: Fonksiyona ayrılan RAM miktarı
- –timeout=60s: Maksimum çalışma süresi, 3600s’e kadar çıkabilir
- –min-instances=0: Cold start kabul ediyoruz, maliyet optimizasyonu için
- –max-instances=10: Maksimum instance sayısı, maliyet kontrolü için kritik
- –service-account: Fonksiyonun hangi kimlikle çalışacağı
# Deploy sonrası fonksiyon URL'ini al
gcloud functions describe http-demo-fonksiyon
--gen2
--region=europe-west1
--format="value(serviceConfig.uri)"
# Fonksiyonu test et
FUNCTION_URL=$(gcloud functions describe http-demo-fonksiyon
--gen2
--region=europe-west1
--format="value(serviceConfig.uri)")
curl "${FUNCTION_URL}?name=GCP"
Güvenlik: Authentication ve IAM
Authenticated Fonksiyon Oluşturma
Production ortamında --allow-unauthenticated kullanmak çoğu zaman kötü bir fikirdir. Kimlik doğrulamalı fonksiyon oluşturalım:
# Kimlik doğrumu zorunlu fonksiyon
gcloud functions deploy guvenli-fonksiyon
--gen2
--runtime=python311
--region=europe-west1
--source=.
--entry-point=http_handler
--trigger-http
--no-allow-unauthenticated
--memory=256Mi
--timeout=30s
# Belirli bir servis hesabına invoker yetkisi ver
gcloud functions add-invoker-policy-binding guvenli-fonksiyon
--region=europe-west1
--member="serviceAccount:backend-sa@PROJE_ADINIZ.iam.gserviceaccount.com"
# Token ile fonksiyonu çağır
TOKEN=$(gcloud auth print-identity-token)
curl -H "Authorization: Bearer ${TOKEN}" "${FUNCTION_URL}"
Gerçek Dünya Senaryosu: Stripe Webhook İşleyici
Gelin daha gerçekçi bir örnek yapalım. E-ticaret uygulamanız var ve Stripe ödeme webhook’larını işlemeniz gerekiyor.
cat > stripe_webhook.py << 'EOF'
import functions_framework
import json
import os
import hmac
import hashlib
import time
from google.cloud import firestore
from google.cloud import secretmanager
def get_stripe_secret():
"""Secret Manager'dan Stripe webhook secret'ı al"""
client = secretmanager.SecretManagerServiceClient()
name = f"projects/{os.environ['PROJECT_ID']}/secrets/stripe-webhook-secret/versions/latest"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("UTF-8")
def verify_stripe_signature(payload, sig_header, secret):
"""Stripe imza doğrulama - güvenlik açısından kritik!"""
try:
# Timestamp ve imzayı ayır
elements = sig_header.split(',')
timestamp = None
signatures = []
for element in elements:
key, value = element.split('=', 1)
if key == 't':
timestamp = int(value)
elif key == 'v1':
signatures.append(value)
if not timestamp or not signatures:
return False
# Replay attack kontrolü - 5 dakikadan eski istekleri reddet
if abs(time.time() - timestamp) > 300:
return False
# HMAC hesapla
signed_payload = f"{timestamp}.{payload}"
expected_sig = hmac.new(
secret.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
return any(hmac.compare_digest(expected_sig, sig) for sig in signatures)
except Exception:
return False
@functions_framework.http
def stripe_webhook_handler(request):
headers = {'Content-Type': 'application/json'}
if request.method != 'POST':
return (json.dumps({'error': 'Sadece POST kabul edilir'}), 405, headers)
# İmza doğrulama
sig_header = request.headers.get('Stripe-Signature')
if not sig_header:
return (json.dumps({'error': 'İmza eksik'}), 400, headers)
payload = request.get_data(as_text=True)
webhook_secret = get_stripe_secret()
if not verify_stripe_signature(payload, sig_header, webhook_secret):
return (json.dumps({'error': 'Geçersiz imza'}), 401, headers)
# Event'i işle
try:
event = json.loads(payload)
event_type = event.get('type')
# Firestore'a kaydet
db = firestore.Client()
if event_type == 'payment_intent.succeeded':
payment_intent = event['data']['object']
handle_successful_payment(db, payment_intent)
elif event_type == 'payment_intent.payment_failed':
payment_intent = event['data']['object']
handle_failed_payment(db, payment_intent)
# Event'i Firestore'a logla
db.collection('stripe_events').add({
'event_id': event.get('id'),
'type': event_type,
'processed_at': firestore.SERVER_TIMESTAMP,
'status': 'processed'
})
return (json.dumps({'received': True}), 200, headers)
except json.JSONDecodeError:
return (json.dumps({'error': 'Geçersiz JSON'}), 400, headers)
except Exception as e:
print(f"Webhook işleme hatası: {e}")
return (json.dumps({'error': 'İşleme hatası'}), 500, headers)
def handle_successful_payment(db, payment_intent):
"""Başarılı ödeme işlemi"""
order_id = payment_intent.get('metadata', {}).get('order_id')
if order_id:
db.collection('orders').document(order_id).update({
'status': 'paid',
'payment_intent_id': payment_intent['id'],
'paid_at': firestore.SERVER_TIMESTAMP
})
def handle_failed_payment(db, payment_intent):
"""Başarısız ödeme işlemi"""
order_id = payment_intent.get('metadata', {}).get('order_id')
if order_id:
db.collection('orders').document(order_id).update({
'status': 'payment_failed',
'failure_reason': payment_intent.get('last_payment_error', {}).get('message'),
'failed_at': firestore.SERVER_TIMESTAMP
})
EOF
CI/CD ile Otomatik Deploy
Manuel deploy yapmak uzun vadede sürdürülebilir değil. GitHub Actions ile otomatik pipeline kuralım:
# .github/workflows/deploy-function.yml
cat > deploy-function.yml << 'EOF'
name: Deploy Cloud Function
on:
push:
branches:
- main
paths:
- 'functions/**'
- '.github/workflows/deploy-function.yml'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Python kur
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Bağımlılıkları kur
run: |
pip install -r functions/requirements.txt
pip install pytest pytest-mock
- name: Testleri çalıştır
run: pytest functions/tests/ -v
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: GCP Auth
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: gcloud kur
uses: google-github-actions/setup-gcloud@v2
- name: Fonksiyonu deploy et
run: |
gcloud functions deploy http-demo-fonksiyon
--gen2
--runtime=python311
--region=europe-west1
--source=./functions
--entry-point=http_handler
--trigger-http
--no-allow-unauthenticated
--memory=256Mi
--timeout=60s
--min-instances=0
--max-instances=20
--set-env-vars=ENVIRONMENT=production,PROJECT_ID=${{ vars.GCP_PROJECT_ID }}
EOF
Monitoring ve Logging
Fonksiyonlarınızın sağlığını izlemek production’da hayati önem taşıyor:
# Son 1 saatin loglarını görüntüle
gcloud functions logs read http-demo-fonksiyon
--gen2
--region=europe-west1
--limit=50
--format="table(time_utc,severity,log_name,text_payload)"
# Gerçek zamanlı log izleme
gcloud alpha functions logs tail http-demo-fonksiyon
--gen2
--region=europe-west1
# Fonksiyon metriklerini sorgula (Cloud Monitoring)
gcloud monitoring metrics list
--filter="metric.type:cloudfunctions"
# Hata oranını kontrol et
gcloud logging read
'resource.type="cloud_function"
AND resource.labels.function_name="http-demo-fonksiyon"
AND severity>=ERROR'
--limit=20
--format=json
Yapılandırılmış Logging
Fonksiyon içinde structured logging kullanmak, Cloud Logging’de filtrelemeyi çok kolaylaştırır:
cat > logging_utils.py << 'EOF'
import json
import sys
import os
def log(severity, message, **kwargs):
"""
Cloud Logging için yapılandırılmış log çıktısı.
severity: DEBUG, INFO, WARNING, ERROR, CRITICAL
"""
entry = {
"severity": severity,
"message": message,
"component": os.environ.get('K_SERVICE', 'unknown'),
**kwargs
}
print(json.dumps(entry, ensure_ascii=False), file=sys.stdout)
def log_info(message, **kwargs):
log("INFO", message, **kwargs)
def log_error(message, **kwargs):
log("ERROR", message, **kwargs)
def log_warning(message, **kwargs):
log("WARNING", message, **kwargs)
EOF
Yaygın Sorunlar ve Çözümleri
Sysadmin olarak sık karşılaşılan sorunları bilmek çok değerli:
Cold Start Sorunu: Uzun süre istek almayan fonksiyonlar ilk istekte geç yanıt verebilir. Kritik fonksiyonlar için --min-instances=1 ayarı yaparak bir instance’ı her zaman sıcak tut. Ama bu maliyet demek, bunu göz önünde bulundur.
Timeout Ayarları: Varsayılan timeout 60 saniyedir. Uzun süren işlemler için maksimum 3600 saniyeye kadar çıkabilirsin. Ama uzun süren işlemler için Cloud Tasks veya Cloud Run daha uygun olabilir.
Memory ve CPU: Gen2’de memory ile CPU orantılı olarak artıyor. 256Mi için 0.167 vCPU, 2Gi için 1 vCPU alırsın. CPU yoğun işlemler için memory’yi artırmak mantıklı olabilir.
Environment Variables vs Secret Manager: Hassas bilgileri asla environment variable olarak koyma. API key, şifre, token gibi veriler için mutlaka Secret Manager kullan.
Bölge Seçimi: Kullanıcılarına en yakın bölgeyi seç. GDPR kapsamındaki veriler için EU bölgelerini (europe-west1, europe-west3) tercih et.
Maliyet Optimizasyonu
Cloud Functions serverless olduğu için maliyet kontrolü kritik:
# Mevcut fonksiyonların listesi ve detayları
gcloud functions list --gen2 --regions=europe-west1
# Bütçe alarmı oluştur (aylık 50$ limitinde uyar)
gcloud billing budgets create
--billing-account=BILLING_HESAP_ID
--display-name="Cloud Functions Butce"
--budget-amount=50USD
--threshold-rule=percent=80
--threshold-rule=percent=100
Maliyet optimizasyonu için dikkat etmen gerekenler:
- İsteksiz bekleme:
--min-instances=0ile sıfır kullanımda sıfır maliyet - Uygun memory ayarı: Gereğinden fazla memory ayırmak direkt maliyet artışıdır
- Timeout değeri: Makul bir timeout belirle, sonsuz döngülerin önüne geç
- Verimli kod: Gereksiz bağımlılıkları temizle, cold start süresini kıs
- Tekrar eden bağlantılar: Veritabanı bağlantılarını global scope’ta tut, her istekte yeniden oluşturma
Sonuç
GCP Cloud Functions ile HTTP fonksiyon oluşturmak, modern uygulama geliştirmenin temel taşlarından biri haline geldi. Bu yazıda ele aldığımız konuları özetlemek gerekirse; ortam hazırlığından başlayıp, güvenli fonksiyon tasarımı, gerçek dünya senaryoları (Stripe webhook örneği gibi), CI/CD entegrasyonu, monitoring ve maliyet optimizasyonuna kadar uçtan uca bir tablo çizdik.
En kritik noktaların altını çizelim: Kimlik doğrulamasını asla atlama, Secret Manager’ı aktif kullan, structured logging ile operasyonel görünürlüğünü artır ve maliyet alarmlarını baştan kur. Gen2 fonksiyonları artık varsayılan tercih olmalı, Cloud Run’ın gücünü arkana alıyorsun.
Serverless demek “sunucusuz değil, sunucudan habersiz” demek. Altyapı hala orada, sadece sen yönetmiyorsun. Bu yüzden GCP’nin senin adına ne yaptığını anlamak, sorunları daha hızlı çözmen için kritik. Fonksiyonlarını küçük, odaklı ve test edilebilir tut. Her fonksiyon tek bir işi iyi yapmalı, geri kalanını başka servislere veya fonksiyonlara bırak. Bu prensiple inşa edilen sistemler hem bakımı kolay hem de hataya dayanıklı olur.
