mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
269 lines
8.5 KiB
Markdown
269 lines
8.5 KiB
Markdown
---
|
|
name: gcp-cloud-functions
|
|
description: Deploy serverless functions on Google Cloud Functions. Configure triggers and manage deployments. Use when implementing serverless workloads on GCP.
|
|
license: MIT
|
|
metadata:
|
|
author: devops-skills
|
|
version: "1.0"
|
|
---
|
|
|
|
# GCP Cloud Functions
|
|
|
|
Build and deploy event-driven serverless applications with Google Cloud Functions (Gen1 and Gen2).
|
|
|
|
## When to Use
|
|
|
|
- Processing webhooks, API endpoints, or lightweight HTTP backends
|
|
- Reacting to events from Pub/Sub, Cloud Storage, Firestore, or Eventarc
|
|
- Running scheduled tasks (cron) without maintaining a server
|
|
- Building data-processing pipelines triggered by file uploads
|
|
- Prototyping microservices before committing to Cloud Run or GKE
|
|
|
|
## Prerequisites
|
|
|
|
- Google Cloud SDK (`gcloud`) installed and authenticated
|
|
- APIs enabled: Cloud Functions, Cloud Build, Artifact Registry, Cloud Run (Gen2)
|
|
- IAM role `roles/cloudfunctions.developer` (or `roles/run.developer` for Gen2)
|
|
|
|
```bash
|
|
gcloud services enable cloudfunctions.googleapis.com cloudbuild.googleapis.com \
|
|
artifactregistry.googleapis.com run.googleapis.com eventarc.googleapis.com
|
|
```
|
|
|
|
## Gen1 vs Gen2 Comparison
|
|
|
|
| Feature | Gen1 | Gen2 (recommended) |
|
|
|---------|------|---------------------|
|
|
| Runtime | Cloud Functions infra | Built on Cloud Run |
|
|
| Max timeout | 9 minutes | 60 minutes |
|
|
| Max memory | 8 GB | 32 GB |
|
|
| Concurrency | 1 request/instance | Up to 1000/instance |
|
|
| Traffic splitting | No | Yes |
|
|
| Eventarc triggers | No | Yes |
|
|
|
|
## Deploy an HTTP Function (Gen2)
|
|
|
|
```bash
|
|
# Python HTTP function
|
|
gcloud functions deploy hello-http \
|
|
--gen2 --region=us-central1 --runtime=python312 \
|
|
--trigger-http --allow-unauthenticated \
|
|
--entry-point=hello_http \
|
|
--memory=256Mi --timeout=60s \
|
|
--min-instances=0 --max-instances=100 \
|
|
--set-env-vars=APP_ENV=production --source=.
|
|
|
|
# Node.js HTTP function
|
|
gcloud functions deploy hello-node \
|
|
--gen2 --region=us-central1 --runtime=nodejs20 \
|
|
--trigger-http --allow-unauthenticated \
|
|
--entry-point=helloNode --memory=256Mi --source=.
|
|
```
|
|
|
|
## Deploy a Pub/Sub Triggered Function
|
|
|
|
```bash
|
|
gcloud pubsub topics create order-events
|
|
|
|
gcloud functions deploy process-order \
|
|
--gen2 --region=us-central1 --runtime=python312 \
|
|
--trigger-topic=order-events \
|
|
--entry-point=process_order \
|
|
--memory=512Mi --timeout=120s --retry \
|
|
--service-account=order-processor@${PROJECT_ID}.iam.gserviceaccount.com \
|
|
--source=.
|
|
```
|
|
|
|
## Deploy a Cloud Storage Triggered Function
|
|
|
|
```bash
|
|
gcloud functions deploy process-upload \
|
|
--gen2 --region=us-central1 --runtime=python312 \
|
|
--trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \
|
|
--trigger-event-filters="bucket=my-upload-bucket" \
|
|
--entry-point=process_upload \
|
|
--memory=1Gi --timeout=300s --source=.
|
|
```
|
|
|
|
## Deploy a Scheduled Function
|
|
|
|
```bash
|
|
gcloud functions deploy daily-cleanup \
|
|
--gen2 --region=us-central1 --runtime=python312 \
|
|
--trigger-http --no-allow-unauthenticated \
|
|
--entry-point=daily_cleanup --source=.
|
|
|
|
gcloud scheduler jobs create http daily-cleanup-job \
|
|
--schedule="0 2 * * *" \
|
|
--uri="https://us-central1-${PROJECT_ID}.cloudfunctions.net/daily-cleanup" \
|
|
--http-method=POST \
|
|
--oidc-service-account-email=scheduler-sa@${PROJECT_ID}.iam.gserviceaccount.com \
|
|
--location=us-central1
|
|
```
|
|
|
|
## Python Function Examples
|
|
|
|
```python
|
|
# main.py
|
|
import functions_framework
|
|
import base64, json
|
|
from flask import jsonify
|
|
from google.cloud import firestore
|
|
|
|
@functions_framework.http
|
|
def hello_http(request):
|
|
"""HTTP Cloud Function."""
|
|
name = request.args.get("name", "World")
|
|
return jsonify({"message": f"Hello, {name}!", "status": "ok"}), 200
|
|
|
|
@functions_framework.cloud_event
|
|
def process_order(cloud_event):
|
|
"""Triggered by a Pub/Sub message."""
|
|
data = base64.b64decode(cloud_event.data["message"]["data"]).decode("utf-8")
|
|
order = json.loads(data)
|
|
db = firestore.Client()
|
|
db.collection("orders").document(order["id"]).set({
|
|
"status": "processing", "items": order["items"], "total": order["total"],
|
|
})
|
|
|
|
@functions_framework.cloud_event
|
|
def process_upload(cloud_event):
|
|
"""Triggered when a file is uploaded to Cloud Storage."""
|
|
data = cloud_event.data
|
|
bucket_name, file_name = data["bucket"], data["name"]
|
|
if not file_name.lower().endswith((".png", ".jpg", ".jpeg")):
|
|
return
|
|
from google.cloud import vision
|
|
client = vision.ImageAnnotatorClient()
|
|
image = vision.Image(source=vision.ImageSource(
|
|
gcs_image_uri=f"gs://{bucket_name}/{file_name}"))
|
|
labels = [l.description for l in client.label_detection(image=image).label_annotations]
|
|
print(f"Labels for {file_name}: {labels}")
|
|
```
|
|
|
|
```
|
|
# requirements.txt
|
|
functions-framework==3.*
|
|
google-cloud-firestore==2.*
|
|
google-cloud-storage==2.*
|
|
google-cloud-vision==3.*
|
|
flask>=2.0
|
|
```
|
|
|
|
## Node.js Function Examples
|
|
|
|
```javascript
|
|
// index.js
|
|
const functions = require("@google-cloud/functions-framework");
|
|
|
|
functions.http("helloNode", (req, res) => {
|
|
const name = req.query.name || "World";
|
|
res.json({ message: `Hello, ${name}!`, status: "ok" });
|
|
});
|
|
|
|
functions.cloudEvent("processMessage", (cloudEvent) => {
|
|
const data = Buffer.from(cloudEvent.data.message.data, "base64").toString();
|
|
console.log(`Processing: ${JSON.parse(data)}`);
|
|
});
|
|
```
|
|
|
|
## Managing Deployed Functions
|
|
|
|
```bash
|
|
gcloud functions list --gen2 --region=us-central1
|
|
gcloud functions describe hello-http --gen2 --region=us-central1
|
|
gcloud functions logs read hello-http --gen2 --region=us-central1 --limit=50
|
|
gcloud functions delete hello-http --gen2 --region=us-central1 --quiet
|
|
|
|
# Update env vars without redeploying code
|
|
gcloud functions deploy hello-http --gen2 --region=us-central1 \
|
|
--update-env-vars=APP_ENV=staging
|
|
|
|
# Test locally before deploying
|
|
functions-framework --target=hello_http --port=8080
|
|
```
|
|
|
|
## Terraform Configuration
|
|
|
|
```hcl
|
|
resource "google_cloudfunctions2_function" "api" {
|
|
name = "hello-http"
|
|
location = "us-central1"
|
|
|
|
build_config {
|
|
runtime = "python312"
|
|
entry_point = "hello_http"
|
|
source {
|
|
storage_source {
|
|
bucket = google_storage_bucket.source.name
|
|
object = google_storage_bucket_object.source.name
|
|
}
|
|
}
|
|
}
|
|
|
|
service_config {
|
|
min_instance_count = 0
|
|
max_instance_count = 100
|
|
available_memory = "256Mi"
|
|
timeout_seconds = 60
|
|
service_account_email = google_service_account.fn.email
|
|
environment_variables = { APP_ENV = "production" }
|
|
}
|
|
}
|
|
|
|
resource "google_cloud_run_service_iam_member" "invoker" {
|
|
location = google_cloudfunctions2_function.api.location
|
|
service = google_cloudfunctions2_function.api.name
|
|
role = "roles/run.invoker"
|
|
member = "allUsers"
|
|
}
|
|
|
|
resource "google_cloudfunctions2_function" "processor" {
|
|
name = "process-order"
|
|
location = "us-central1"
|
|
|
|
build_config {
|
|
runtime = "python312"
|
|
entry_point = "process_order"
|
|
source {
|
|
storage_source {
|
|
bucket = google_storage_bucket.source.name
|
|
object = google_storage_bucket_object.source.name
|
|
}
|
|
}
|
|
}
|
|
|
|
service_config {
|
|
max_instance_count = 50
|
|
available_memory = "512Mi"
|
|
timeout_seconds = 120
|
|
service_account_email = google_service_account.fn.email
|
|
}
|
|
|
|
event_trigger {
|
|
trigger_region = "us-central1"
|
|
event_type = "google.cloud.pubsub.topic.v1.messagePublished"
|
|
pubsub_topic = google_pubsub_topic.orders.id
|
|
retry_policy = "RETRY_POLICY_RETRY"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Cause | Fix |
|
|
|---------|-------|-----|
|
|
| `PERMISSION_DENIED` on deploy | Missing Cloud Build or Artifact Registry perms | Grant `roles/cloudbuild.builds.builder` to Cloud Build SA |
|
|
| Function deploys but returns 403 | Missing `roles/run.invoker` for Gen2 | Add `--allow-unauthenticated` or grant invoker role |
|
|
| Cold start latency > 5s | Large dependencies or no min instances | Set `--min-instances=1`; reduce deps; use lazy imports |
|
|
| Pub/Sub messages redelivered | Function errors or times out | Increase `--timeout`; fix error handling; add dead-letter topic |
|
|
| `Build failed` during deploy | Syntax error or missing dependency | Check `gcloud builds log`; verify requirements.txt |
|
|
| Cannot connect to VPC resource | Function not on VPC connector | Add `--vpc-connector=my-connector` to deploy |
|
|
|
|
## Related Skills
|
|
|
|
- **gcp-networking** - VPC connectors for accessing private resources from functions
|
|
- **gcp-cloud-sql** - Connecting Cloud Functions to managed databases
|
|
- **terraform-gcp** - Deploy Cloud Functions with Infrastructure as Code
|
|
- **gcp-gke** - When workloads outgrow serverless and need Kubernetes
|