🎯 什麼是 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 分鐘),其他時間流量很少。哪種架構最划算?
📊 真實成本比較
- 部署 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)
🔍 查看函式日誌
在函式詳情頁 → 點擊「日誌」分頁
你會看到每次執行的記錄:
- 執行時間(毫秒)
- 記憶體使用量
- 錯誤訊息(如果有)
- 自訂日誌輸出
- 使用
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 觸發器自動產生縮圖,但發現函式一直不停執行。最可能的原因是?
🎯 常見觸發器類型
📂 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?
🛠️ 部署 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"}'
- 自動擴展到零:沒流量時實例數降為 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 轉存?
🔐 權限設定最佳實踐
✅ 該做的事
- 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 萬次免費額度)
🎯 優化技巧
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)
- 執行時間:識別效能瓶頸
- 錯誤率:快速發現故障
- 冷啟動頻率:評估是否需要設定最小實例
🎉 單元總結與知識檢查
恭喜完成 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 核心服務!
你已掌握的技能
✅ 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、解決方案架構師