📦 Unit 03 - Serverless 雲端應用開發

用 Cloud Functions 與 Cloud Run 打造自動化相簿系統

⏱️ 學習時間

3 小時

🎯 難度等級

進階

🏗️ 建置專案

照片牆系統

💰 預估成本

$0.01 - $0.05

🎯 什麼是 Serverless?

Serverless(無伺服器)不是沒有伺服器,而是你不需要管理伺服器!雲端供應商會自動處理所有基礎設施,你只需專注在程式碼。

🖥️ 傳統 VM 模式

  • 需要規劃機器規格
  • 24 小時持續運行
  • 手動處理擴展
  • 需要維護系統
  • 最低月費 $7+

⚡ Serverless 模式

  • ✅ 按需求自動分配資源
  • ✅ 只在執行時計費
  • ✅ 自動無限擴展
  • ✅ 零維護負擔
  • ✅ 每月前 200 萬次免費

🏗️ Serverless 的兩大服務

⚡ Cloud Functions

函式即服務(FaaS)

輕量級、事件驅動

適合:圖片處理、API 整合

+

🚀 Cloud Run

容器即服務(CaaS)

完整應用、自訂運行環境

適合:Web API、微服務

💡 選擇建議
  • Cloud Functions:單一功能、快速開發(如:圖片裁切、資料轉換)
  • Cloud Run:複雜應用、需要 Docker(如:完整 API 服務、資料庫連線)

🧪 概念測驗 1

問題:某網站每天只有中午 12 點有流量高峰(10 分鐘),其他時間流量很少。哪種架構最划算?

A. 租用一台 VM,24 小時運行
B. 使用 Cloud Functions,按實際執行次數計費
C. 設定自動擴展的 VM 群組
D. 租用最小規格 VM,手動重啟應對高峰

📊 真實成本比較

$7.11
VM e2-micro 月費
$0.014
Serverless 同樣功能
99.8%
成本節省
200萬
每月免費請求次數
✅ 本單元學完後你將能夠
  • 部署 Cloud Functions 處理圖片上傳事件
  • 建立 Cloud Run API 服務並與前端整合
  • 理解 Serverless 的計費模式與優化方法
  • 完成一個自動化照片牆應用

⚡ Cloud Functions 快速上手

Cloud Functions 讓你只需寫一個函式,GCP 會自動處理剩下的一切:伺服器、擴展、監控。

🎯 第一個 Cloud Function

步驟 1:建立 HTTP 觸發函式

在 GCP Console 搜尋「Cloud Functions」→ 點擊「建立函式」

步驟 2:基本設定

設定項目 建議值 說明
函式名稱 hello-world-http 只能用小寫字母、數字、連字號
區域 us-central1 選擇鄰近使用者的區域
觸發類型 HTTPS 可透過網址直接呼叫
驗證 允許未經驗證的叫用 測試時方便,生產環境需改為需要驗證

🔬 實作:第一個 HTTP Function

選擇運行環境:Python 3.11

貼上以下程式碼:

import functions_framework

@functions_framework.http
def hello_http(request):
    """HTTP 觸發的 Cloud Function"""
    
    # 取得請求參數
    name = request.args.get('name', '訪客')
    
    # 回傳 JSON 格式
    return {
        'message': f'你好,{name}!',
        'status': 'success',
        'source': 'Cloud Functions'
    }

部署完成後,點擊函式名稱 → 複製「觸發網址」

在瀏覽器測試:

https://YOUR-REGION-YOUR-PROJECT.cloudfunctions.net/hello-world-http?name=Frank

# 回應
{
  "message": "你好,Frank!",
  "status": "success",
  "source": "Cloud Functions"
}

🧪 Cloud Functions 測驗

問題:如果你的 Cloud Function 每次執行需要 200ms,每天被呼叫 1000 次,月費大約多少? (前 200 萬次請求免費,每 100 萬次後收費 $0.40)

A. $120/月
B. $12/月
C. $0/月(在免費額度內)
D. $7.11/月(固定費用)

🔍 查看函式日誌

在函式詳情頁 → 點擊「日誌」分頁

