Google Cloud How To Use

Google Cloud How To Use

Reading time1 min
#Cloud#Serverless#GoogleCloud#GCP#CloudFunctions#AppEngine

How to Architect Cost-Effective and Scalable Solutions Using Google Cloud's Serverless Technologies

The challenge: scaling rapidly while controlling costs and minimizing maintenance overhead. Google Cloud’s serverless portfolio—Cloud Functions, App Engine, Cloud Run—removes low-level infrastructure friction, making it viable to handle unpredictable workloads with minimal operational burden.


Serverless in Practice: When and Why

Forget server patching, cluster sizing, or cold-capacity planning. Teams deploying to Google Cloud Serverless environments see these benefits:

  • No fleet management: Underlying compute abstracted away by GCP.
  • Granular, usage-based billing: No charges for idle services—metered by invocations and runtime duration.
  • Native monitoring: Stackdriver (now part of Google Cloud Operations Suite) surfaces logs, errors, and performance metrics out of the box.
  • Distributed scale-out: Instances replicate instantly on demand, then scale back to zero under load drop.
  • Tight CI/CD support: Deployments as simple as gcloud functions deploy or gcloud app deploy.

But there are constraints: cold start latency on infrequent paths, execution timeout limits, and less control over runtime environment compared to GKE or Compute Engine.


Core Components: Where to Use Each

ServiceTypical Use CaseNotable LimitsScaling Model
Cloud FunctionsEvent-driven compute, webhooks, lightweight APIs9 minutes max execution (as of v2), cold startsZero-to-N by demand
App Engine StandardStateful frontends, backend APIs with session, legacy workloadsSupported languages/runtime lists, sandboxedAuto or basic/manual
Cloud RunContainerized workloads, custom runtimes, sidecar dependencies15m max request, limited egress without VPCFast; noisy-neighbor resilience

Note: Use Cloud Run if you need Docker support or additional binaries (e.g., ImageMagick). Stick with App Engine Standard for the fastest scaling and simplest configuration for web-facing endpoints.


Common Workflow: Event-Driven Image Processing

A frequent pattern: user uploads, async post-processing, then UI update.

ASCII diagram:

[User] —(HTTP POST)—> [App Engine (Flask)] 
      —(writes)—> [Cloud Storage]
                  —(event)—> [Cloud Function]
                               —(writes)—> [Cloud Storage: /resized/]

Implementation: Minimal Example (as of 2024)

1. App Engine: HTTP Endpoint for Upload

app.yaml (Python 3.9 runtime, minimal scaling—pro tip: set max_instances to control runaway costs with unthrottled traffic):

runtime: python39
instance_class: F1
automatic_scaling:
  max_instances: 3

main.py:

from flask import Flask, request, redirect
from google.cloud import storage

app = Flask(__name__)

@app.route("/", methods=["GET"])
def upload_form():
    return '<form action="/upload" method="post" enctype="multipart/form-data">' \
           '<input type="file" name="image"><input type="submit"></form>'

@app.route("/upload", methods=["POST"])
def upload_image():
    image = request.files["image"]
    bucket = storage.Client().bucket("my-bucket")
    blob = bucket.blob(image.filename)
    blob.upload_from_file(image)
    return redirect("/")

Deploy:

gcloud app deploy app.yaml

Gotcha: Flask on App Engine Standard may emit gunicorn logging noise. Set entrypoint: gunicorn -b :$PORT main:app in app.yaml for cleaner logs.


2. Cloud Function: Image Resize on Storage Event

resize_image.py (Python 3.9, uses Pillow):

from google.cloud import storage
from PIL import Image
import os
import tempfile

def resize_image(event, context):
    bucket_name = event['bucket']
    file_name = event['name']
    if file_name.startswith('resized/'):
        # Prevent recursion on already-processed images
        return

    storage_client = storage.Client()
    bucket = storage_client.get_bucket(bucket_name)
    blob = bucket.blob(file_name)

    _, temp_local_filename = tempfile.mkstemp()
    blob.download_to_filename(temp_local_filename)

    try:
        with Image.open(temp_local_filename) as image:
            image.thumbnail((128, 128))
            temp_resized = f"/tmp/resized-{os.path.basename(file_name)}"
            image.save(temp_resized)
            bucket.blob(f"resized/{file_name}").upload_from_filename(temp_resized)
    except Exception as e:
        print(f"Image processing failed: {e}")
    finally:
        os.remove(temp_local_filename)

Deployment (set memory lower unless you process huge inputs):

gcloud functions deploy resize_image \
  --runtime python39 \
  --trigger-resource=my-bucket \
  --trigger-event=google.storage.object.finalize \
  --entry-point=resize_image \
  --region=us-central1 \
  --memory=256MB

Tip: Memory set too low can provoke:

Function execution took 60001 ms, finished with status: 'timeout'

Check GCP logs; adjust memory and timeout as needed.


Performance & Cost Control

  • Scale App Engine to fit anticipated usage (adjust max_instances).
  • Keep function deployments small—dependencies in requirements.txt matter (avoid unused libraries).
  • Clean unused Storage buckets; stale objects drive up bills.
  • Cold starts: for near real-time paths, hit the endpoint periodically or use minInstances (not free).
  • Use VPC connectors for private resource access only if required (adds latency/cost).

Final Notes

Google Cloud’s serverless stack—properly architected—enables burst scaling and minimal idle spend. It’s no silver bullet; compliance-heavy workloads or latency-critical microservices may require hybrid approaches. However, for most event-driven workloads and dynamic web APIs, this model eliminates routine ops toil entirely.

If you need to chain workflows (image -> Pub/Sub -> downstream function), consider Eventarc or Workflows for orchestration. Also: Cloud Run has matured rapidly—if you run into strange dependency issues on App Engine or Cloud Functions, migrating to Cloud Run may solve them.

Questions on Pub/Sub integration or access control best practices? Might cover those in detail next.