把「能用」升級成「耐用、好維運、可擴充」;今天一次幫你收尾打包 ✅
這篇是系列最終回:把前面 Day 1–6 的重點串起來,給你一條龍的部署 runbook、升級 Roadmap、以及實用檔案清單(Dockerfile / requirements.txt / .env / gcloud 指令)。
另外也附上圖表(部署流程、間隔 vs 請求量、Roadmap 清單)讓你貼文更有梗。
[部署 Runbook]
口訣:先打包 → 再部署 → 接 webhook/cron → 開監控。
步驟重點:
app.py
、requirements.txt
、Dockerfile
CHANNEL_ACCESS_TOKEN
/ CHANNEL_SECRET
docker build
→ 推至 Artifact Registry/cron/tick
/webhook
[間隔 vs 請求數(估算)]
曲線只示意:間隔越短、任務越多,請求數越高。以此選擇你的「抗吵」與「資源」平衡點。
[Roadmap(中文)]
短期(1–2 週):
中期(1–2 月):
長期(3–6 月):
requirements.txt
flask==3.0.3
gunicorn==21.2.0
requests==2.32.3
beautifulsoup4==4.12.3
lxml==5.2.1
google-cloud-firestore==2.16.1
google-cloud-logging==3.10.0
line-bot-sdk==3.11.0
Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安裝系統套件(若需)
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8080
# 用 gunicorn 啟動 Flask app: 模組為 app.py 內的 app 物件
CMD exec gunicorn -b :$PORT -w 2 --timeout 90 app:app
.env
(本地測試範例)LINE_CHANNEL_ACCESS_TOKEN=your_line_token
LINE_CHANNEL_SECRET=your_line_secret
DEFAULT_PERIOD_SEC=60
ALWAYS_NOTIFY=0
MAX_PER_TICK=5
TICK_SOFT_DEADLINE_SEC=25
# 建 Artifact Registry(若尚未建立)
gcloud artifacts repositories create ibon-repo \
--repository-format=docker --location=asia-east1
# 建鏡像
PROJECT_ID=$(gcloud config get-value project)
REGION=asia-east1
IMAGE=asia-east1-docker.pkg.dev/$PROJECT_ID/ibon-repo/ibon-watch:latest
docker build -t "$IMAGE" .
docker push "$IMAGE"
# 部署 Cloud Run
# 建 Artifact Registry(若尚未建立)
gcloud artifacts repositories create ibon-repo \
--repository-format=docker --location=asia-east1
# 建鏡像
PROJECT_ID=$(gcloud config get-value project)
REGION=asia-east1
IMAGE=asia-east1-docker.pkg.dev/$PROJECT_ID/ibon-repo/ibon-watch:latest
docker build -t "$IMAGE" .
docker push "$IMAGE"
# 部署 Cloud Run
gcloud run deploy ibon-watch \
--image "$IMAGE" \
--platform managed \
--region $REGION \
--allow-unauthenticated \
--set-env-vars "DEFAULT_PERIOD_SEC=60,ALWAYS_NOTIFY=0,MAX_PER_TICK=5,TICK_SOFT_DEADLINE_SEC=25" \
--set-env-vars "LINE_CHANNEL_ACCESS_TOKEN=YOUR_TOKEN,LINE_CHANNEL_SECRET=YOUR_SECRET"
SERVICE_URL=$(gcloud run services describe ibon-watch --region $REGION --format='value(status.url)')
gcloud scheduler jobs create http ibon-cron \
--schedule="* * * * *" \
--uri="$SERVICE_URL/cron/tick" \
--http-method=POST
企業環境可改用 OIDC 觸發(設定 --oidc-service-account-email)避免公開端點。
// collection: jobs
{
id: "W123456",
user_id: "Uxxxxx",
url: "https://tickets.ibon.com/Show/Index/...",
interval: 60,
enabled: true,
last_run: 1737800000,
areas_cache: [
{ code: "B09P2J33", name: "5F B區 3800", status: "25" },
{ code: "C02X...", name: "3F C區 2800", status: "熱賣中" }
]
}
Logging:記錄每次 tick 的 processed/elapsed_ms/errors;webhook 指令與錯誤碼
Metrics:通知次數、通知延遲、抓取失敗率、ibon 解析錯誤率
告警:
5 分鐘內錯誤率 > X%
單次 tick > 30s(疑似卡住)
通知連續 0 次 > N 小時(可能排程停了)
保持合理頻率(例 60s),避免造成網站負擔
僅做提醒與資訊整理;購票仍需你親自完成
Token/Secret 請用 Secret Manager 管理,避免硬編在程式裡
Webhook 沒回 200:LINE 會重試 → 記得回 {"ok":true} 類的成功訊息
排程命中 403/401:若關閉公開存取,請改用 OIDC 呼叫
火力不足:把通知策略改成「只在變化時 + 合併視窗」,或把間隔適度放長
如果你一路追到這裡,恭喜擁有一個可維運的「ibon 票數自動偵測 + LINE 推播」系統。
接下來就看你要走美化派(Flex 卡片、圖像化)或猛攻派(多平台、動態調頻)啦!
下一篇是心得,敬請期待 🎉