你會看到每次執行的記錄:

  • 執行時間(毫秒)
  • 記憶體使用量
  • 錯誤訊息(如果有)
  • 自訂日誌輸出
💡 除錯技巧
  • 使用 print() 輸出變數值,會顯示在日誌中
  • 在 Console 可以手動測試函式(不需實際發送 HTTP 請求)
  • 查看「指標」分頁了解執行次數與效能

🔔 事件驅動的自動化魔法

Cloud Functions 最強大的功能是自動響應事件。當 Cloud Storage 有新檔案上傳、Pub/Sub 收到訊息時,函式會自動執行。

📂 Storage 觸發器:自動處理上傳檔案

👤 使用者

上傳照片

🗄️ Cloud Storage

儲存原圖

⚡ Cloud Function

自動觸發

🖼️ 產出縮圖

儲存處理結果

🔬 實作:自動產生縮圖

新增函式,設定如下:

設定項目
觸發類型 Cloud Storage
事件類型 finalize/create(檔案上傳完成)
Bucket 選擇你的照片 Bucket

程式碼範例(需安裝 Pillow 套件):

import functions_framework
from google.cloud import storage
from PIL import Image
import io

@functions_framework.cloud_event
def generate_thumbnail(cloud_event):
    """當圖片上傳時自動產生縮圖"""
    
    data = cloud_event.data
    bucket_name = data["bucket"]
    file_name = data["name"]
    
    # 忽略縮圖資料夾(避免無限遞迴)
    if file_name.startswith("thumbnails/"):
        return
    
    # 下載原圖
    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(file_name)
    image_data = blob.download_as_bytes()
    
    # 產生縮圖(300x300)
    img = Image.open(io.BytesIO(image_data))
    img.thumbnail((300, 300))
    
    # 儲存縮圖
    thumb_bytes = io.BytesIO()
    img.save(thumb_bytes, format=img.format)
    thumb_bytes.seek(0)
    
    thumb_blob = bucket.blob(f"thumbnails/{file_name}")
    thumb_blob.upload_from_file(thumb_bytes, content_type=blob.content_type)
    
    print(f"✅ 縮圖已生成:thumbnails/{file_name}")

requirements.txt 新增依賴:

functions-framework==3.*
google-cloud-storage==2.10.0
Pillow==10.0.0

🧪 事件觸發測驗

問題:你設定了 Storage 觸發器自動產生縮圖,但發現函式一直不停執行。最可能的原因是?

A. GCP 系統錯誤
B. 函式產生的縮圖又觸發了新事件(無限遞迴)
C. 記憶體不足導致重試
D. Bucket 權限設定錯誤

🎯 常見觸發器類型

📂 Cloud Storage

使用場景

  • 圖片上傳自動壓縮
  • 文件自動分類
  • 資料檔案轉換

📨 Pub/Sub

使用場景

  • 異步任務處理
  • 微服務間通訊
  • 日誌收集分析

🗄️ Firestore

使用場景

  • 資料變更通知
  • 自動計算統計
  • 觸發後續流程

⏰ Cloud Scheduler

使用場景

  • 定時備份
  • 每日報表生成
  • 資料清理任務
⚠️ 注意事項
  • 避免無限遞迴:檢查觸發來源,忽略函式自己產生的檔案
  • 冪等性設計:同一事件可能觸發多次,確保重複執行不會造成問題
  • 逾時設定:預設 60 秒,處理大檔案可能需要調整至 540 秒上限

🚀 Cloud Run:部署完整 API 服務

Cloud Run 讓你把任何容器化應用部署到 GCP,自動擴展到零(沒流量時不收費),適合建立完整的 Web API。

🆚 Cloud Functions vs Cloud Run

特性 Cloud Functions Cloud Run
執行環境 單一函式 完整容器(可包含多個路由)
語言支援 Python、Node.js、Go 等 任何語言(只要能容器化)
逾時限制 最多 540 秒(9 分鐘) 最多 3600 秒(60 分鐘)
適用場景 事件驅動、輕量任務 完整 API、需要長時間處理
部署難度 簡單(直接貼程式碼) 中等(需要 Dockerfile)

🧪 Cloud Run 選擇測驗

問題:以下哪個場景不適合使用 Cloud Run?

