Cloud Build CI/CD
Set up Cloud Build triggers for automatic deployments on every push to main. Deployment Overview
Deploy FraiseQL on Google Cloud Platform using serverless Cloud Run or Kubernetes Engine (GKE).
# Set projectexport PROJECT_ID=$(gcloud config get-value project)export REGION=us-central1
# Create Artifact Registrygcloud artifacts repositories create fraiseql \ --repository-format=docker \ --location=$REGION
# Configure Docker authgcloud auth configure-docker $REGION-docker.pkg.dev
# Build and pushdocker build -t fraiseql:latest .docker tag fraiseql:latest \ $REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latestdocker push $REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latest# Create database instancegcloud sql instances create fraiseql-prod \ --database-version=POSTGRES_16 \ --tier=db-f1-micro \ --region=$REGION \ --availability-type=REGIONAL \ --backup-start-time=02:00 \ --retained-backups-count=30 \ --transaction-log-retention-days=7
# Create databasegcloud sql databases create fraiseql \ --instance=fraiseql-prod
# Create database usergcloud sql users create fraiseql \ --instance=fraiseql-prod \ --password="$(openssl rand -base64 32)"
# Get connection stringgcloud sql instances describe fraiseql-prod \ --format='value(connectionName)'# Create secretsecho "postgresql://fraiseql:PASSWORD@/fraiseql?host=/cloudsql/PROJECT:REGION:fraiseql-prod" | \ gcloud secrets create fraiseql-database-url --data-file=-
echo "$(openssl rand -base64 32)" | \ gcloud secrets create fraiseql-jwt-secret --data-file=-
echo "https://app.example.com" | \ gcloud secrets create fraiseql-cors-origins --data-file=-# Deploy servicegcloud run deploy fraiseql \ --image=$REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latest \ --region=$REGION \ --platform=managed \ --memory=1Gi \ --cpu=1 \ --timeout=60 \ --max-instances=100 \ --min-instances=1 \ --set-env-vars="ENVIRONMENT=production,LOG_LEVEL=info,LOG_FORMAT=json" \ --set-secrets="DATABASE_URL=fraiseql-database-url:latest,JWT_SECRET=fraiseql-jwt-secret:latest,CORS_ORIGINS=fraiseql-cors-origins:latest" \ --add-cloudsql-instances=$PROJECT_ID:$REGION:fraiseql-prod \ --allow-unauthenticated
# Get service URLgcloud run services describe fraiseql \ --region=$REGION \ --format='value(status.url)'# Map custom domaingcloud run domain-mappings create \ --service=fraiseql \ --domain=api.example.com \ --region=$REGION
# Update DNS records (output from above command)# Add CNAME record pointing to ghs.googleusercontent.comCloud Load Balancer (optional, for multiple regions) |Cloud Run (Serverless, auto-scaling) |Cloud SQL PostgreSQL (Managed database)# Create VPCgcloud compute networks create fraiseql-vpc \ --subnet-mode=custom \ --bgp-routing-mode=regional
# Create subnetgcloud compute networks subnets create fraiseql-subnet \ --network=fraiseql-vpc \ --region=$REGION \ --range=10.0.0.0/20
# Create Private Service Connectiongcloud compute addresses create fraiseql-db-range \ --global \ --purpose=VPC_PEERING \ --prefix-length=16 \ --network=fraiseql-vpc
gcloud services vpc-peerings connect \ --service=servicenetworking.googleapis.com \ --ranges=fraiseql-db-range \ --network=fraiseql-vpcgcloud sql instances create fraiseql-prod \ --database-version=POSTGRES_16 \ --tier=db-custom-2-8192 \ --region=$REGION \ --network=fraiseql-vpc \ --no-assign-ip \ --availability-type=REGIONAL \ --backup-start-time=02:00 \ --retained-backups-count=30 \ --transaction-log-retention-days=7 \ --database-flags=cloudsql_iam_authentication=on# Add Cloud Run service to IAM policygcloud projects add-iam-policy-binding $PROJECT_ID \ --member=serviceAccount:$PROJECT_ID@appspot.gserviceaccount.com \ --role=roles/cloudsql.client
# In Cloud Run, use unix socket connection:# DATABASE_URL=postgresql://fraiseql:pass@/fraiseql?host=/cloudsql/PROJECT:REGION:instance-nameCloud Run auto-scales automatically based on request count.
To customize:
apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: fraiseql namespace: defaultspec: template: metadata: annotations: autoscaling.knative.dev/minScale: "1" autoscaling.knative.dev/maxScale: "100" autoscaling.knative.dev/targetUtilization: "0.7" spec: containerConcurrency: 80 containers: - image: us-central1-docker.pkg.dev/PROJECT/fraiseql/fraiseql:latestApply:
gcloud run services replace cloud-run-config.yaml --region=$REGIONFor more control and complex workloads:
# Create clustergcloud container clusters create fraiseql-cluster \ --region=$REGION \ --num-nodes=3 \ --machine-type=n2-standard-2 \ --enable-autoscaling \ --min-nodes=3 \ --max-nodes=10 \ --enable-autorepair \ --enable-autoupgrade \ --enable-stackdriver-kubernetes \ --addons=HttpLoadBalancing,HttpsLoadBalancing \ --workload-pool=$PROJECT_ID.svc.id.goog \ --enable-network-policy
# Get credentialsgcloud container clusters get-credentials fraiseql-cluster --region=$REGIONFollow the Kubernetes deployment guide with GCP-specific configuration:
apiVersion: apps/v1kind: Deploymentmetadata: name: fraiseql namespace: defaultspec: replicas: 3 template: spec: serviceAccountName: fraiseql containers: - name: fraiseql image: us-central1-docker.pkg.dev/PROJECT/fraiseql/fraiseql:latest env: - name: DATABASE_URL valueFrom: secretKeyRef: name: fraiseql-secrets key: database-url
---apiVersion: v1kind: Servicemetadata: name: fraiseqlspec: type: LoadBalancer selector: app: fraiseql ports: - port: 80 targetPort: 8000Deploy:
# Store secretskubectl create secret generic fraiseql-secrets \ --from-literal=database-url=$DATABASE_URL \ --from-literal=jwt-secret=$JWT_SECRET
# Deploykubectl apply -f fraiseql-gke-deployment.yaml
# Check statuskubectl get services fraiseqlkubectl get podsLogs are automatically collected from Cloud Run and GKE.
View logs:
# Cloud Run logsgcloud run logs read fraiseql --region=$REGION --limit=100
# GKE logskubectl logs deployment/fraiseql --all-containers --follow# Create alert policy (high error rate)gcloud alpha monitoring policies create \ --notification-channels=CHANNEL_ID \ --display-name="FraiseQL High Error Rate" \ --condition-display-name="Error rate > 5%" \ --condition-threshold-value=0.05 \ --condition-threshold-filter='resource.type="cloud_run_revision" AND metric.type="run.googleapis.com/request_count" AND resource.label.service_name="fraiseql"'Enable distributed tracing:
# In your FraiseQL code, initialize tracerfrom google.cloud import trace_v2
client = trace_v2.TraceServiceClient()# Configure backups (already set in creation)gcloud sql instances patch fraiseql-prod \ --backup-start-time=02:00 \ --retained-backups-count=30
# Create on-demand backupgcloud sql backups create \ --instance=fraiseql-prod \ --description="Manual backup"
# List backupsgcloud sql backups list --instance=fraiseql-prod
# Restore from backupgcloud sql backups restore BACKUP_ID \ --backup-instance=fraiseql-prod \ --backup-id=BACKUP_ID# Create read replicagcloud sql instances create fraiseql-replica \ --master-instance-name=fraiseql-prod \ --tier=db-f1-micro \ --region=us-west1 # Different region
# Promote replica to standalonegcloud sql instances promote-replica fraiseql-replicaCloud Run automatically scales based on request count.
For higher throughput:
# Increase maximum instancesgcloud run services update fraiseql \ --max-instances=500 \ --region=$REGION
# Set minimum instances (keeps instances warm)gcloud run services update fraiseql \ --min-instances=10 \ --region=$REGION# Enable insightsgcloud sql instances patch fraiseql-prod \ --enable-database-flags=cloudsql_insights_enabled=on
# View insightsgcloud sql operations list --instance=fraiseql-prodCreate build pipeline:
steps: # Build Docker image - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - '$_IMAGE_NAME' - '.'
# Push to Artifact Registry - name: 'gcr.io/cloud-builders/docker' args: - 'push' - '$_IMAGE_NAME'
# Deploy to Cloud Run - name: 'gcr.io/cloud-builders/gke-deploy' args: - 'run' - '--service=' - 'fraiseql' - '--region=$_REGION' - '--image=$_IMAGE_NAME'
substitutions: _IMAGE_NAME: 'us-central1-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:$SHORT_SHA' _REGION: 'us-central1'
images: - '$_IMAGE_NAME'Trigger from GitHub:
gcloud builds connect --repository-name=fraiseql \ --repository-owner=your-github-org \ --region=$REGIONExample:
1 request at 1 CPU for 1 second = $0.00001667100,000 requests/month = $0.04Optimization tips:
min-instances=0 to save on idle timeOptimization tips:
| Feature | Cloud Run | GKE |
|---|---|---|
| Setup time | 5 minutes | 30 minutes |
| Scaling | Automatic | Manual/Automatic |
| Price (low traffic) | $0.04/month | $200+/month |
| Price (high traffic) | $0.40/M requests | Better for sustained load |
| Latency | 100-500ms cold start | Low (warm) |
| Customization | Limited | Full control |
| Multi-region | Easy | More complex |
# Check service statusgcloud run services describe fraiseql --region=$REGION
# View recent deploymentsgcloud run revisions list --service=fraiseql --region=$REGION
# View logs during deploymentgcloud builds log $(gcloud builds list --limit=1 --format='value(id)')# Test connection from Cloud Rungcloud run exec --service=fraiseql \ --region=$REGION \ -- psql $DATABASE_URL# Increase memorygcloud run services update fraiseql \ --memory=2Gi \ --region=$REGIONCloud Build CI/CD
Set up Cloud Build triggers for automatic deployments on every push to main. Deployment Overview
Cloud Monitoring
Configure Cloud Monitoring dashboards and alert policies. Scaling Guide
Cloud Armor
Set up Cloud Armor DDoS protection and security policies. Deployment Overview
Troubleshooting
Debug Cloud Run deployment failures and Cloud SQL connection issues. Troubleshooting Guide