16 min read

    30 - Capstone Project - Production Deployment

    gcpcloudcapstoneproductioncloud-rungeminibigquerycloud-buildgraduation

    Welcome to Day 30 of Learn GCP in 30 Days! πŸŽ‰

    Thirty days ago, you began this journey with a single question: "What is cloud computing?" Over the last month, you transformed from a beginner into a capable Cloud Architect who understands networking, virtual machines, serverless containers, relational databases, global NoSQL, caching, big data analytics, CI/CD automation, and artificial intelligence.

    Today is your Grand Finale Capstone Project & Course Graduation!

    🎯

    Today's Goal Today, you will build and release a complete, production-grade AI-Powered Smart Asset Intelligence Platform. This unified architecture combines Cloud Run (Serverless Frontend & API), Cloud Storage (Asset Storage), Gemini 2.5 Flash (Multimodal AI Analysis), Firestore (Realtime Metadata), BigQuery (Analytical Logging), and Cloud Build (Automated CI/CD)β€”tested end-to-end with our $0.00 credit safety guarantee!


    πŸ—οΈ The 30-Day Unified Architecture Blueprint

    In the real world, enterprise cloud applications are not isolated silos. They are interconnected systems where compute, storage, databases, analytics, and AI work together harmoniously:

    mermaid

    πŸ›‘ The Problem: Fragmented Legacy Architectures

    mermaid

    What Existed Previously:

    Traditional companies ran monolithic applications on fixed virtual machines:

    • Image processing and file uploads were handled on the local web server disk, crashing servers when storage filled up.
    • Reports and analytics were run directly against the live transactional database, locking tables and slowing down customer traffic.
    • Adding AI capabilities required months of custom machine learning training and expensive GPU server rentals.
    • Deployments were done manually by developers via FTP or SSH terminal commands late at night.

    How Google Cloud Solves It (The Modern Serverless Stack):

    1. Stateless Scalability (Cloud Run): The web service auto-scales from 0 to thousands of requests and back to zero with $0.00 idle cost.
    2. Infinite Durability (Cloud Storage): Media files are stored securely with 11 9's of durability.
    3. Instant Intelligence (Gemini 2.5 Flash): Images are automatically analyzed for objects, defects, and text in seconds without custom ML training.
    4. Separated Storage & Analytics (Firestore + BigQuery): Fast operational data is kept in Firestore, while analytical audit records stream directly into BigQuery for reporting.
    5. Zero-Touch Automation (Cloud Build): A single git push or build command tests, packages, and deploys the entire application.

    πŸ› οΈ Step-by-Step Hands-On Capstone Build

    Let's build, deploy, and test the entire production platform in Cloud Shell!

    mermaid

    Step 1: Initialize Project & Enable All APIs

    Open Google Cloud Shell and run:

    bash
    # 1. Set environment variables
    export PROJECT_ID=$(gcloud config get-value project)
    export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format='value(projectNumber)')
    export REGION="us-central1"
    export BUCKET_NAME="asset-portal-${PROJECT_ID}"
    
    # 2. Enable all required GCP services
    gcloud services enable \
        run.googleapis.com \
        storage.googleapis.com \
        firestore.googleapis.com \
        bigquery.googleapis.com \
        aiplatform.googleapis.com \
        cloudbuild.googleapis.com
    

    Step 2: Provision Storage, Database & Analytics Resources

    1. Create Cloud Storage Bucket (with Public Asset Access)

    bash
    # Create bucket
    gcloud storage buckets create gs://${BUCKET_NAME} \
        --location=${REGION} \
        --uniform-bucket-level-access
    
    # Allow public read access for displaying uploaded images in browser
    gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
        --member="allUsers" \
        --role="roles/storage.objectViewer"
    

    2. Initialize Firestore (if not already active)

    bash
    gcloud firestore databases create \
        --location=${REGION} \
        --type=firestore-native 2>/dev/null || echo "Firestore already initialized!"
    

    3. Create BigQuery Analytics Dataset & Table

    bash
    # Create dataset
    bq --location=${REGION} mk -d asset_analytics
    
    # Create table for audit logs
    bq query --use_legacy_sql=false '
    CREATE OR REPLACE TABLE `asset_analytics.upload_events` (
        asset_id STRING,
        filename STRING,
        user_caption STRING,
        ai_summary STRING,
        upload_timestamp TIMESTAMP
    );
    '
    

    4. Grant IAM Permissions for Cloud Build & Cloud Run

    bash
    # Grant Vertex AI User to Cloud Run default service account
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
        --role="roles/aiplatform.user"
    
    # Grant BigQuery Data Editor to Cloud Run default service account
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
        --role="roles/bigquery.dataEditor"
    

    Step 3: Author the Application Source Code

    Let's create our project workspace and author the full-stack application:

    bash
    mkdir -p ~/gcp-capstone-app/templates
    cd ~/gcp-capstone-app
    

    1. Author Backend API & AI Pipeline (main.py)

    πŸ“

    Terminal File Creation Options Choose either the 1-Click command or the manual editor:

    ⚑ Option A: Fast 1-Click Way (Copy & Paste):

    bash
    cat << 'EOF' > main.py
    import os
    import datetime
    import json
    import urllib.request
    import subprocess
    from flask import Flask, render_template, request, redirect, url_for
    from google.cloud import firestore
    from google.cloud import storage
    from google.cloud import bigquery
    
    app = Flask(__name__)
    
    # Initialize GCP Clients
    PROJECT_ID = os.environ.get("PROJECT_ID")
    BUCKET_NAME = os.environ.get("BUCKET_NAME")
    REGION = "us-central1"
    
    db = firestore.Client(project=PROJECT_ID)
    storage_client = storage.Client(project=PROJECT_ID)
    bq_client = bigquery.Client(project=PROJECT_ID)
    
    def analyze_image_with_gemini(image_uri, mime_type):
        """Calls Gemini 2.5 Flash on Vertex AI to inspect uploaded image."""
        try:
            # Get access token from Google Metadata server
            token_req = urllib.request.Request(
                "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",
                headers={"Metadata-Flavor": "Google"}
            )
            with urllib.request.urlopen(token_req) as resp:
                token = json.loads(resp.read().decode())["access_token"]
    
            prompt = "Analyze this image in 2 concise sentences: 1) What is depicted? 2) Key visual objects or features."
            payload = {
                "contents": [{
                    "role": "user",
                    "parts": [
                        {"fileData": {"mimeType": mime_type, "fileUri": image_uri}},
                        {"text": prompt}
                    ]
                }],
                "generationConfig": {"temperature": 0.2, "maxOutputTokens": 256}
            }
    
            url = f"https://us-central1-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/us-central1/publishers/google/models/gemini-2.5-flash:generateContent"
            req = urllib.request.Request(
                url,
                data=json.dumps(payload).encode("utf-8"),
                headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
                method="POST"
            )
            with urllib.request.urlopen(req, timeout=10) as resp:
                result = json.loads(resp.read().decode())
                return result["candidates"][0]["content"]["parts"][0]["text"]
        except Exception as e:
            return f"AI Analysis Note: Image uploaded successfully. (Vertex AI Info: {str(e)[:100]})"
    
    @app.route("/", methods=["GET"])
    def index():
        # 1. Fetch live assets from Firestore
        assets_ref = db.collection("assets").order_by("created_at", direction=firestore.Query.DESCENDING).limit(12)
        assets = [doc.to_dict() for doc in assets_ref.stream()]
        return render_template("index.html", assets=assets, project_id=PROJECT_ID)
    
    @app.route("/upload", methods=["POST"])
    def upload():
        title = request.form.get("title", "Untitled Asset")
        caption = request.form.get("caption", "")
        image_file = request.files.get("photo")
    
        if image_file and image_file.filename != "":
            timestamp_str = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d_%H%M%S")
            filename = f"{timestamp_str}_{image_file.filename}"
            mime_type = image_file.content_type or "image/jpeg"
    
            # 1. Upload Raw Image to Cloud Storage (GCS)
            bucket = storage_client.bucket(BUCKET_NAME)
            blob = bucket.blob(filename)
            blob.upload_from_file(image_file, content_type=mime_type)
            image_url = f"https://storage.googleapis.com/{BUCKET_NAME}/{filename}"
            gcs_uri = f"gs://{BUCKET_NAME}/{filename}"
    
            # 2. Analyze Image with Gemini 2.5 Flash (AI)
            ai_summary = analyze_image_with_gemini(gcs_uri, mime_type)
    
            now = datetime.datetime.now(datetime.timezone.utc)
            asset_id = f"asset_{timestamp_str}"
    
            # 3. Store Realtime Document in Firestore
            db.collection("assets").document(asset_id).set({
                "asset_id": asset_id,
                "title": title,
                "caption": caption,
                "image_url": image_url,
                "ai_summary": ai_summary,
                "created_at": now
            })
    
            # 4. Stream Audit Record into BigQuery Data Warehouse
            bq_rows = [{
                "asset_id": asset_id,
                "filename": filename,
                "user_caption": caption,
                "ai_summary": ai_summary,
                "upload_timestamp": now.isoformat()
            }]
            bq_client.insert_rows_json("asset_analytics.upload_events", bq_rows)
    
        return redirect(url_for("index"))
    
    if __name__ == "__main__":
        app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
    EOF
    

    πŸ“ Option B: Manual Way with Nano:

    bash
    nano main.py
    

    (Paste the code above, press Ctrl+O β†’\rightarrow Enter to save, then Ctrl+X to exit).


    2. Author Frontend UI (templates/index.html)

    ⚑ Fast 1-Click Way (Copy & Paste):

    bash
    cat << 'EOF' > templates/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>🌟 GCP Day 30 Capstone: Smart Asset Platform</title>
      <style>
        * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
        body { background: #0f172a; color: #f8fafc; padding: 2rem; }
        .container { max-width: 1000px; margin: 0 auto; }
        header { text-align: center; margin-bottom: 2.5rem; }
        header h1 { font-size: 2.4rem; background: linear-gradient(135deg, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 0.5rem; }
        header p { color: #94a3b8; font-size: 1.1rem; }
        .badge { display: inline-block; background: #1e293b; border: 1px solid #38bdf8; color: #38bdf8; padding: 0.4rem 1rem; border-radius: 20px; font-weight: bold; margin-top: 0.8rem; }
    
        .card { background: #1e293b; border-radius: 16px; padding: 2rem; margin-bottom: 2.5rem; border: 1px solid #334155; box-shadow: 0 10px 25px rgba(0,0,0,0.3); }
        .card h2 { font-size: 1.4rem; margin-bottom: 1.2rem; color: #e2e8f0; display: flex; align-items: center; gap: 0.5rem; }
        .form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; }
        .form-group { margin-bottom: 1rem; }
        label { display: block; font-weight: 600; margin-bottom: 0.4rem; font-size: 0.9rem; color: #cbd5e1; }
        input[type="text"], textarea { width: 100%; padding: 0.8rem; background: #0f172a; border: 1px solid #475569; border-radius: 8px; font-size: 1rem; color: white; }
        input[type="file"] { margin-top: 0.3rem; color: #94a3b8; }
        button { background: linear-gradient(135deg, #2563eb, #3b82f6); color: white; border: none; padding: 0.9rem 2rem; border-radius: 8px; font-size: 1.05rem; font-weight: bold; cursor: pointer; transition: 0.2s; }
        button:hover { opacity: 0.9; transform: translateY(-1px); }
    
        .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 1.5rem; }
        .asset-card { background: #1e293b; border-radius: 14px; overflow: hidden; border: 1px solid #334155; transition: transform 0.2s; }
        .asset-card:hover { transform: translateY(-4px); }
        .asset-card img { width: 100%; height: 210px; object-fit: cover; }
        .asset-card .info { padding: 1.2rem; }
        .asset-card h3 { font-size: 1.2rem; color: #f1f5f9; margin-bottom: 0.4rem; }
        .asset-card .caption { color: #94a3b8; font-size: 0.9rem; margin-bottom: 0.8rem; }
        .ai-box { background: #0f172a; border-left: 3px solid #38bdf8; padding: 0.8rem; border-radius: 6px; font-size: 0.85rem; color: #e2e8f0; line-height: 1.4; }
        .ai-title { font-weight: bold; color: #38bdf8; margin-bottom: 0.2rem; font-size: 0.8rem; text-transform: uppercase; }
        .empty-state { text-align: center; padding: 3rem; color: #64748b; }
      </style>
    </head>
    <body>
      <div class="container">
        <header>
          <h1>🌟 Cloud Asset Intelligence Platform</h1>
          <p>Full-Stack Production System: Cloud Run + GCS + Gemini 2.5 Flash + Firestore + BigQuery</p>
          <div class="badge">πŸ† Learn GCP in 30 Days Capstone</div>
        </header>
    
        <div class="card">
          <h2>πŸ“€ Upload Asset for Automated AI Inspection</h2>
          <form action="/upload" method="POST" enctype="multipart/form-data">
            <div class="form-grid">
              <div class="form-group">
                <label for="title">Asset Title</label>
                <input type="text" id="title" name="title" placeholder="e.g., Solar Panel Inspection #402" required>
              </div>
              <div class="form-group">
                <label for="photo">Select Image / Asset</label>
                <input type="file" id="photo" name="photo" accept="image/*" required>
              </div>
            </div>
            <div class="form-group">
              <label for="caption">User Description</label>
              <textarea id="caption" name="caption" rows="2" placeholder="Field technician notes or context..."></textarea>
            </div>
            <button type="submit">⚑ Process Asset with Gemini AI</button>
          </form>
        </div>
    
        <h2 style="margin-bottom: 1.2rem; color: #f1f5f9;">πŸ–ΌοΈ Live Asset Feed (Firestore Realtime Sync)</h2>
        {% if assets %}
          <div class="gallery">
            {% for asset in assets %}
              <div class="asset-card">
                <img src="{{ asset.image_url }}" alt="{{ asset.title }}" loading="lazy">
                <div class="info">
                  <h3>{{ asset.title }}</h3>
                  <p class="caption">{{ asset.caption }}</p>
                  <div class="ai-box">
                    <div class="ai-title">πŸ€– Gemini 2.5 Flash Analysis:</div>
                    {{ asset.ai_summary }}
                  </div>
                </div>
              </div>
            {% endfor %}
          </div>
        {% else %}
          <div class="card empty-state">
            <p>No assets uploaded yet! Upload your first image above to trigger automated Gemini inspection.</p>
          </div>
        {% endif %}
      </div>
    </body>
    </html>
    EOF
    

    3. Author Dependencies (requirements.txt), Dockerfile & cloudbuild.yaml

    bash
    cat << 'EOF' > requirements.txt
    Flask==3.0.3
    google-cloud-firestore==2.16.0
    google-cloud-storage==2.16.0
    google-cloud-bigquery==3.21.0
    gunicorn==22.0.0
    EOF
    
    cat << 'EOF' > Dockerfile
    FROM python:3.11-slim
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    COPY . .
    ENV PORT=8080
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
    EOF
    
    cat << 'EOF' > cloudbuild.yaml
    steps:
      # Step 1: Build Docker Container
      - name: 'gcr.io/cloud-builders/docker'
        args: ['build', '-t', 'gcr.io/$PROJECT_ID/capstone-app:$BUILD_ID', '.']
    
      # Step 2: Push Container Image to Registry
      - name: 'gcr.io/cloud-builders/docker'
        args: ['push', 'gcr.io/$PROJECT_ID/capstone-app:$BUILD_ID']
    
      # Step 3: Deploy to Cloud Run with Environment Variables
      - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
        entrypoint: 'gcloud'
        args:
          - 'run'
          - 'deploy'
          - 'capstone-app'
          - '--image=gcr.io/$PROJECT_ID/capstone-app:$BUILD_ID'
          - '--region=us-central1'
          - '--allow-unauthenticated'
          - '--set-env-vars=PROJECT_ID=$PROJECT_ID,BUCKET_NAME=asset-portal-$PROJECT_ID'
    
      # Step 4: Ensure Public Access (Prevents 403 Forbidden Errors)
      - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
        entrypoint: 'gcloud'
        args:
          - 'run'
          - 'services'
          - 'add-iam-policy-binding'
          - 'capstone-app'
          - '--region=us-central1'
          - '--member=allUsers'
          - '--role=roles/run.invoker'
    
    images:
      - 'gcr.io/$PROJECT_ID/capstone-app:$BUILD_ID'
    EOF
    

    Step 4: Deploy the Entire Platform via Cloud Build

    Trigger the automated build pipeline directly from Cloud Shell:

    bash
    gcloud builds submit --config=cloudbuild.yaml .
    

    πŸ” What Just Happened?

    1. Cloud Build packages the Python app and builds the Docker container.
    2. It pushes the immutable image capstone-app:$BUILD_ID to the registry.
    3. It deploys the live service to Cloud Run, automatically wiring up Firestore, GCS, Vertex AI, and BigQuery!
    4. It attaches the roles/run.invoker policy binding for allUsers so the web frontend is immediately accessible over public HTTPS with zero 403 errors.

    Step 5: Test & Verify Your Live Production System

    1. Retrieve the Live Web Application URL

    bash
    # Get the live URL
    gcloud run services describe capstone-app \
        --region=${REGION} \
        --format='value(status.url)'
    
    # Explicit public access verification (if needed)
    gcloud run services add-iam-policy-binding capstone-app \
        --region=${REGION} \
        --member="allUsers" \
        --role="roles/run.invoker"
    
    πŸ›‘οΈ

    Why roles/run.invoker is Required By default, Google Cloud protects internal microservices from unauthorized internet traffic. Granting roles/run.invoker to allUsers tells Google Cloud's Front End (GFE) that this specific web service is a public-facing website.

    2. Open the URL & Upload an Image!

    1. Open the public HTTPS link in your browser.
    2. Enter an Asset Title (e.g. San Francisco Bridge Inspection).
    3. Select an image from your computer (or sample photo) and click Process Asset with Gemini AI.
    4. In 2 to 3 seconds, the page refreshes:
      • The image is served from Cloud Storage.
      • The title and caption are pulled in real-time from Firestore.
      • The visual description is automatically generated by Gemini 2.5 Flash!

    3. Inspect BigQuery Audit Logs (Data Warehouse Verification)

    Run this query in Cloud Shell to verify that the upload event was simultaneously streamed into your BigQuery data warehouse:

    bash
    bq query --use_legacy_sql=false '
    SELECT 
        asset_id, 
        user_caption, 
        ai_summary, 
        upload_timestamp 
    FROM `asset_analytics.upload_events` 
    ORDER BY upload_timestamp DESC 
    LIMIT 5;
    '
    

    (You will see your upload event logged with full AI insights in BigQuery!)


    πŸ›‘οΈ Step 6: Credit Safety & Resource Teardown ($0.00 Guarantee)

    To preserve your 300FreeTrialcreditwithβˆ—βˆ—300 Free Trial credit with **0.00 ongoing charges**, clean up your test resources:

    mermaid

    1. Delete Cloud Run Service

    bash
    gcloud run services delete capstone-app --region=${REGION} --quiet
    

    2. Delete Cloud Storage Bucket

    bash
    gcloud storage rm --recursive gs://${BUCKET_NAME}
    

    3. Delete BigQuery Dataset & Tables

    bash
    bq rm -r -f asset_analytics
    

    4. Delete Container Images

    bash
    gcloud container images list-tags gcr.io/${PROJECT_ID}/capstone-app --format="value(tags)" | \
        xargs -I {} gcloud container images delete -q --force-delete-tags "gcr.io/${PROJECT_ID}/capstone-app:{}"
    

    🧠 Daily Practice Drill & Final Knowledge Check

    Test your mastery of the complete Google Cloud ecosystem:

    text
    // Try answering these:
    1. In our Day 30 Capstone architecture, why do we use both Firestore AND BigQuery instead of just storing everything in one database?A) Firestore is an OLTP document store optimized for sub-10ms realtime web UI reads; BigQuery is an OLAP columnar warehouse optimized for massive analytical aggregation without slowing down the web appB) BigQuery cannot store text stringsC) Firestore only works on mobile phonesD) Google Cloud requires using at least 5 services per app
    2. How does Cloud Run authenticate securely with Vertex AI (Gemini), GCS, and BigQuery without storing API keys or passwords in the source code?A) By hardcoding secret passwords in the DockerfileB) Using Google Cloud's built-in Service Account IAM identity and Application Default Credentials (ADC)C) By disabling SSL encryptionD) By connecting through a public VPN
    3. What is the primary advantage of deploying applications via Cloud Build (cloudbuild.yaml) rather than running manual deployment commands from a laptop?A) It guarantees reproducible, automated, serverless CI/CD builds on Google's high-speed fiber infrastructure with zero local environment dependenciesB) It makes Python code run without errorsC) It eliminates the need for HTML and CSSD) It bypasses all cloud billing
    4. Which storage service provides 11 9's (99.999999999%) of durability for unstructured media files and photos?A) Compute Engine Local SSDB) Cloud Storage (GCS)C) Cloud Memorystore RedisD) Cloud Logging
    
    πŸ’‘ Click for Solutions
    1. A (OLTP vs OLAP separation) β€” Firestore delivers lightning-fast live UI feeds for end users, while BigQuery provides heavy business intelligence reporting without locking live database rows.
    2. B (Service Account IAM & ADC) β€” Cloud Run assumes its assigned IAM Service Account identity automatically, enabling secure keyless communication across GCP services.
    3. A (Reproducible Serverless CI/CD) β€” Cloud Build ensures automated, auditable, containerized builds on every release.
    4. B (Cloud Storage) β€” GCS stores immutable objects across multiple availability zones with 11 9's of durability.

    πŸŽ“ COURSE GRADUATION: Your 30-Day Master Roadmap

    mermaid

    πŸš€ What You Achieved in 30 Days:

    1. Architectural Fluency: You can design and explain resilient, highly available multi-region cloud systems.
    2. Hands-on Production Skills: You have built and deployed live virtual clusters, serverless containers, event-driven message queues, databases, data warehouses, and AI applications.
    3. Enterprise Security & Cost Optimization: You understand least-privilege IAM, secret management, billing alerts, and $0.00 credit safety principles.

    πŸ“œ Certification & Next Steps Roadmap

    With the knowledge gained in this 30-day curriculum, you are primed to pursue Google Cloud's industry-leading certifications:

    mermaid
    1. Google Cloud Certified Associate Cloud Engineer (ACE): The premier foundational certification covering everyday deployment, monitoring, IAM, and resource management.
    2. Google Cloud Certified Professional Cloud Architect (PCA): The gold standard enterprise certification focusing on high-level system design, security, and global scale.

    Congratulations on graduating from Learn GCP in 30 Days! Keep building, keep exploring, and welcome to the cloud! β˜οΈπŸš€πŸŽ“


    ← 29 - Vertex AI Studio and Gemini API | Course Syllabus β†’ 00 - Learn GCP in 30 Days: Course Syllabus