A. 建立一個有 10 個 API 端點的 RESTful 服務
B. 部署即時聊天應用(需要 WebSocket)
C. 執行 24 小時持續運行的資料爬蟲程式
D. 每小時執行一次的資料處理 API

🛠️ 部署 Flask API 到 Cloud Run

🔬 實作:建立照片管理 API

步驟 1:準備專案結構

photo-api/
├── main.py
├── requirements.txt
└── Dockerfile

步驟 2:撰寫 Flask 應用(main.py)

from flask import Flask, jsonify, request
from flask_cors import CORS
from google.cloud import storage
import os

app = Flask(__name__)
CORS(app)

BUCKET_NAME = os.environ.get('BUCKET_NAME', 'your-bucket-name')

@app.route('/')
def home():
    return jsonify({
        'service': 'Photo API',
        'version': '1.0',
        'endpoints': ['/photos', '/upload-url']
    })

@app.route('/photos', methods=['GET'])
def list_photos():
    """列出所有照片"""
    storage_client = storage.Client()
    bucket = storage_client.bucket(BUCKET_NAME)
    blobs = bucket.list_blobs(prefix='photos/')
    
    photos = []
    for blob in blobs:
        if blob.name != 'photos/':  # 忽略資料夾本身
            photos.append({
                'name': blob.name,
                'url': blob.public_url,
                'size': blob.size,
                'updated': blob.updated.isoformat()
            })
    
    return jsonify({'photos': photos, 'count': len(photos)})

@app.route('/upload-url', methods=['POST'])
def generate_upload_url():
    """產生簽署上傳網址(Signed URL)"""
    data = request.json
    filename = data.get('filename')
    
    storage_client = storage.Client()
    bucket = storage_client.bucket(BUCKET_NAME)
    blob = bucket.blob(f'photos/{filename}')
    
    url = blob.generate_signed_url(
        version="v4",
        expiration=300,  # 5 分鐘有效
        method="PUT",
        content_type=data.get('content_type', 'image/jpeg')
    )
    
    return jsonify({'upload_url': url})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

步驟 3:依賴套件(requirements.txt)

Flask==3.0.0
flask-cors==4.0.0
google-cloud-storage==2.10.0
gunicorn==21.2.0

步驟 4:容器設定(Dockerfile)

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY main.py .

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 main:app

步驟 5:部署到 Cloud Run

# 啟用 Cloud Run API
gcloud services enable run.googleapis.com

# 使用 Cloud Build 自動建置並部署
gcloud run deploy photo-api \
  --source . \
  --region us-central1 \
  --platform managed \
  --allow-unauthenticated \
  --set-env-vars BUCKET_NAME=my-photos-bucket

# 部署完成會顯示服務網址
Service URL: https://photo-api-xxx-uc.a.run.app

測試 API

# 列出照片
curl https://photo-api-xxx-uc.a.run.app/photos

# 取得上傳網址
curl -X POST https://photo-api-xxx-uc.a.run.app/upload-url \
  -H "Content-Type: application/json" \
  -d '{"filename": "sunset.jpg", "content_type": "image/jpeg"}'
✅ Cloud Run 優勢
  • 自動擴展到零:沒流量時實例數降為 0,不收費
  • 快速冷啟動:首次請求約 1-2 秒啟動
  • 簡單部署gcloud run deploy 一條指令完成
  • 自訂網域:可綁定自己的域名

🔗 打造完整照片牆應用

現在我們整合所有元件:Cloud Storage(儲存)+ Cloud Functions(自動處理)+ Cloud Run(API)+ 靜態網站(前端)。

🏗️ 完整架構圖

👤 使用者

瀏覽器

🌐 靜態網站

Cloud Storage 託管

🚀 Cloud Run API

照片列表 / 上傳網址

🗄️ Cloud Storage

儲存原始照片

⚡ Cloud Function

自動產生縮圖

🔬 實作:前端整合範例

