iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Build on Google AI

30 天用 Google AI 打造台灣防災速報 App系列 第 28 篇

Day 28|正式環境一個月約 3 美元:費用、監控與資安的實際做法

  • 分享至 

  • xImage
  •  

系列:30 天用 Google AI 打造臺灣防災速報 App(Day 28/30)

後端 9/25 上線,到今天兩週多。今天算兩件事:錢花在哪裡,以及怎麼防止服務被濫用。費用用帳單的實際數字;資安只寫已經做好的部分,限流門檻這類可能幫到攻擊者的細節不公開。

帳單:App 只佔一小部分

帳單匯出到 BigQuery,用 SQL 依服務和日期加總。下面是 10/1 到 10/9 這個專案的金額。帳單以新臺幣計,美元用帳單欄位裡的匯率平均值 31.775 換算。

用途 新臺幣 約美元
Demo 影片製作(影片、圖片、配樂、語音) 555.92 17.49
評測與本機測試(Day 27 的評審模型等) 48.99 1.54
App:Gemini(含本機測試) 46.11 1.45
App:Cloud Functions(五支函式) 11.21 0.35
App:Cloud DNS 1.21 0.04
App:Firestore、Hosting、Scheduler、Secret Manager 等 12 項 0 0
  • 帳單 9 天約 21 美元,八成以上是 Demo 影片。 之後影片改到另一個專案生成,和正式服務的額度分開。
  • App 的 Gemini 混了本機測試。 部署前測試和評測用同一個專案的金鑰,帳單分不出來,所以正式環境另外用日誌估。
  • 網域年費不在表裡。 .dev 網域一年新臺幣 381.36 元(約 12 美元),帳單上沒有掛在專案下。

目前帳單由試用抵免額抵掉。這篇算的是用量本身的金額。

只算正式環境:9 天約 0.92 美元

gemini_usage.py 把正式函式日誌記下的 token 數乘上官方單價。10/1 到 10/9 每天 0.0068 到 0.168 美元,合計 0.527 美元;帳單上 App 的 Gemini 1.45 美元,其餘約 0.9 美元是本機測試。

項目 10/1–10/9(9 天) 換算 30 天
Gemini(日誌估算) 0.53 美元 約 1.8 美元
Cloud Functions 0.35 美元 約 1.2 美元
Cloud DNS 0.04 美元 約 0.1 美元
合計 約 0.92 美元 約 3.1 美元

這個數字不含網域年費(攤到每月約 1 美元)和開發測試。這段期間使用者很少,正式上架後用量會增加。

少寫入、少呼叫

  • 只寫有變化的文件(Day 16)。10/1 到 10/9,Firestore 每天寫入 320 到 906 次,是第一版寫法(約每天 12 萬次)的 0.3% 到 0.8%。
  • 同步狀態最多 10 分鐘寫一次。 地震每 2 分鐘一輪,meta/quake_status 只在有新地震、剛恢復或滿 10 分鐘時寫。App 監聽這份文件,每寫一次,每個監聽中的 App 就多一次讀取。
  • 短電文不呼叫模型摘要,有官方等級就不呼叫模型分級(Day 27)。原文 60 字以內直接當摘要;1,214 則有等級的電文中,AI 判定的只有 4 則。
  • 問答的前處理併成一次呼叫。 外語問題的中文查詢句、意圖判斷和錯字校正同一次完成。錯字校正有同時呼叫數上限和 1.5 秒逾時,拿不到名額或逾時就用原句;10/7 查前 7 天,98 次中逾時 2 次。
  • 檢索的兩句併成一次 embedding 呼叫;知識庫向量先算好隨程式部署,每題只算查詢句。

監控:每天早上看一次

用量檢查。 本機排程每天 09:00 跑 gemini_usage.py,統計前一天正式環境的 Gemini 呼叫數、token、推估花費和錯誤碼。另外檢查:出現 402(預付餘額不足)或餘額撐不到 3 天、外語回答殘留民國年、錯字校正逾時率超過 5%。任一項成立就寄示警信給我。

