30 - Capstone Project - Production Deployment
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:
π The Problem: Fragmented Legacy Architectures
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):
- Stateless Scalability (Cloud Run): The web service auto-scales from 0 to thousands of requests and back to zero with $0.00 idle cost.
- Infinite Durability (Cloud Storage): Media files are stored securely with 11 9's of durability.
- Instant Intelligence (Gemini 2.5 Flash): Images are automatically analyzed for objects, defects, and text in seconds without custom ML training.
- Separated Storage & Analytics (Firestore + BigQuery): Fast operational data is kept in Firestore, while analytical audit records stream directly into BigQuery for reporting.
- Zero-Touch Automation (Cloud Build): A single
git pushor 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!
Step 1: Initialize Project & Enable All APIs
Open Google Cloud Shell and run:
# 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)
# 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)
gcloud firestore databases create \
--location=${REGION} \
--type=firestore-native 2>/dev/null || echo "Firestore already initialized!"
3. Create BigQuery Analytics Dataset & Table
# 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
# 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:
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):
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:
nano main.py
(Paste the code above, press Ctrl+O Enter to save, then Ctrl+X to exit).
2. Author Frontend UI (templates/index.html)
β‘ Fast 1-Click Way (Copy & Paste):
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
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:
gcloud builds submit --config=cloudbuild.yaml .
π What Just Happened?
- Cloud Build packages the Python app and builds the Docker container.
- It pushes the immutable image
capstone-app:$BUILD_IDto the registry. - It deploys the live service to Cloud Run, automatically wiring up Firestore, GCS, Vertex AI, and BigQuery!
- It attaches the
roles/run.invokerpolicy binding forallUsersso 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
# 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!
- Open the public HTTPS link in your browser.
- Enter an Asset Title (e.g.
San Francisco Bridge Inspection). - Select an image from your computer (or sample photo) and click Process Asset with Gemini AI.
- 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:
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 0.00 ongoing charges**, clean up your test resources:
1. Delete Cloud Run Service
gcloud run services delete capstone-app --region=${REGION} --quiet
2. Delete Cloud Storage Bucket
gcloud storage rm --recursive gs://${BUCKET_NAME}
3. Delete BigQuery Dataset & Tables
bq rm -r -f asset_analytics
4. Delete Container Images
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:
// 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
- 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.
- B (Service Account IAM & ADC) β Cloud Run assumes its assigned IAM Service Account identity automatically, enabling secure keyless communication across GCP services.
- A (Reproducible Serverless CI/CD) β Cloud Build ensures automated, auditable, containerized builds on every release.
- B (Cloud Storage) β GCS stores immutable objects across multiple availability zones with 11 9's of durability.
π COURSE GRADUATION: Your 30-Day Master Roadmap
π What You Achieved in 30 Days:
- Architectural Fluency: You can design and explain resilient, highly available multi-region cloud systems.
- Hands-on Production Skills: You have built and deployed live virtual clusters, serverless containers, event-driven message queues, databases, data warehouses, and AI applications.
- 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:
- Google Cloud Certified Associate Cloud Engineer (ACE): The premier foundational certification covering everyday deployment, monitoring, IAM, and resource management.
- 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