HTML + JavaScript 呼叫 Cloud Run API:

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>照片牆</title>
  <style>
    body { font-family: Arial, sans-serif; padding: 20px; }
    .photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; }
    .photo-card { border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
    .photo-card img { width: 100%; height: 200px; object-fit: cover; }
  </style>
</head>
<body>
  <h1>📸 我的照片牆</h1>
  
  <!-- 上傳區域 -->
  <div>
    <input type="file" id="fileInput" accept="image/*">
    <button onclick="uploadPhoto()">上傳照片</button>
  </div>

  <!-- 照片展示 -->
  <div id="photoGrid" class="photo-grid"></div>

  <script>
    const API_URL = 'https://photo-api-xxx-uc.a.run.app';

    // 載入所有照片
    async function loadPhotos() {
      const response = await fetch(`${API_URL}/photos`);
      const data = await response.json();
      
      const grid = document.getElementById('photoGrid');
      grid.innerHTML = '';
      
      data.photos.forEach(photo => {
        const card = document.createElement('div');
        card.className = 'photo-card';
        card.innerHTML = `
          <img src="${photo.url}" alt="${photo.name}">
          <p>${photo.name}</p>
        `;
        grid.appendChild(card);
      });
    }

    // 上傳照片
    async function uploadPhoto() {
      const fileInput = document.getElementById('fileInput');
      const file = fileInput.files[0];
      
      if (!file) {
        alert('請選擇檔案');
        return;
      }

      // 1. 取得簽署上傳網址
      const response = await fetch(`${API_URL}/upload-url`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          filename: file.name,
          content_type: file.type
        })
      });
      
      const { upload_url } = await response.json();

      // 2. 直接上傳到 Cloud Storage
      await fetch(upload_url, {
        method: 'PUT',
        headers: { 'Content-Type': file.type },
        body: file
      });

      alert('上傳成功!');
      loadPhotos();  // 重新載入
    }

    // 頁面載入時顯示照片
    loadPhotos();
  </script>
</body>
</html>

🧪 架構設計測驗

問題:為什麼要使用「簽署上傳網址(Signed URL)」而不是直接讓前端把檔案傳給 Cloud Run 再由 API 轉存?

A. 安全性更好
B. 避免大檔案經過 API,減少流量費用與逾時風險
C. 實作更簡單
D. 上傳速度更快

🔐 權限設定最佳實踐

✅ 該做的事

  • Cloud Run 設為需要驗證(生產環境)
  • 使用 Signed URL 控制上傳權限
  • 為 Cloud Function 設定專用 Service Account
  • 啟用 Cloud Armor 防護 DDoS

❌ 不該做的事

  • 整個 Bucket 設為公開讀取
  • API Key 寫在前端程式碼
  • 無限制的上傳大小
  • 未設定 CORS 政策
⚠️ 常見整合問題
  • CORS 錯誤:確認 Cloud Run 已安裝 flask-cors 並正確設定
  • 權限拒絕:檢查 Service Account 是否有 Storage 權限
  • 逾時:大檔案上傳應使用 Signed URL 直接傳 Storage
  • 費用暴增:設定流量預算警示,避免 DDoS 攻擊

💰 Serverless 成本優化策略

Serverless 最大優勢是按使用量付費,但如果設計不當,仍可能產生意外費用。

💵 計費模式解析

服務 計費單位 免費額度 超過後費用
Cloud Functions 執行次數 + 執行時間 200 萬次/月
40 萬 GB-秒/月
$0.40 / 100 萬次
$0.0000025 / GB-秒
Cloud Run CPU 時間 + 記憶體 + 請求數 240,000 vCPU-秒/月
450,000 GiB-秒/月
200 萬次請求/月
$0.00002400 / vCPU-秒
$0.00000250 / GiB-秒
$0.40 / 100 萬次
Cloud Storage 儲存空間 + 操作次數 5 GB Standard 儲存
5000 次 Class A 操作
$0.020 / GB/月
$0.05 / 10,000 次

🧪 成本計算測驗

問題:某個 Cloud Function 平均執行 500ms,記憶體配置 256MB,每月執行 500 萬次。預估月費多少? (超過 200 萬次免費額度)

A. $0(在免費額度內)
B. 約 $1.00 - $1.50
C. 約 $10
D. 約 $50

🎯 優化技巧