App Check 統計。 appcheck_monitor.py 依函式、用戶端和 HTTP 狀態統計最近 3 小時的請求,加上 Firestore 的驗證結果和 reCAPTCHA 評估;網頁版被擋超過 2% 就通知。

排程紀錄。 10/1 到 10/9 每天示警 144 輪、地震 720 輪都有執行,10/10 上午也沒有漏跑。沒有漏跑只代表每一輪都有跑,錯誤另外算:10/10 09:30 之前有 6 輪以錯誤結束,原因是連 NCDR 或中央氣象署逾時,以及 NCDR 回 429,都在下一輪自動恢復。9/25 到 9/30 的 4 輪 Day 21 寫過。

10/10:NCDR 連續回 429

以下是臺灣時間。

  • 現象。 09:30 起,示警排程 poll_alerts 一再以錯誤結束,原因都是 NCDR 回 429 Too Many Requests,到 11:10 共 9 輪失敗。本機實測也一樣:連續兩次請求,第二次就被擋。
  • 判斷。 NCDR 對同一個 IP 做頻率限制。Cloud Run 的對外 IP 和其他使用者共用,大家又多在整十分抓,所以撞在一起。
  • 影響。 meta/sync_status 記下失敗,App 顯示同步失敗,最後一次成功是 10:50。推播和既有的示警不受影響。

11 點多部署修正:

  1. 遇到 429 或 5xx,先等 25 秒重試,再失敗等 30 秒。回應有 Retry-After 就照它,上限 30 秒;整輪總時間 75 秒內。本機實測 429 後等 25 秒,成功抓到 182 筆。
  2. 排程從整十分改到每十分的第 3 分(cron 寫成 3-59/10)。
  3. AI 加值函式下載單則電文時也加一次重試。重試的 9 項測試全過。

修正後的 11:23、11:33 兩輪都正常,沒有觸發重試,同步狀態恢復。之後再遇到 429,日誌會記一筆 ncdr_retry,只記狀態碼、等待秒數和第幾次,用來統計重試頻率。

App Check:確認請求來自正版 App

程式裡依平台設定:Android 正式版用 Play Integrity,網頁版用 reCAPTCHA Enterprise,除錯版用 debug provider;iOS 正式版設定為 App Attest,要等 Apple 開發者帳號核准後才會實際用到。10/4 起問答 ask 和回報 report_answer 強制 App Check,不帶權杖回 401;10/7 起 Firestore 也強制。

開強制前,先查清無效權杖的來源:模擬器上的 profile 版、背景沒關的本機測試頁,都是開發流量。Firestore 強制後 24 小時的統計:Android 481 筆、iOS 184 筆、網頁 86 筆請求都通過,被拒的 29 筆全是開發測試和後端自測。

權杖到期不會自己換新。 實測時,權杖到期後 Android 約 35 分鐘、iOS 超過 13 分鐘都是無效狀態。修法是開啟 SDK 的自動換新,再加上到期前 10 分鐘主動換新並讓 Firestore 重連;行動版和網頁版(背景分頁)都改了。修正後連續 85 分鐘前景驗證,無效 0 次。

限流與費用上限

  • 問答和回報都以「來源」計次,超過就回錯誤。來源只存 IP 加鹽後的單向雜湊,鹽依臺灣日期每天換,同一個 IP 跨日無法比對(Day 15)。
  • 問答的問題最多 300 字。回報全部來源合計每天有寫入上限,欄位也有長度上限。
  • 每支函式設了最多實例數,被大量請求時,費用也有上限。
  • Day 16 設了每月 40 美元的預算快訊。預算快訊只通知、不停服務,實際控制費用的是上面幾項。