1. 減少冷啟動(Cold Start)

  • 使用最小實例數(Minimum Instances)保持服務溫暖
  • 減少依賴套件大小(只安裝必要的庫)
  • Cloud Run: 設定 --min-instances 1 避免冷啟動(會產生固定費用)

2. 控制記憶體配置

  • Cloud Functions 預設 256MB,多數場景足夠
  • 圖片處理可能需要 512MB - 1GB
  • 記憶體越大執行越快,但費用成正比

3. 設定逾時與重試策略

  • 合理設定逾時(避免卡住消耗資源)
  • 失敗重試最多 3 次(避免無限循環)
  • 使用 Dead Letter Queue 處理失敗事件

🔬 實作:設定成本預算警示

在 GCP Console → 「帳單」→ 「預算與快訊」

# 設定預算
預算名稱:Serverless 月預算
預算金額:$10 USD / 月
警示門檻:50%, 90%, 100%

# 通知方式
Email: your-email@example.com
Pub/Sub 主題:(選用,可串接自動化流程)

# 當達到 100% 時可自動停用服務
設定 Cloud Function 檢查預算並停用資源

📊 監控儀表板

💡 重要監控指標
  • 執行次數:偵測異常流量(可能是 DDoS)
  • 執行時間:識別效能瓶頸
  • 錯誤率:快速發現故障
  • 冷啟動頻率:評估是否需要設定最小實例
< 100ms
理想執行時間
< 5%
目標錯誤率
99.9%
GCP SLA 保證
$0.014
本專案實際月費

🎉 單元總結與知識檢查

🏆

恭喜完成 Unit 03!

你已掌握 Serverless 架構,可以建立自動化雲端應用了!

✅ 你已經學會

  • ✅ 理解 Serverless 的核心概念與優勢(按使用量計費、自動擴展)
  • ✅ 部署 Cloud Functions 處理 HTTP 請求與 Storage 事件
  • ✅ 設計事件驅動架構避免無限遞迴
  • ✅ 使用 Cloud Run 部署容器化 API 服務
  • ✅ 整合前端、API、自動化處理的完整應用
  • ✅ 優化成本並設定監控警示
  • ✅ 比較 Cloud Functions 與 Cloud Run 的適用場景

🎓 知識檢查清單

確認你能回答以下問題:

💡 實際應用場景

案例 1:電商產品推薦

使用者瀏覽商品 → Pub/Sub 事件 → Cloud Function 分析行為 → 推薦相關商品

成本:約 $2/月(100 萬次推薦)

案例 2:社群圖片服務

上傳照片 → Storage 觸發 → Function 產生多尺寸圖 → Cloud Run API 管理

成本:約 $5/月(10 萬張圖片)

案例 3:IoT 資料收集

感測器資料 → Pub/Sub → Cloud Function 處理 → 寫入 BigQuery

成本:約 $3/月(50 萬筆資料)

案例 4:每日報表生成

Cloud Scheduler 觸發 → Cloud Function 查詢資料庫 → 產生 PDF 報表

成本:約 $0.10/月(每天 1 次)

🎯 延伸學習

進階實作建議

  • 整合 Firebase Authentication 實作使用者驗證
  • 使用 Cloud Vision API 自動標記照片內容
  • 實作 Firestore 儲存照片元資料與評論
  • 使用 Cloud CDN 加速全球存取
  • 設定 Cloud Armor 防護 DDoS 攻擊
🎓

🎊 完成整個 GCP 課程!🎊

你已經完成全部 3 個單元,從零開始掌握 Google Cloud Platform 核心服務!

3
單元完成
9
小時學習
6
核心服務
3
完整專案

你已掌握的技能

Unit 01:GCP 平台架構、Compute Engine、SSH 連線、IAM 權限管理

Unit 02:Cloud Storage、靜態網站託管、生命週期規則、成本優化

Unit 03:Cloud Functions、Cloud Run、Serverless 架構、事件驅動設計

🚀 下一步建議

🎯 考取 GCP 認證:Associate Cloud Engineer(ACE)

🛠️ 建立個人專案:部署真實應用到 GCP

📚 深化學習:Kubernetes、BigQuery、Cloud SQL

💼 職涯發展:雲端工程師、DevOps、解決方案架構師

查看 GCP 認證路徑
⬆️ 回到頂部