金鑰、資料庫規則與日誌

  • 伺服器金鑰。 Gemini 和中央氣象署兩把金鑰只放在 Secret Manager,部署時注入函式,不進程式碼和 App。Gemini 金鑰只開 Generative Language API。
  • 地圖金鑰分平台三把。 Android 限套件名加簽章指紋、iOS 限 bundle ID、網頁限網域,每把只開對應的一個 Maps API。金鑰檔不進 git,建置找不到金鑰就失敗。
  • Firestore 規則(Day 15)。只有 alerts、history、earthquakes、meta 公開可讀;寫入一律拒絕,只有後端用 Admin SDK 寫;其他集合讀寫全拒,內部資料用戶端讀不到。
  • 日誌不記問答內容。 只記分流、工具名稱、分數等。靜態掃描測試檢查每個 print 的欄位,部署後再用一次性標記字串實測,查日誌確認沒有洩漏。每小時用量統計只存整數。
  • 保存期限。 限流的雜湊約 2 小時失效,24 小時內由 Firestore TTL 刪除;回報保存 90 天;內部集合共設 9 個 TTL 政策。

提示注入與工具

  • 提問用固定界線包起來,提示詞規定界線內只當資料、不當指令;回答帶出提示詞片段就不採用。
  • 問答的兩個工具(查示警、查地震)都是唯讀。分類參數走白名單,白名單外的值當沒給;回傳筆數有上限,摘要截到 120 字。關鍵字只在記憶體裡做子字串比對,不組任何查詢。
  • 抓 CAP 電文只連白名單主機,也不跟隨轉址,避免伺服器被誘導去連不該連的位址(SSRF)。

開發時的規矩

  • 套件下載前先查發布者、OSV 已知漏洞、授權、維護狀態和安裝腳本(10/3 起)。ffmpeg-full 是個例子:Homebrew 安裝時順帶升級共用函式庫,連帶弄壞既有的 ffmpeg。要看的不只套件本身,還有安裝會動到什麼。
  • 除錯權杖不進正式版。 模擬器建置用 debug provider 加已登錄的固定除錯權杖,正式版建置腳本擋掉除錯旗標和權杖。權杖檔放 repo 外、權限 600,只以路徑交給建置工具。
  • 日誌先遮蔽再看。 有一次 SDK 把除錯權杖印進日誌,發現後作廢重發,新權杖確認有效才刪舊的。之後 logcat 只抓需要的標籤,輸出先遮蔽。
  • 公開 repo 用白名單複製。 危機詞表換成空清單、去掉程式註解、Firebase 設定檔不公開;每一輪掃金鑰、本機路徑、帳號名稱、私有網段 IP,都是 0 筆。
  • 金鑰、權杖、帳號、付款、權限變更由我本人操作。 開發腳本不讀簽章金鑰的密碼,簽章指紋由我自己提供。

今日小結

  • 帳單 9 天約 21 美元,八成以上是 Demo 影片;App 正式環境 9 天約 0.92 美元,換算一個月約 3 美元,不含網域年費和開發測試,上架後會增加。
  • 費用低靠少寫、少呼叫:只寫有變化的文件、短電文不呼叫模型摘要、有官方等級不呼叫模型分級、問答前處理併成一次呼叫。
  • 每天 09:00 檢查用量與異常;排程每一輪都有跑,錯誤另外記。
  • 10/10 NCDR 連續回 429:重試照 Retry-After 並設總時間上限,排程錯開整十分。
  • App Check 在問答、回報兩支函式和 Firestore 都已強制;開強制前先查清無效權杖來源,權杖要主動換新。
  • 限流不存 IP、函式有實例上限、伺服器金鑰放 Secret Manager、日誌不記問答內容。

明天 Day 29:成果展示與上架進度。


上一篇
Day 27|漏掉一則該升級的警報,比多推一則嚴重得多:AI 輸出怎麼評估
系列文
30 天用 Google AI 打造台灣防災速報 App 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言